diff options
Diffstat (limited to 'src/eval.c')
-rw-r--r-- | src/eval.c | 10682 |
1 files changed, 10682 insertions, 0 deletions
diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 000000000..2e339e6d0 --- /dev/null +++ b/src/eval.c @@ -0,0 +1,10682 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * eval.c: Expression evaluation. + */ +#if defined(MSDOS) || defined(MSWIN) +# include <io.h> /* for mch_open(), must be before vim.h */ +#endif + +#include "vim.h" + +#ifdef AMIGA +# include <time.h> /* for strftime() */ +#endif + +#ifdef MACOS +# include <time.h> /* for time_t */ +#endif + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) + +#if SIZEOF_INT <= 3 /* use long if int is smaller than 32 bits */ +typedef long varnumber_T; +#else +typedef int varnumber_T; +#endif + +/* + * Structure to hold an internal variable. + */ +typedef struct +{ + char_u *var_name; /* name of variable */ + char var_type; /* VAR_NUMBER or VAR_STRING */ + union + { + varnumber_T var_number; /* number value */ + char_u *var_string; /* string value (Careful: can be NULL!) */ + } var_val; +} var; + +#define VAR_UNKNOWN 0 +#define VAR_NUMBER 1 +#define VAR_STRING 2 + +typedef var * VAR; + +/* + * All user-defined global variables are stored in "variables". + */ +garray_T variables = {0, 0, sizeof(var), 4, NULL}; + +/* + * Array to hold an array with variables local to each sourced script. + */ +static garray_T ga_scripts = {0, 0, sizeof(garray_T), 4, NULL}; +#define SCRIPT_VARS(id) (((garray_T *)ga_scripts.ga_data)[(id) - 1]) + + +#define VAR_ENTRY(idx) (((VAR)(variables.ga_data))[idx]) +#define VAR_GAP_ENTRY(idx, gap) (((VAR)(gap->ga_data))[idx]) +#define BVAR_ENTRY(idx) (((VAR)(curbuf->b_vars.ga_data))[idx]) +#define WVAR_ENTRY(idx) (((VAR)(curwin->w_vars.ga_data))[idx]) + +static int echo_attr = 0; /* attributes used for ":echo" */ + +/* + * Structure to hold info for a user function. + */ +typedef struct ufunc ufunc_T; + +struct ufunc +{ + ufunc_T *next; /* next function in list */ + char_u *name; /* name of function; can start with <SNR>123_ + (<SNR> is K_SPECIAL KS_EXTRA KE_SNR) */ + int varargs; /* variable nr of arguments */ + int flags; + int calls; /* nr of active calls */ + garray_T args; /* arguments */ + garray_T lines; /* function lines */ + scid_T script_ID; /* ID of script where function was defined, + used for s: variables */ +}; + +/* function flags */ +#define FC_ABORT 1 /* abort function on error */ +#define FC_RANGE 2 /* function accepts range */ + +/* + * All user-defined functions are found in the forward-linked function list. + * The first function is pointed at by firstfunc. + */ +ufunc_T *firstfunc = NULL; + +#define FUNCARG(fp, j) ((char_u **)(fp->args.ga_data))[j] +#define FUNCLINE(fp, j) ((char_u **)(fp->lines.ga_data))[j] + +/* structure to hold info for a function that is currently being executed. */ +struct funccall +{ + ufunc_T *func; /* function being called */ + int linenr; /* next line to be executed */ + int returned; /* ":return" used */ + int argcount; /* nr of arguments */ + VAR argvars; /* arguments */ + var a0_var; /* "a:0" variable */ + var firstline; /* "a:firstline" variable */ + var lastline; /* "a:lastline" variable */ + garray_T l_vars; /* local function variables */ + VAR retvar; /* return value variable */ + linenr_T breakpoint; /* next line with breakpoint or zero */ + int dbg_tick; /* debug_tick when breakpoint was set */ + int level; /* top nesting level of executed function */ +}; + +/* + * Return the name of the executed function. + */ + char_u * +func_name(cookie) + void *cookie; +{ + return ((struct funccall *)cookie)->func->name; +} + +/* + * Return the address holding the next breakpoint line for a funccall cookie. + */ + linenr_T * +func_breakpoint(cookie) + void *cookie; +{ + return &((struct funccall *)cookie)->breakpoint; +} + +/* + * Return the address holding the debug tick for a funccall cookie. + */ + int * +func_dbg_tick(cookie) + void *cookie; +{ + return &((struct funccall *)cookie)->dbg_tick; +} + +/* + * Return the nesting level for a funccall cookie. + */ + int +func_level(cookie) + void *cookie; +{ + return ((struct funccall *)cookie)->level; +} + +/* pointer to funccal for currently active function */ +struct funccall *current_funccal = NULL; + +/* + * Return TRUE when a function was ended by a ":return" command. + */ + int +current_func_returned() +{ + return current_funccal->returned; +} + + +/* + * Array to hold the value of v: variables. + */ +#include "version.h" + +/* values for flags: */ +#define VV_COMPAT 1 /* compatible, also used without "v:" */ +#define VV_RO 2 /* read-only */ + +struct vimvar +{ + char *name; /* name of variable, without v: */ + int len; /* length of name */ + char_u *val; /* current value (can also be a number!) */ + char type; /* VAR_NUMBER or VAR_STRING */ + char flags; /* VV_COMPAT and VV_RO */ +} vimvars[VV_LEN] = +{ /* The order here must match the VV_ defines in vim.h! */ + {"count", sizeof("count") - 1, NULL, VAR_NUMBER, VV_COMPAT+VV_RO}, + {"count1", sizeof("count1") - 1, NULL, VAR_NUMBER, VV_RO}, + {"prevcount", sizeof("prevcount") - 1, NULL, VAR_NUMBER, VV_RO}, + {"errmsg", sizeof("errmsg") - 1, NULL, VAR_STRING, VV_COMPAT}, + {"warningmsg", sizeof("warningmsg") - 1, NULL, VAR_STRING, 0}, + {"statusmsg", sizeof("statusmsg") - 1, NULL, VAR_STRING, 0}, + {"shell_error", sizeof("shell_error") - 1, NULL, VAR_NUMBER, + VV_COMPAT+VV_RO}, + {"this_session", sizeof("this_session") - 1, NULL, VAR_STRING, VV_COMPAT}, + {"version", sizeof("version") - 1, (char_u *)VIM_VERSION_100, + VAR_NUMBER, VV_COMPAT+VV_RO}, + {"lnum", sizeof("lnum") - 1, NULL, VAR_NUMBER, VV_RO}, + {"termresponse", sizeof("termresponse") - 1, NULL, VAR_STRING, VV_RO}, + {"fname", sizeof("fname") - 1, NULL, VAR_STRING, VV_RO}, + {"lang", sizeof("lang") - 1, NULL, VAR_STRING, VV_RO}, + {"lc_time", sizeof("lc_time") - 1, NULL, VAR_STRING, VV_RO}, + {"ctype", sizeof("ctype") - 1, NULL, VAR_STRING, VV_RO}, + {"charconvert_from", sizeof("charconvert_from") - 1, NULL, VAR_STRING, VV_RO}, + {"charconvert_to", sizeof("charconvert_to") - 1, NULL, VAR_STRING, VV_RO}, + {"fname_in", sizeof("fname_in") - 1, NULL, VAR_STRING, VV_RO}, + {"fname_out", sizeof("fname_out") - 1, NULL, VAR_STRING, VV_RO}, + {"fname_new", sizeof("fname_new") - 1, NULL, VAR_STRING, VV_RO}, + {"fname_diff", sizeof("fname_diff") - 1, NULL, VAR_STRING, VV_RO}, + {"cmdarg", sizeof("cmdarg") - 1, NULL, VAR_STRING, VV_RO}, + {"foldstart", sizeof("foldstart") - 1, NULL, VAR_NUMBER, VV_RO}, + {"foldend", sizeof("foldend") - 1, NULL, VAR_NUMBER, VV_RO}, + {"folddashes", sizeof("folddashes") - 1, NULL, VAR_STRING, VV_RO}, + {"foldlevel", sizeof("foldlevel") - 1, NULL, VAR_NUMBER, VV_RO}, + {"progname", sizeof("progname") - 1, NULL, VAR_STRING, VV_RO}, + {"servername", sizeof("servername") - 1, NULL, VAR_STRING, VV_RO}, + {"dying", sizeof("dying") - 1, NULL, VAR_NUMBER, VV_RO}, + {"exception", sizeof("exception") - 1, NULL, VAR_STRING, VV_RO}, + {"throwpoint", sizeof("throwpoint") - 1, NULL, VAR_STRING, VV_RO}, + {"register", sizeof("register") - 1, NULL, VAR_STRING, VV_RO}, + {"cmdbang", sizeof("cmdbang") - 1, NULL, VAR_NUMBER, VV_RO}, +}; + +static int eval0 __ARGS((char_u *arg, VAR retvar, char_u **nextcmd, int evaluate)); +static int eval1 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval2 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval3 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval4 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval5 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval6 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int eval7 __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int get_option_var __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int get_string_var __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int get_lit_string_var __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int get_env_var __ARGS((char_u **arg, VAR retvar, int evaluate)); +static int find_internal_func __ARGS((char_u *name)); +static int get_func_var __ARGS((char_u *name, int len, VAR retvar, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate)); +static int call_func __ARGS((char_u *name, int len, VAR retvar, int argcount, VAR argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate)); +static void f_append __ARGS((VAR argvars, VAR retvar)); +static void f_argc __ARGS((VAR argvars, VAR retvar)); +static void f_argidx __ARGS((VAR argvars, VAR retvar)); +static void f_argv __ARGS((VAR argvars, VAR retvar)); +static void f_browse __ARGS((VAR argvars, VAR retvar)); +static buf_T *find_buffer __ARGS((VAR avar)); +static void f_bufexists __ARGS((VAR argvars, VAR retvar)); +static void f_buflisted __ARGS((VAR argvars, VAR retvar)); +static void f_bufloaded __ARGS((VAR argvars, VAR retvar)); +static buf_T *get_buf_var __ARGS((VAR avar)); +static void f_bufname __ARGS((VAR argvars, VAR retvar)); +static void f_bufnr __ARGS((VAR argvars, VAR retvar)); +static void f_bufwinnr __ARGS((VAR argvars, VAR retvar)); +static void f_byte2line __ARGS((VAR argvars, VAR retvar)); +static void f_char2nr __ARGS((VAR argvars, VAR retvar)); +static void f_cindent __ARGS((VAR argvars, VAR retvar)); +static void f_col __ARGS((VAR argvars, VAR retvar)); +static void f_confirm __ARGS((VAR argvars, VAR retvar)); +static void f_cscope_connection __ARGS((VAR argvars, VAR retvar)); +static void f_cursor __ARGS((VAR argsvars, VAR retvar)); +static void f_delete __ARGS((VAR argvars, VAR retvar)); +static void f_did_filetype __ARGS((VAR argvars, VAR retvar)); +static void f_escape __ARGS((VAR argvars, VAR retvar)); +static void f_eventhandler __ARGS((VAR argvars, VAR retvar)); +static void f_executable __ARGS((VAR argvars, VAR retvar)); +static void f_exists __ARGS((VAR argvars, VAR retvar)); +static void f_expand __ARGS((VAR argvars, VAR retvar)); +static void f_filereadable __ARGS((VAR argvars, VAR retvar)); +static void f_filewritable __ARGS((VAR argvars, VAR retvar)); +static void f_fnamemodify __ARGS((VAR argvars, VAR retvar)); +static void f_foldclosed __ARGS((VAR argvars, VAR retvar)); +static void f_foldclosedend __ARGS((VAR argvars, VAR retvar)); +static void foldclosed_both __ARGS((VAR argvars, VAR retvar, int end)); +static void f_foldlevel __ARGS((VAR argvars, VAR retvar)); +static void f_foldtext __ARGS((VAR argvars, VAR retvar)); +static void f_foreground __ARGS((VAR argvars, VAR retvar)); +static void f_getbufvar __ARGS((VAR argvars, VAR retvar)); +static void f_getchar __ARGS((VAR argvars, VAR retvar)); +static void f_getcharmod __ARGS((VAR argvars, VAR retvar)); +static void f_getcmdline __ARGS((VAR argvars, VAR retvar)); +static void f_getcmdpos __ARGS((VAR argvars, VAR retvar)); +static void f_getcwd __ARGS((VAR argvars, VAR retvar)); +static void f_getfsize __ARGS((VAR argvars, VAR retvar)); +static void f_getftime __ARGS((VAR argvars, VAR retvar)); +static void f_getline __ARGS((VAR argvars, VAR retvar)); +static void f_getreg __ARGS((VAR argvars, VAR retvar)); +static void f_getregtype __ARGS((VAR argvars, VAR retvar)); +static void f_getwinposx __ARGS((VAR argvars, VAR retvar)); +static void f_getwinposy __ARGS((VAR argvars, VAR retvar)); +static void f_getwinvar __ARGS((VAR argvars, VAR retvar)); +static void f_glob __ARGS((VAR argvars, VAR retvar)); +static void f_globpath __ARGS((VAR argvars, VAR retvar)); +static void f_has __ARGS((VAR argvars, VAR retvar)); +static void f_hasmapto __ARGS((VAR argvars, VAR retvar)); +static void f_histadd __ARGS((VAR argvars, VAR retvar)); +static void f_histdel __ARGS((VAR argvars, VAR retvar)); +static void f_histget __ARGS((VAR argvars, VAR retvar)); +static void f_histnr __ARGS((VAR argvars, VAR retvar)); +static void f_hlexists __ARGS((VAR argvars, VAR retvar)); +static void f_hlID __ARGS((VAR argvars, VAR retvar)); +static void f_hostname __ARGS((VAR argvars, VAR retvar)); +static void f_iconv __ARGS((VAR argvars, VAR retvar)); +static void f_indent __ARGS((VAR argvars, VAR retvar)); +static void f_isdirectory __ARGS((VAR argvars, VAR retvar)); +static void f_input __ARGS((VAR argvars, VAR retvar)); +static void f_inputdialog __ARGS((VAR argvars, VAR retvar)); +static void f_inputrestore __ARGS((VAR argvars, VAR retvar)); +static void f_inputsave __ARGS((VAR argvars, VAR retvar)); +static void f_inputsecret __ARGS((VAR argvars, VAR retvar)); +static void f_last_buffer_nr __ARGS((VAR argvars, VAR retvar)); +static void f_libcall __ARGS((VAR argvars, VAR retvar)); +static void f_libcallnr __ARGS((VAR argvars, VAR retvar)); +static void libcall_common __ARGS((VAR argvars, VAR retvar, int type)); +static void f_line __ARGS((VAR argvars, VAR retvar)); +static void f_line2byte __ARGS((VAR argvars, VAR retvar)); +static void f_lispindent __ARGS((VAR argvars, VAR retvar)); +static void f_localtime __ARGS((VAR argvars, VAR retvar)); +static void f_maparg __ARGS((VAR argvars, VAR retvar)); +static void f_mapcheck __ARGS((VAR argvars, VAR retvar)); +static void get_maparg __ARGS((VAR argvars, VAR retvar, int exact)); +static void f_match __ARGS((VAR argvars, VAR retvar)); +static void f_matchend __ARGS((VAR argvars, VAR retvar)); +static void f_matchstr __ARGS((VAR argvars, VAR retvar)); +static void f_mode __ARGS((VAR argvars, VAR retvar)); +static void f_nextnonblank __ARGS((VAR argvars, VAR retvar)); +static void f_nr2char __ARGS((VAR argvars, VAR retvar)); +static void f_prevnonblank __ARGS((VAR argvars, VAR retvar)); +static void f_setbufvar __ARGS((VAR argvars, VAR retvar)); +static void f_setcmdpos __ARGS((VAR argvars, VAR retvar)); +static void f_setwinvar __ARGS((VAR argvars, VAR retvar)); +static void f_rename __ARGS((VAR argvars, VAR retvar)); +static void f_resolve __ARGS((VAR argvars, VAR retvar)); +static void f_search __ARGS((VAR argvars, VAR retvar)); +static void f_searchpair __ARGS((VAR argvars, VAR retvar)); +static int get_search_arg __ARGS((VAR varp, int *flagsp)); +static void f_remote_expr __ARGS((VAR argvars, VAR retvar)); +static void f_remote_foreground __ARGS((VAR argvars, VAR retvar)); +static void f_remote_peek __ARGS((VAR argvars, VAR retvar)); +static void f_remote_read __ARGS((VAR argvars, VAR retvar)); +static void f_remote_send __ARGS((VAR argvars, VAR retvar)); +static void f_server2client __ARGS((VAR argvars, VAR retvar)); +static void f_serverlist __ARGS((VAR argvars, VAR retvar)); +static void f_setline __ARGS((VAR argvars, VAR retvar)); +static void f_setreg __ARGS((VAR argvars, VAR retvar)); +static void f_simplify __ARGS((VAR argvars, VAR retvar)); +static void find_some_match __ARGS((VAR argvars, VAR retvar, int start)); +static void f_strftime __ARGS((VAR argvars, VAR retvar)); +static void f_stridx __ARGS((VAR argvars, VAR retvar)); +static void f_strlen __ARGS((VAR argvars, VAR retvar)); +static void f_strpart __ARGS((VAR argvars, VAR retvar)); +static void f_strridx __ARGS((VAR argvars, VAR retvar)); +static void f_strtrans __ARGS((VAR argvars, VAR retvar)); +static void f_synID __ARGS((VAR argvars, VAR retvar)); +static void f_synIDattr __ARGS((VAR argvars, VAR retvar)); +static void f_synIDtrans __ARGS((VAR argvars, VAR retvar)); +static void f_system __ARGS((VAR argvars, VAR retvar)); +static void f_submatch __ARGS((VAR argvars, VAR retvar)); +static void f_substitute __ARGS((VAR argvars, VAR retvar)); +static void f_tempname __ARGS((VAR argvars, VAR retvar)); +static void f_tolower __ARGS((VAR argvars, VAR retvar)); +static void f_toupper __ARGS((VAR argvars, VAR retvar)); +static void f_type __ARGS((VAR argvars, VAR retvar)); +static void f_virtcol __ARGS((VAR argvars, VAR retvar)); +static void f_visualmode __ARGS((VAR argvars, VAR retvar)); +static void f_winbufnr __ARGS((VAR argvars, VAR retvar)); +static void f_wincol __ARGS((VAR argvars, VAR retvar)); +static void f_winheight __ARGS((VAR argvars, VAR retvar)); +static void f_winline __ARGS((VAR argvars, VAR retvar)); +static void f_winnr __ARGS((VAR argvars, VAR retvar)); +static void f_winrestcmd __ARGS((VAR argvars, VAR retvar)); +static void f_winwidth __ARGS((VAR argvars, VAR retvar)); +static win_T *find_win_by_nr __ARGS((VAR vp)); +static pos_T *var2fpos __ARGS((VAR varp, int lnum)); +static int get_env_len __ARGS((char_u **arg)); +static int get_id_len __ARGS((char_u **arg)); +static int get_func_len __ARGS((char_u **arg, char_u **alias, int evaluate)); +static char_u *find_name_end __ARGS((char_u *arg, char_u **expr_start, char_u **expr_end)); +static int eval_isnamec __ARGS((int c)); +static int find_vim_var __ARGS((char_u *name, int len)); +static int get_var_var __ARGS((char_u *name, int len, VAR retvar)); +static VAR alloc_var __ARGS((void)); +static VAR alloc_string_var __ARGS((char_u *string)); +static void free_var __ARGS((VAR varp)); +static void clear_var __ARGS((VAR varp)); +static long get_var_number __ARGS((VAR varp)); +static linenr_T get_var_lnum __ARGS((VAR argvars)); +static char_u *get_var_string __ARGS((VAR varp)); +static char_u *get_var_string_buf __ARGS((VAR varp, char_u *buf)); +static VAR find_var __ARGS((char_u *name, int writing)); +static VAR find_var_in_ga __ARGS((garray_T *gap, char_u *varname)); +static garray_T *find_var_ga __ARGS((char_u *name, char_u **varname)); +static void var_free_one __ARGS((VAR v)); +static void list_one_var __ARGS((VAR v, char_u *prefix)); +static void list_vim_var __ARGS((int i)); +static void list_one_var_a __ARGS((char_u *prefix, char_u *name, int type, char_u *string)); +static void set_var __ARGS((char_u *name, VAR varp)); +static void copy_var __ARGS((VAR from, VAR to)); +static char_u *find_option_end __ARGS((char_u **arg, int *opt_flags)); +static char_u *trans_function_name __ARGS((char_u **pp, int skip, int internal)); +static int eval_fname_script __ARGS((char_u *p)); +static int eval_fname_sid __ARGS((char_u *p)); +static void list_func_head __ARGS((ufunc_T *fp, int indent)); +static void cat_func_name __ARGS((char_u *buf, ufunc_T *fp)); +static ufunc_T *find_func __ARGS((char_u *name)); +static void call_user_func __ARGS((ufunc_T *fp, int argcount, VAR argvars, VAR retvar, linenr_T firstline, linenr_T lastline)); + +/* Magic braces are always enabled, otherwise Vim scripts would not be + * portable. */ +#define FEAT_MAGIC_BRACES + +#ifdef FEAT_MAGIC_BRACES +static char_u * make_expanded_name __ARGS((char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end)); +#endif + +/* + * Set an internal variable to a string value. Creates the variable if it does + * not already exist. + */ + void +set_internal_string_var(name, value) + char_u *name; + char_u *value; +{ + char_u *val; + VAR varp; + + val = vim_strsave(value); + if (val != NULL) + { + varp = alloc_string_var(val); + if (varp != NULL) + { + set_var(name, varp); + free_var(varp); + } + } +} + +# if defined(FEAT_MBYTE) || defined(PROTO) + int +eval_charconvert(enc_from, enc_to, fname_from, fname_to) + char_u *enc_from; + char_u *enc_to; + char_u *fname_from; + char_u *fname_to; +{ + int err = FALSE; + + set_vim_var_string(VV_CC_FROM, enc_from, -1); + set_vim_var_string(VV_CC_TO, enc_to, -1); + set_vim_var_string(VV_FNAME_IN, fname_from, -1); + set_vim_var_string(VV_FNAME_OUT, fname_to, -1); + if (eval_to_bool(p_ccv, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_CC_FROM, NULL, -1); + set_vim_var_string(VV_CC_TO, NULL, -1); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + + if (err) + return FAIL; + return OK; +} +# endif + +# if defined(FEAT_POSTSCRIPT) || defined(PROTO) + int +eval_printexpr(fname, args) + char_u *fname; + char_u *args; +{ + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, fname, -1); + set_vim_var_string(VV_CMDARG, args, -1); + if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_CMDARG, NULL, -1); + + if (err) + { + mch_remove(fname); + return FAIL; + } + return OK; +} +# endif + +# if defined(FEAT_DIFF) || defined(PROTO) + void +eval_diff(origfile, newfile, outfile) + char_u *origfile; + char_u *newfile; + char_u *outfile; +{ + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_NEW, newfile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_dex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_NEW, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + + void +eval_patch(origfile, difffile, outfile) + char_u *origfile; + char_u *difffile; + char_u *outfile; +{ + int err; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_DIFF, difffile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_pex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_DIFF, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} +# endif + +/* + * Top level evaluation function, returning a boolean. + * Sets "error" to TRUE if there was an error. + * Return TRUE or FALSE. + */ + int +eval_to_bool(arg, error, nextcmd, skip) + char_u *arg; + int *error; + char_u **nextcmd; + int skip; /* only parse, don't execute */ +{ + var retvar; + int retval = FALSE; + + if (skip) + ++emsg_skip; + if (eval0(arg, &retvar, nextcmd, !skip) == FAIL) + { + *error = TRUE; + } + else + { + *error = FALSE; + if (!skip) + { + retval = (get_var_number(&retvar) != 0); + clear_var(&retvar); + } + } + if (skip) + --emsg_skip; + + return retval; +} + +/* + * Top level evaluation function, returning a string. If "skip" is TRUE, + * only parsing to "nextcmd" is done, without reporting errors. Return + * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. + */ + char_u * +eval_to_string_skip(arg, nextcmd, skip) + char_u *arg; + char_u **nextcmd; + int skip; /* only parse, don't execute */ +{ + var retvar; + char_u *retval; + + if (skip) + ++emsg_skip; + if (eval0(arg, &retvar, nextcmd, !skip) == FAIL || skip) + retval = NULL; + else + { + retval = vim_strsave(get_var_string(&retvar)); + clear_var(&retvar); + } + if (skip) + --emsg_skip; + + return retval; +} + +/* + * Top level evaluation function, returning a string. + * Return pointer to allocated memory, or NULL for failure. + */ + char_u * +eval_to_string(arg, nextcmd) + char_u *arg; + char_u **nextcmd; +{ + var retvar; + char_u *retval; + + if (eval0(arg, &retvar, nextcmd, TRUE) == FAIL) + retval = NULL; + else + { + retval = vim_strsave(get_var_string(&retvar)); + clear_var(&retvar); + } + + return retval; +} + +/* + * Call eval_to_string() with "sandbox" set and not using local variables. + */ + char_u * +eval_to_string_safe(arg, nextcmd) + char_u *arg; + char_u **nextcmd; +{ + char_u *retval; + void *save_funccalp; + + save_funccalp = save_funccal(); + ++sandbox; + retval = eval_to_string(arg, nextcmd); + --sandbox; + restore_funccal(save_funccalp); + return retval; +} + +#if 0 /* not used */ +/* + * Top level evaluation function, returning a string. + * Advances "arg" to the first non-blank after the evaluated expression. + * Return pointer to allocated memory, or NULL for failure. + * Doesn't give error messages. + */ + char_u * +eval_arg_to_string(arg) + char_u **arg; +{ + var retvar; + char_u *retval; + int ret; + + ++emsg_off; + + ret = eval1(arg, &retvar, TRUE); + if (ret == FAIL) + retval = NULL; + else + { + retval = vim_strsave(get_var_string(&retvar)); + clear_var(&retvar); + } + + --emsg_off; + + return retval; +} +#endif + +/* + * Top level evaluation function, returning a number. + * Evaluates "expr" silently. + * Returns -1 for an error. + */ + int +eval_to_number(expr) + char_u *expr; +{ + var retvar; + int retval; + char_u *p = expr; + + ++emsg_off; + + if (eval1(&p, &retvar, TRUE) == FAIL) + retval = -1; + else + { + retval = get_var_number(&retvar); + clear_var(&retvar); + } + --emsg_off; + + return retval; +} + +#if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) || defined(PROTO) +/* + * Call some vimL function and return the result as a string + * Uses argv[argc] for the function arguments. + */ + char_u * +call_vim_function(func, argc, argv, safe) + char_u *func; + int argc; + char_u **argv; + int safe; /* use the sandbox */ +{ + char_u *retval = NULL; + var retvar; + VAR argvars; + long n; + int len; + int i; + int doesrange; + void *save_funccalp = NULL; + + argvars = (VAR)alloc((unsigned)(argc * sizeof(var))); + if (argvars == NULL) + return NULL; + + for (i = 0; i < argc; i++) + { + /* Recognize a number argument, the others must be strings. */ + vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL); + if (len != 0 && len == (int)STRLEN(argv[i])) + { + argvars[i].var_type = VAR_NUMBER; + argvars[i].var_val.var_number = n; + } + else + { + argvars[i].var_type = VAR_STRING; + argvars[i].var_val.var_string = argv[i]; + } + } + + if (safe) + { + save_funccalp = save_funccal(); + ++sandbox; + } + + retvar.var_type = VAR_UNKNOWN; /* clear_var() uses this */ + if (call_func(func, (int)STRLEN(func), &retvar, argc, argvars, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &doesrange, TRUE) == OK) + retval = vim_strsave(get_var_string(&retvar)); + + clear_var(&retvar); + vim_free(argvars); + + if (safe) + { + --sandbox; + restore_funccal(save_funccalp); + } + return retval; +} +#endif + +/* + * Save the current function call pointer, and set it to NULL. + * Used when executing autocommands and for ":source". + */ + void * +save_funccal() +{ + struct funccall *fc; + + fc = current_funccal; + current_funccal = NULL; + return (void *)fc; +} + + void +restore_funccal(fc) + void *fc; +{ + current_funccal = (struct funccall *)fc; +} + +#ifdef FEAT_FOLDING +/* + * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding + * it in "*cp". Doesn't give error messages. + */ + int +eval_foldexpr(arg, cp) + char_u *arg; + int *cp; +{ + var retvar; + int retval; + char_u *s; + + ++emsg_off; + ++sandbox; + *cp = NUL; + if (eval0(arg, &retvar, NULL, TRUE) == FAIL) + retval = 0; + else + { + /* If the result is a number, just return the number. */ + if (retvar.var_type == VAR_NUMBER) + retval = retvar.var_val.var_number; + else if (retvar.var_type == VAR_UNKNOWN + || retvar.var_val.var_string == NULL) + retval = 0; + else + { + /* If the result is a string, check if there is a non-digit before + * the number. */ + s = retvar.var_val.var_string; + if (!VIM_ISDIGIT(*s) && *s != '-') + *cp = *s++; + retval = atol((char *)s); + } + clear_var(&retvar); + } + --emsg_off; + --sandbox; + + return retval; +} +#endif + +#ifdef FEAT_MAGIC_BRACES +/* + * Expands out the 'magic' {}'s in a variable/function name. + * Note that this can call itself recursively, to deal with + * constructs like foo{bar}{baz}{bam} + * The four pointer arguments point to "foo{expre}ss{ion}bar" + * "in_start" ^ + * "expr_start" ^ + * "expr_end" ^ + * "in_end" ^ + * + * Returns a new allocated string, which the caller must free. + * Returns NULL for failure. + */ + static char_u * +make_expanded_name(in_start, expr_start, expr_end, in_end) + char_u *in_start; + char_u *expr_start; + char_u *expr_end; + char_u *in_end; +{ + char_u c1; + char_u *retval = NULL; + char_u *temp_result; + char_u *nextcmd = NULL; + + if (expr_end == NULL || in_end == NULL) + return NULL; + *expr_start = NUL; + *expr_end = NUL; + c1 = *in_end; + *in_end = NUL; + + temp_result = eval_to_string(expr_start + 1, &nextcmd); + if (temp_result != NULL && nextcmd == NULL) + { + retval = alloc((unsigned)(STRLEN(temp_result) + (expr_start - in_start) + + (in_end - expr_end) + 1)); + + if (retval != NULL) + { + STRCPY(retval, in_start); + STRCAT(retval, temp_result); + STRCAT(retval, expr_end + 1); + } + } + vim_free(temp_result); + + *in_end = c1; /* put char back for error messages */ + *expr_start = '{'; + *expr_end = '}'; + + if (retval != NULL) + { + temp_result = find_name_end(retval, &expr_start, &expr_end); + if (expr_start != NULL) + { + /* Further expansion! */ + temp_result = make_expanded_name(retval, expr_start, + expr_end, temp_result); + vim_free(retval); + retval = temp_result; + } + } + + return retval; + +} +#endif /* FEAT_MAGIC_BRACES */ + +/* + * ":let var = expr" assignment command. + * ":let var" list one variable value + * ":let" list all variable values + */ + void +ex_let(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + char_u *expr; + char_u *name; + VAR varp; + var retvar; + char_u *p; + int c1 = 0, c2; + int i; + char_u *expr_start; + char_u *expr_end; + char_u *name_end; + + name_end = find_name_end(arg, &expr_start, &expr_end); + expr = vim_strchr(name_end, '='); + if (expr == NULL) + { + if (ends_excmd(*arg)) + { + if (!eap->skip) + { + /* + * List all variables. + */ + for (i = 0; i < variables.ga_len && !got_int; ++i) + if (VAR_ENTRY(i).var_name != NULL) + list_one_var(&VAR_ENTRY(i), (char_u *)""); + for (i = 0; i < curbuf->b_vars.ga_len && !got_int; ++i) + if (BVAR_ENTRY(i).var_name != NULL) + list_one_var(&BVAR_ENTRY(i), (char_u *)"b:"); + for (i = 0; i < curwin->w_vars.ga_len && !got_int; ++i) + if (WVAR_ENTRY(i).var_name != NULL) + list_one_var(&WVAR_ENTRY(i), (char_u *)"w:"); + for (i = 0; i < VV_LEN && !got_int; ++i) + if (vimvars[i].type == VAR_NUMBER || vimvars[i].val != NULL) + list_vim_var(i); + } + } + else + { + int error = FALSE; + + /* + * List variables. + */ + while (!ends_excmd(*arg) && !got_int) + { + char_u *temp_string = NULL; + int arg_len; + + /* Find the end of the name. */ + name_end = find_name_end(arg, &expr_start, &expr_end); + + if (!vim_iswhite(*name_end) && !ends_excmd(*name_end)) + { + emsg_severe = TRUE; + EMSG(_(e_trailing)); + break; + } + if (!error && !eap->skip) + { +#ifdef FEAT_MAGIC_BRACES + if (expr_start != NULL) + { + temp_string = make_expanded_name(arg, expr_start, + expr_end, name_end); + if (temp_string == NULL) + { + /* + * Report an invalid expression in braces, unless + * the expression evaluation has been cancelled due + * to an aborting error, an interrupt, or an + * exception. + */ + if (!aborting()) + { + emsg_severe = TRUE; + EMSG2(_(e_invarg2), arg); + break; + } + error = TRUE; + arg = skipwhite(name_end); + continue; + } + arg = temp_string; + arg_len = STRLEN(temp_string); + } + else +#endif + { + c1 = *name_end; + *name_end = NUL; + arg_len = (int)(name_end - arg); + } + i = find_vim_var(arg, arg_len); + if (i >= 0) + list_vim_var(i); + else if (STRCMP("b:changedtick", arg) == 0) + { + char_u numbuf[NUMBUFLEN]; + + sprintf((char *)numbuf, "%ld", + (long)curbuf->b_changedtick); + list_one_var_a((char_u *)"b:", (char_u *)"changedtick", + VAR_NUMBER, numbuf); + } + else + { + varp = find_var(arg, FALSE); + if (varp == NULL) + { + /* Skip further arguments but do continue to + * search for a trailing command. */ + EMSG2(_("E106: Unknown variable: \"%s\""), arg); + error = TRUE; + } + else + { + name = vim_strchr(arg, ':'); + if (name != NULL) + { + /* "a:" vars have no name stored, use whole + * arg */ + if (arg[0] == 'a' && arg[1] == ':') + c2 = NUL; + else + { + c2 = *++name; + *name = NUL; + } + list_one_var(varp, arg); + if (c2 != NUL) + *name = c2; + } + else + list_one_var(varp, (char_u *)""); + } + } +#ifdef FEAT_MAGIC_BRACES + if (expr_start != NULL) + vim_free(temp_string); + else +#endif + *name_end = c1; + } + arg = skipwhite(name_end); + } + } + eap->nextcmd = check_nextcmd(arg); + } + else + { + if (eap->skip) + ++emsg_skip; + i = eval0(expr + 1, &retvar, &eap->nextcmd, !eap->skip); + if (eap->skip) + { + if (i != FAIL) + clear_var(&retvar); + --emsg_skip; + } + else if (i != FAIL) + { + /* + * ":let $VAR = expr": Set environment variable. + */ + if (*arg == '$') + { + int len; + int cc; + + /* Find the end of the name. */ + ++arg; + name = arg; + len = get_env_len(&arg); + if (len == 0) + EMSG2(_(e_invarg2), name - 1); + else + { + if (*skipwhite(arg) != '=') + EMSG(_(e_letunexp)); + else + { + cc = name[len]; + name[len] = NUL; + p = get_var_string(&retvar); + vim_setenv(name, p); + if (STRICMP(name, "HOME") == 0) + init_homedir(); + else if (didset_vim && STRICMP(name, "VIM") == 0) + didset_vim = FALSE; + else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) + didset_vimruntime = FALSE; + name[len] = cc; + } + } + } + + /* + * ":let &option = expr": Set option value. + * ":let &l:option = expr": Set local option value. + * ":let &g:option = expr": Set global option value. + */ + else if (*arg == '&') + { + int opt_flags; + + /* + * Find the end of the name; + */ + p = find_option_end(&arg, &opt_flags); + if (p == NULL || *skipwhite(p) != '=') + EMSG(_(e_letunexp)); + else + { + c1 = *p; + *p = NUL; + set_option_value(arg, get_var_number(&retvar), + get_var_string(&retvar), opt_flags); + *p = c1; /* put back for error messages */ + } + } + + /* + * ":let @r = expr": Set register contents. + */ + else if (*arg == '@') + { + ++arg; + if (*skipwhite(arg + 1) != '=') + EMSG(_(e_letunexp)); + else + write_reg_contents(*arg == '@' ? '"' : *arg, + get_var_string(&retvar), -1, FALSE); + } + + /* + * ":let var = expr": Set internal variable. + */ + else if (eval_isnamec(*arg) && !VIM_ISDIGIT(*arg)) + { + /* Find the end of the name. */ + p = find_name_end(arg, &expr_start, &expr_end); + + if (*skipwhite(p) != '=') + EMSG(_(e_letunexp)); + else if (p - arg == 13 + && STRNCMP(arg, "b:changedtick", 13) == 0) + EMSG2(_(e_readonlyvar), arg); +#ifdef FEAT_MAGIC_BRACES + else if (expr_start != NULL) + { + char_u *temp_string; + + temp_string = make_expanded_name(arg, expr_start, + expr_end, p); + if (temp_string == NULL) + { + /* + * Report an invalid expression in braces, unless the + * expression evaluation has been cancelled due to an + * aborting error, an interrupt, or an exception. + */ + if (!aborting()) + EMSG2(_(e_invarg2), arg); + } + else + { + set_var(temp_string, &retvar); + vim_free(temp_string); + } + } +#endif + else + { + c1 = *p; + *p = NUL; + set_var(arg, &retvar); + *p = c1; /* put char back for error messages */ + } + } + + else + { + EMSG2(_(e_invarg2), arg); + } + + clear_var(&retvar); + } + } +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + + void +set_context_for_expression(xp, arg, cmdidx) + expand_T *xp; + char_u *arg; + cmdidx_T cmdidx; +{ + int got_eq = FALSE; + int c; + + xp->xp_context = cmdidx == CMD_let ? EXPAND_USER_VARS + : cmdidx == CMD_call ? EXPAND_FUNCTIONS + : EXPAND_EXPRESSION; + while ((xp->xp_pattern = vim_strpbrk(arg, + (char_u *)"\"'+-*/%.=!?~|&$([<>,#")) != NULL) + { + c = *xp->xp_pattern; + if (c == '&') + { + c = xp->xp_pattern[1]; + if (c == '&') + { + ++xp->xp_pattern; + xp->xp_context = cmdidx != CMD_let || got_eq + ? EXPAND_EXPRESSION : EXPAND_NOTHING; + } + else if (c != ' ') + xp->xp_context = EXPAND_SETTINGS; + } + else if (c == '$') + { + /* environment variable */ + xp->xp_context = EXPAND_ENV_VARS; + } + else if (c == '=') + { + got_eq = TRUE; + xp->xp_context = EXPAND_EXPRESSION; + } + else if (c == '<' + && xp->xp_context == EXPAND_FUNCTIONS + && vim_strchr(xp->xp_pattern, '(') == NULL) + { + /* Function name can start with "<SNR>" */ + break; + } + else if (cmdidx != CMD_let || got_eq) + { + if (c == '"') /* string */ + { + while ((c = *++xp->xp_pattern) != NUL && c != '"') + if (c == '\\' && xp->xp_pattern[1] != NUL) + ++xp->xp_pattern; + xp->xp_context = EXPAND_NOTHING; + } + else if (c == '\'') /* literal string */ + { + while ((c = *++xp->xp_pattern) != NUL && c != '\'') + /* skip */ ; + xp->xp_context = EXPAND_NOTHING; + } + else if (c == '|') + { + if (xp->xp_pattern[1] == '|') + { + ++xp->xp_pattern; + xp->xp_context = EXPAND_EXPRESSION; + } + else + xp->xp_context = EXPAND_COMMANDS; + } + else + xp->xp_context = EXPAND_EXPRESSION; + } + else + xp->xp_context = EXPAND_NOTHING; + arg = xp->xp_pattern; + if (*arg != NUL) + while ((c = *++arg) != NUL && (c == ' ' || c == '\t')) + /* skip */ ; + } + xp->xp_pattern = arg; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * ":1,25call func(arg1, arg2)" function call. + */ + void +ex_call(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + char_u *startarg; + char_u *alias; + char_u *name; + var retvar; + int len; + linenr_T lnum; + int doesrange; + int failed = FALSE; + + name = arg; + len = get_func_len(&arg, &alias, !eap->skip); + if (len == 0) + goto end; + if (alias != NULL) + name = alias; + + startarg = arg; + retvar.var_type = VAR_UNKNOWN; /* clear_var() uses this */ + + if (*startarg != '(') + { + EMSG2(_("E107: Missing braces: %s"), name); + goto end; + } + + /* + * When skipping, evaluate the function once, to find the end of the + * arguments. + * When the function takes a range, this is discovered after the first + * call, and the loop is broken. + */ + if (eap->skip) + { + ++emsg_skip; + lnum = eap->line2; /* do it once, also with an invalid range */ + } + else + lnum = eap->line1; + for ( ; lnum <= eap->line2; ++lnum) + { + if (!eap->skip && eap->addr_count > 0) + { + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + } + arg = startarg; + if (get_func_var(name, len, &retvar, &arg, + eap->line1, eap->line2, &doesrange, !eap->skip) == FAIL) + { + failed = TRUE; + break; + } + clear_var(&retvar); + if (doesrange || eap->skip) + break; + /* Stop when immediately aborting on error, or when an interrupt + * occurred or an exception was thrown but not caught. get_func_var() + * returned OK, so that the check for trailing characters below is + * executed. */ + if (aborting()) + break; + } + if (eap->skip) + --emsg_skip; + + if (!failed) + { + /* Check for trailing illegal characters and a following command. */ + if (!ends_excmd(*arg)) + { + emsg_severe = TRUE; + EMSG(_(e_trailing)); + } + else + eap->nextcmd = check_nextcmd(arg); + } + +end: + if (alias != NULL) + vim_free(alias); +} + +/* + * ":unlet[!] var1 ... " command. + */ + void +ex_unlet(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + char_u *name_end; + char_u cc; + char_u *expr_start; + char_u *expr_end; + int error = FALSE; + + do + { + /* Find the end of the name. */ + name_end = find_name_end(arg, &expr_start, &expr_end); + + if (!vim_iswhite(*name_end) && !ends_excmd(*name_end)) + { + emsg_severe = TRUE; + EMSG(_(e_trailing)); + break; + } + + if (!error && !eap->skip) + { +#ifdef FEAT_MAGIC_BRACES + if (expr_start != NULL) + { + char_u *temp_string; + + temp_string = make_expanded_name(arg, expr_start, + expr_end, name_end); + if (temp_string == NULL) + { + /* + * Report an invalid expression in braces, unless the + * expression evaluation has been cancelled due to an + * aborting error, an interrupt, or an exception. + */ + if (!aborting()) + { + emsg_severe = TRUE; + EMSG2(_(e_invarg2), arg); + break; + } + error = TRUE; + } + else + { + if (do_unlet(temp_string) == FAIL && !eap->forceit) + { + EMSG2(_("E108: No such variable: \"%s\""), temp_string); + error = TRUE; + } + vim_free(temp_string); + } + } + else +#endif + { + cc = *name_end; + *name_end = NUL; + + if (do_unlet(arg) == FAIL && !eap->forceit) + { + EMSG2(_("E108: No such variable: \"%s\""), arg); + error = TRUE; + } + + *name_end = cc; + } + } + arg = skipwhite(name_end); + } while (!ends_excmd(*arg)); + + eap->nextcmd = check_nextcmd(arg); +} + +/* + * "unlet" a variable. Return OK if it existed, FAIL if not. + */ + int +do_unlet(name) + char_u *name; +{ + VAR v; + + v = find_var(name, TRUE); + if (v != NULL) + { + var_free_one(v); + return OK; + } + return FAIL; +} + +#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) +/* + * Delete all "menutrans_" variables. + */ + void +del_menutrans_vars() +{ + int i; + + for (i = 0; i < variables.ga_len; ++i) + if (VAR_ENTRY(i).var_name != NULL + && STRNCMP(VAR_ENTRY(i).var_name, "menutrans_", 10) == 0) + var_free_one(&VAR_ENTRY(i)); +} +#endif + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * Local string buffer for the next two functions to store a variable name + * with its prefix. Allocated in cat_prefix_varname(), freed later in + * get_user_var_name(). + */ + +static char_u *cat_prefix_varname __ARGS((int prefix, char_u *name)); + +static char_u *varnamebuf = NULL; +static int varnamebuflen = 0; + +/* + * Function to concatenate a prefix and a variable name. + */ + static char_u * +cat_prefix_varname(prefix, name) + int prefix; + char_u *name; +{ + int len; + + len = (int)STRLEN(name) + 3; + if (len > varnamebuflen) + { + vim_free(varnamebuf); + len += 10; /* some additional space */ + varnamebuf = alloc(len); + if (varnamebuf == NULL) + { + varnamebuflen = 0; + return NULL; + } + varnamebuflen = len; + } + *varnamebuf = prefix; + varnamebuf[1] = ':'; + STRCPY(varnamebuf + 2, name); + return varnamebuf; +} + +/* + * Function given to ExpandGeneric() to obtain the list of user defined + * (global/buffer/window/built-in) variable names. + */ +/*ARGSUSED*/ + char_u * +get_user_var_name(xp, idx) + expand_T *xp; + int idx; +{ + static int gidx; + static int bidx; + static int widx; + static int vidx; + char_u *name; + + if (idx == 0) + gidx = bidx = widx = vidx = 0; + if (gidx < variables.ga_len) /* Global variables */ + { + while ((name = VAR_ENTRY(gidx++).var_name) == NULL + && gidx < variables.ga_len) + /* skip */; + if (name != NULL) + { + if (STRNCMP("g:", xp->xp_pattern, 2) == 0) + return cat_prefix_varname('g', name); + else + return name; + } + } + if (bidx < curbuf->b_vars.ga_len) /* Current buffer variables */ + { + while ((name = BVAR_ENTRY(bidx++).var_name) == NULL + && bidx < curbuf->b_vars.ga_len) + /* skip */; + if (name != NULL) + return cat_prefix_varname('b', name); + } + if (bidx == curbuf->b_vars.ga_len) + { + ++bidx; + return (char_u *)"b:changedtick"; + } + if (widx < curwin->w_vars.ga_len) /* Current window variables */ + { + while ((name = WVAR_ENTRY(widx++).var_name) == NULL + && widx < curwin->w_vars.ga_len) + /* skip */; + if (name != NULL) + return cat_prefix_varname('w', name); + } + if (vidx < VV_LEN) /* Built-in variables */ + return cat_prefix_varname('v', (char_u *)vimvars[vidx++].name); + + vim_free(varnamebuf); + varnamebuf = NULL; + varnamebuflen = 0; + return NULL; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * types for expressions. + */ +typedef enum +{ + TYPE_UNKNOWN = 0 + , TYPE_EQUAL /* == */ + , TYPE_NEQUAL /* != */ + , TYPE_GREATER /* > */ + , TYPE_GEQUAL /* >= */ + , TYPE_SMALLER /* < */ + , TYPE_SEQUAL /* <= */ + , TYPE_MATCH /* =~ */ + , TYPE_NOMATCH /* !~ */ +} exptype_T; + +/* + * The "evaluate" argument: When FALSE, the argument is only parsed but not + * executed. The function may return OK, but the retvar will be of type + * VAR_UNKNOWN. The function still returns FAIL for a syntax error. + */ + +/* + * Handle zero level expression. + * This calls eval1() and handles error message and nextcmd. + * Return OK or FAIL. + */ + static int +eval0(arg, retvar, nextcmd, evaluate) + char_u *arg; + VAR retvar; + char_u **nextcmd; + int evaluate; +{ + int ret; + char_u *p; + + p = skipwhite(arg); + ret = eval1(&p, retvar, evaluate); + if (ret == FAIL || !ends_excmd(*p)) + { + if (ret != FAIL) + clear_var(retvar); + /* + * Report the invalid expression unless the expression evaluation has + * been cancelled due to an aborting error, an interrupt, or an + * exception. + */ + if (!aborting()) + EMSG2(_(e_invexpr2), arg); + ret = FAIL; + } + if (nextcmd != NULL) + *nextcmd = check_nextcmd(p); + + return ret; +} + +/* + * Handle top level expression: + * expr1 ? expr0 : expr0 + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval1(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + int result; + var var2; + + /* + * Get the first variable. + */ + if (eval2(arg, retvar, evaluate) == FAIL) + return FAIL; + + if ((*arg)[0] == '?') + { + result = FALSE; + if (evaluate) + { + if (get_var_number(retvar) != 0) + result = TRUE; + clear_var(retvar); + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, retvar, evaluate && result) == FAIL) /* recursive! */ + return FAIL; + + /* + * Check for the ":". + */ + if ((*arg)[0] != ':') + { + EMSG(_("E109: Missing ':' after '?'")); + if (evaluate && result) + clear_var(retvar); + return FAIL; + } + + /* + * Get the third variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, &var2, evaluate && !result) == FAIL) /* recursive! */ + { + if (evaluate && result) + clear_var(retvar); + return FAIL; + } + if (evaluate && !result) + *retvar = var2; + } + + return OK; +} + +/* + * Handle first level expression: + * expr2 || expr2 || expr2 logical OR + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval2(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + long result; + int first; + + /* + * Get the first variable. + */ + if (eval3(arg, retvar, evaluate) == FAIL) + return FAIL; + + /* + * Repeat until there is no following "||". + */ + first = TRUE; + result = FALSE; + while ((*arg)[0] == '|' && (*arg)[1] == '|') + { + if (evaluate && first) + { + if (get_var_number(retvar) != 0) + result = TRUE; + clear_var(retvar); + first = FALSE; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval3(arg, &var2, evaluate && !result) == FAIL) + return FAIL; + + /* + * Compute the result. + */ + if (evaluate && !result) + { + if (get_var_number(&var2) != 0) + result = TRUE; + clear_var(&var2); + } + if (evaluate) + { + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = result; + } + } + + return OK; +} + +/* + * Handle second level expression: + * expr3 && expr3 && expr3 logical AND + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval3(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + long result; + int first; + + /* + * Get the first variable. + */ + if (eval4(arg, retvar, evaluate) == FAIL) + return FAIL; + + /* + * Repeat until there is no following "&&". + */ + first = TRUE; + result = TRUE; + while ((*arg)[0] == '&' && (*arg)[1] == '&') + { + if (evaluate && first) + { + if (get_var_number(retvar) == 0) + result = FALSE; + clear_var(retvar); + first = FALSE; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval4(arg, &var2, evaluate && result) == FAIL) + return FAIL; + + /* + * Compute the result. + */ + if (evaluate && result) + { + if (get_var_number(&var2) == 0) + result = FALSE; + clear_var(&var2); + } + if (evaluate) + { + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = result; + } + } + + return OK; +} + +/* + * Handle third level expression: + * var1 == var2 + * var1 =~ var2 + * var1 != var2 + * var1 !~ var2 + * var1 > var2 + * var1 >= var2 + * var1 < var2 + * var1 <= var2 + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval4(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + char_u *p; + int i; + exptype_T type = TYPE_UNKNOWN; + int len = 2; + long n1, n2; + char_u *s1, *s2; + char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; + regmatch_T regmatch; + int ic; + char_u *save_cpo; + + /* + * Get the first variable. + */ + if (eval5(arg, retvar, evaluate) == FAIL) + return FAIL; + + p = *arg; + switch (p[0]) + { + case '=': if (p[1] == '=') + type = TYPE_EQUAL; + else if (p[1] == '~') + type = TYPE_MATCH; + break; + case '!': if (p[1] == '=') + type = TYPE_NEQUAL; + else if (p[1] == '~') + type = TYPE_NOMATCH; + break; + case '>': if (p[1] != '=') + { + type = TYPE_GREATER; + len = 1; + } + else + type = TYPE_GEQUAL; + break; + case '<': if (p[1] != '=') + { + type = TYPE_SMALLER; + len = 1; + } + else + type = TYPE_SEQUAL; + break; + } + + /* + * If there is a comparitive operator, use it. + */ + if (type != TYPE_UNKNOWN) + { + /* extra question mark appended: ignore case */ + if (p[len] == '?') + { + ic = TRUE; + ++len; + } + /* extra '#' appended: match case */ + else if (p[len] == '#') + { + ic = FALSE; + ++len; + } + /* nothing appened: use 'ignorecase' */ + else + ic = p_ic; + + /* + * Get the second variable. + */ + *arg = skipwhite(p + len); + if (eval5(arg, &var2, evaluate) == FAIL) + { + clear_var(retvar); + return FAIL; + } + + if (evaluate) + { + /* + * If one of the two variables is a number, compare as a number. + * When using "=~" or "!~", always compare as string. + */ + if ((retvar->var_type == VAR_NUMBER || var2.var_type == VAR_NUMBER) + && type != TYPE_MATCH && type != TYPE_NOMATCH) + { + n1 = get_var_number(retvar); + n2 = get_var_number(&var2); + switch (type) + { + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; /* avoid gcc warning */ + } + } + else + { + s1 = get_var_string_buf(retvar, buf1); + s2 = get_var_string_buf(&var2, buf2); + if (type != TYPE_MATCH && type != TYPE_NOMATCH) + i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2); + else + i = 0; + n1 = FALSE; + switch (type) + { + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; + + case TYPE_MATCH: + case TYPE_NOMATCH: + /* avoid 'l' flag in 'cpoptions' */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + regmatch.regprog = vim_regcomp(s2, + RE_MAGIC + RE_STRING); + regmatch.rm_ic = ic; + if (regmatch.regprog != NULL) + { + n1 = vim_regexec_nl(®match, s1, (colnr_T)0); + vim_free(regmatch.regprog); + if (type == TYPE_NOMATCH) + n1 = !n1; + } + p_cpo = save_cpo; + break; + + case TYPE_UNKNOWN: break; /* avoid gcc warning */ + } + } + clear_var(retvar); + clear_var(&var2); + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = n1; + } + } + + return OK; +} + +/* + * Handle fourth level expression: + * + number addition + * - number subtraction + * . string concatenation + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval5(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + int op; + long n1, n2; + char_u *s1, *s2; + char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; + char_u *p; + + /* + * Get the first variable. + */ + if (eval6(arg, retvar, evaluate) == FAIL) + return FAIL; + + /* + * Repeat computing, until no '+', '-' or '.' is following. + */ + for (;;) + { + op = **arg; + if (op != '+' && op != '-' && op != '.') + break; + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval6(arg, &var2, evaluate) == FAIL) + { + clear_var(retvar); + return FAIL; + } + + if (evaluate) + { + /* + * Compute the result. + */ + if (op == '.') + { + s1 = get_var_string_buf(retvar, buf1); + s2 = get_var_string_buf(&var2, buf2); + op = (int)STRLEN(s1); + p = alloc((unsigned)(op + STRLEN(s2) + 1)); + if (p != NULL) + { + STRCPY(p, s1); + STRCPY(p + op, s2); + } + clear_var(retvar); + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = p; + } + else + { + n1 = get_var_number(retvar); + n2 = get_var_number(&var2); + clear_var(retvar); + if (op == '+') + n1 = n1 + n2; + else + n1 = n1 - n2; + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = n1; + } + clear_var(&var2); + } + } + return OK; +} + +/* + * Handle fifth level expression: + * * number multiplication + * / number division + * % number modulo + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval6(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + int op; + long n1, n2; + + /* + * Get the first variable. + */ + if (eval7(arg, retvar, evaluate) == FAIL) + return FAIL; + + /* + * Repeat computing, until no '*', '/' or '%' is following. + */ + for (;;) + { + op = **arg; + if (op != '*' && op != '/' && op != '%') + break; + + if (evaluate) + { + n1 = get_var_number(retvar); + clear_var(retvar); + } + else + n1 = 0; + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval7(arg, &var2, evaluate) == FAIL) + return FAIL; + + if (evaluate) + { + n2 = get_var_number(&var2); + clear_var(&var2); + + /* + * Compute the result. + */ + if (op == '*') + n1 = n1 * n2; + else if (op == '/') + { + if (n2 == 0) /* give an error message? */ + n1 = 0x7fffffffL; + else + n1 = n1 / n2; + } + else + { + if (n2 == 0) /* give an error message? */ + n1 = 0; + else + n1 = n1 % n2; + } + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = n1; + } + } + + return OK; +} + +/* + * Handle sixth level expression: + * number number constant + * "string" string contstant + * 'string' literal string contstant + * &option-name option value + * @r register contents + * identifier variable value + * function() function call + * $VAR environment variable + * (expression) nested expression + * + * Also handle: + * ! in front logical NOT + * - in front unary minus + * + in front unary plus (ignored) + * trailing [] subscript in String + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to the next non-white after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval7(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + var var2; + long n; + int len; + char_u *s; + int val; + char_u *start_leader, *end_leader; + int ret = OK; + char_u *alias; + + /* + * Initialise variable so that clear_var() can't mistake this for a string + * and free a string that isn't there. + */ + retvar->var_type = VAR_UNKNOWN; + + /* + * Skip '!' and '-' characters. They are handled later. + */ + start_leader = *arg; + while (**arg == '!' || **arg == '-' || **arg == '+') + *arg = skipwhite(*arg + 1); + end_leader = *arg; + + switch (**arg) + { + /* + * Number constant. + */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); + *arg += len; + if (evaluate) + { + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = n; + } + break; + + /* + * String constant: "string". + */ + case '"': ret = get_string_var(arg, retvar, evaluate); + break; + + /* + * Literal string constant: 'string'. + */ + case '\'': ret = get_lit_string_var(arg, retvar, evaluate); + break; + + /* + * Option value: &name + */ + case '&': ret = get_option_var(arg, retvar, evaluate); + break; + + /* + * Environment variable: $VAR. + */ + case '$': ret = get_env_var(arg, retvar, evaluate); + break; + + /* + * Register contents: @r. + */ + case '@': ++*arg; + if (evaluate) + { + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = get_reg_contents(**arg, FALSE); + } + if (**arg != NUL) + ++*arg; + break; + + /* + * nested expression: (expression). + */ + case '(': *arg = skipwhite(*arg + 1); + ret = eval1(arg, retvar, evaluate); /* recursive! */ + if (**arg == ')') + ++*arg; + else if (ret == OK) + { + EMSG(_("E110: Missing ')'")); + clear_var(retvar); + ret = FAIL; + } + break; + + /* + * Must be a variable or function name then. + */ + default: s = *arg; + len = get_func_len(arg, &alias, evaluate); + if (alias != NULL) + s = alias; + + if (len == 0) + ret = FAIL; + else + { + if (**arg == '(') /* recursive! */ + { + ret = get_func_var(s, len, retvar, arg, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &len, evaluate); + /* Stop the expression evaluation when immediately + * aborting on error, or when an interrupt occurred or + * an exception was thrown but not caught. */ + if (aborting()) + { + if (ret == OK) + clear_var(retvar); + ret = FAIL; + } + } + else if (evaluate) + ret = get_var_var(s, len, retvar); + } + + if (alias != NULL) + vim_free(alias); + + break; + } + *arg = skipwhite(*arg); + + /* + * Handle expr[expr] subscript. + */ + if (**arg == '[' && ret == OK) + { + /* + * Get the variable from inside the []. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, &var2, evaluate) == FAIL) /* recursive! */ + { + clear_var(retvar); + return FAIL; + } + + /* Check for the ']'. */ + if (**arg != ']') + { + EMSG(_("E111: Missing ']'")); + clear_var(retvar); + clear_var(&var2); + return FAIL; + } + + if (evaluate) + { + n = get_var_number(&var2); + clear_var(&var2); + + /* + * The resulting variable is a string of a single character. + * If the index is too big or negative, the result is empty. + */ + s = get_var_string(retvar); + if (n >= (long)STRLEN(s) || n < 0) + s = NULL; + else + s = vim_strnsave(s + n, 1); + clear_var(retvar); + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = s; + } + *arg = skipwhite(*arg + 1); /* skip the ']' */ + } + + /* + * Apply logical NOT and unary '-', from right to left, ignore '+'. + */ + if (ret == OK && evaluate && end_leader > start_leader) + { + val = get_var_number(retvar); + while (end_leader > start_leader) + { + --end_leader; + if (*end_leader == '!') + val = !val; + else if (*end_leader == '-') + val = -val; + } + clear_var(retvar); + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = val; + } + + return ret; +} + +/* + * Get an option value. + * "arg" points to the '&' or '+' before the option name. + * "arg" is advanced to character after the option name. + * Return OK or FAIL. + */ + static int +get_option_var(arg, retvar, evaluate) + char_u **arg; + VAR retvar; /* when NULL, only check if option exists */ + int evaluate; +{ + char_u *option_end; + long numval; + char_u *stringval; + int opt_type; + int c; + int working = (**arg == '+'); /* has("+option") */ + int ret = OK; + int opt_flags; + + /* + * Isolate the option name and find its value. + */ + option_end = find_option_end(arg, &opt_flags); + if (option_end == NULL) + { + if (retvar != NULL) + EMSG2(_("E112: Option name missing: %s"), *arg); + return FAIL; + } + + if (!evaluate) + { + *arg = option_end; + return OK; + } + + c = *option_end; + *option_end = NUL; + opt_type = get_option_value(*arg, &numval, + retvar == NULL ? NULL : &stringval, opt_flags); + + if (opt_type == -3) /* invalid name */ + { + if (retvar != NULL) + EMSG2(_("E113: Unknown option: %s"), *arg); + ret = FAIL; + } + else if (retvar != NULL) + { + if (opt_type == -2) /* hidden string option */ + { + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + } + else if (opt_type == -1) /* hidden number option */ + { + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = 0; + } + else if (opt_type == 1) /* number option */ + { + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = numval; + } + else /* string option */ + { + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = stringval; + } + } + else if (working && (opt_type == -2 || opt_type == -1)) + ret = FAIL; + + *option_end = c; /* put back for error messages */ + *arg = option_end; + + return ret; +} + +/* + * Allocate a variable for a string constant. + * Return OK or FAIL. + */ + static int +get_string_var(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + char_u *p; + char_u *name; + int i; + int extra = 0; + + /* + * Find the end of the string, skipping backslashed characters. + */ + for (p = *arg + 1; *p && *p != '"'; ++p) + { + if (*p == '\\' && p[1] != NUL) + { + ++p; + /* A "\<x>" form occupies at least 4 characters, and produces up + * to 6 characters: reserve space for 2 extra */ + if (*p == '<') + extra += 2; + } +#ifdef FEAT_MBYTE + if (has_mbyte) + p += (*mb_ptr2len_check)(p) - 1; +#endif + } + + if (*p != '"') + { + EMSG2(_("E114: Missing quote: %s"), *arg); + return FAIL; + } + + /* If only parsing, set *arg and return here */ + if (!evaluate) + { + *arg = p + 1; + return OK; + } + + /* + * Copy the string into allocated memory, handling backslashed + * characters. + */ + name = alloc((unsigned)(p - *arg + extra)); + if (name == NULL) + return FAIL; + + i = 0; + for (p = *arg + 1; *p && *p != '"'; ++p) + { + if (*p == '\\') + { + switch (*++p) + { + case 'b': name[i++] = BS; break; + case 'e': name[i++] = ESC; break; + case 'f': name[i++] = FF; break; + case 'n': name[i++] = NL; break; + case 'r': name[i++] = CAR; break; + case 't': name[i++] = TAB; break; + + case 'X': /* hex: "\x1", "\x12" */ + case 'x': + case 'u': /* Unicode: "\u0023" */ + case 'U': + if (vim_isxdigit(p[1])) + { + int n, nr; + int c = toupper(*p); + + if (c == 'X') + n = 2; + else + n = 4; + nr = 0; + while (--n >= 0 && vim_isxdigit(p[1])) + { + ++p; + nr = (nr << 4) + hex2nr(*p); + } +#ifdef FEAT_MBYTE + /* For "\u" store the number according to + * 'encoding'. */ + if (c != 'X') + i += (*mb_char2bytes)(nr, name + i); + else +#endif + name[i++] = nr; + } + else + name[i++] = *p; + break; + + /* octal: "\1", "\12", "\123" */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': name[i] = *p - '0'; + if (p[1] >= '0' && p[1] <= '7') + { + ++p; + name[i] = (name[i] << 3) + *p - '0'; + if (p[1] >= '0' && p[1] <= '7') + { + ++p; + name[i] = (name[i] << 3) + *p - '0'; + } + } + ++i; + break; + + /* Special key, e.g.: "\<C-W>" */ + case '<': extra = trans_special(&p, name + i, TRUE); + if (extra != 0) + { + i += extra; + --p; + break; + } + /* FALLTHROUGH */ + + default: name[i++] = *p; + break; + } + } + else + name[i++] = *p; + +#ifdef FEAT_MBYTE + /* For a multi-byte character copy the bytes after the first one. */ + if (has_mbyte) + { + int l = (*mb_ptr2len_check)(p); + + while (--l > 0) + name[i++] = *++p; + } +#endif + } + name[i] = NUL; + *arg = p + 1; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = name; + + return OK; +} + +/* + * Allocate a variable for an backtick-string constant. + * Return OK or FAIL. + */ + static int +get_lit_string_var(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + char_u *p; + char_u *name; + + /* + * Find the end of the string. + */ + p = vim_strchr(*arg + 1, '\''); + if (p == NULL) + { + EMSG2(_("E115: Missing quote: %s"), *arg); + return FAIL; + } + + if (evaluate) + { + /* + * Copy the string into allocated memory. + */ + name = vim_strnsave(*arg + 1, (int)(p - (*arg + 1))); + if (name == NULL) + return FAIL; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = name; + } + + *arg = p + 1; + + return OK; +} + +/* + * Get the value of an environment variable. + * "arg" is pointing to the '$'. It is advanced to after the name. + * If the environment variable was not set, silently assume it is empty. + * Always return OK. + */ + static int +get_env_var(arg, retvar, evaluate) + char_u **arg; + VAR retvar; + int evaluate; +{ + char_u *string = NULL; + int len; + int cc; + char_u *name; + + ++*arg; + name = *arg; + len = get_env_len(arg); + if (evaluate) + { + if (len != 0) + { + cc = name[len]; + name[len] = NUL; + /* first try mch_getenv(), fast for normal environment vars */ + string = mch_getenv(name); + if (string != NULL && *string != NUL) + string = vim_strsave(string); + else + { + /* next try expanding things like $VIM and ${HOME} */ + string = expand_env_save(name - 1); + if (string != NULL && *string == '$') + { + vim_free(string); + string = NULL; + } + } + name[len] = cc; + } + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = string; + } + + 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) __ARGS((VAR args, VAR rvar)); /* impl. function */ +} functions[] = +{ + {"append", 2, 2, f_append}, + {"argc", 0, 0, f_argc}, + {"argidx", 0, 0, f_argidx}, + {"argv", 1, 1, f_argv}, + {"browse", 4, 4, f_browse}, + {"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, 1, f_bufnr}, + {"bufwinnr", 1, 1, f_bufwinnr}, + {"byte2line", 1, 1, f_byte2line}, + {"char2nr", 1, 1, f_char2nr}, + {"cindent", 1, 1, f_cindent}, + {"col", 1, 1, f_col}, + {"confirm", 1, 4, f_confirm}, + {"cscope_connection",0,3, f_cscope_connection}, + {"cursor", 2, 2, f_cursor}, + {"delete", 1, 1, f_delete}, + {"did_filetype", 0, 0, f_did_filetype}, + {"escape", 2, 2, f_escape}, + {"eventhandler", 0, 0, f_eventhandler}, + {"executable", 1, 1, f_executable}, + {"exists", 1, 1, f_exists}, + {"expand", 1, 2, f_expand}, + {"file_readable", 1, 1, f_filereadable}, /* obsolete */ + {"filereadable", 1, 1, f_filereadable}, + {"filewritable", 1, 1, f_filewritable}, + {"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}, + {"foreground", 0, 0, f_foreground}, + {"getbufvar", 2, 2, f_getbufvar}, + {"getchar", 0, 1, f_getchar}, + {"getcharmod", 0, 0, f_getcharmod}, + {"getcmdline", 0, 0, f_getcmdline}, + {"getcmdpos", 0, 0, f_getcmdpos}, + {"getcwd", 0, 0, f_getcwd}, + {"getfsize", 1, 1, f_getfsize}, + {"getftime", 1, 1, f_getftime}, + {"getline", 1, 1, f_getline}, + {"getreg", 0, 1, f_getreg}, + {"getregtype", 0, 1, f_getregtype}, + {"getwinposx", 0, 0, f_getwinposx}, + {"getwinposy", 0, 0, f_getwinposy}, + {"getwinvar", 2, 2, f_getwinvar}, + {"glob", 1, 1, f_glob}, + {"globpath", 2, 2, f_globpath}, + {"has", 1, 1, f_has}, + {"hasmapto", 1, 2, 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}, + {"input", 1, 2, f_input}, + {"inputdialog", 1, 3, f_inputdialog}, + {"inputrestore", 0, 0, f_inputrestore}, + {"inputsave", 0, 0, f_inputsave}, + {"inputsecret", 1, 2, f_inputsecret}, + {"isdirectory", 1, 1, f_isdirectory}, + {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */ + {"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}, + {"maparg", 1, 2, f_maparg}, + {"mapcheck", 1, 2, f_mapcheck}, + {"match", 2, 3, f_match}, + {"matchend", 2, 3, f_matchend}, + {"matchstr", 2, 3, f_matchstr}, + {"mode", 0, 0, f_mode}, + {"nextnonblank", 1, 1, f_nextnonblank}, + {"nr2char", 1, 1, f_nr2char}, + {"prevnonblank", 1, 1, f_prevnonblank}, + {"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}, + {"rename", 2, 2, f_rename}, + {"resolve", 1, 1, f_resolve}, + {"search", 1, 2, f_search}, + {"searchpair", 3, 5, f_searchpair}, + {"server2client", 2, 2, f_server2client}, + {"serverlist", 0, 0, f_serverlist}, + {"setbufvar", 3, 3, f_setbufvar}, + {"setcmdpos", 1, 1, f_setcmdpos}, + {"setline", 2, 2, f_setline}, + {"setreg", 2, 3, f_setreg}, + {"setwinvar", 3, 3, f_setwinvar}, + {"simplify", 1, 1, f_simplify}, +#ifdef HAVE_STRFTIME + {"strftime", 1, 2, f_strftime}, +#endif + {"stridx", 2, 2, f_stridx}, + {"strlen", 1, 1, f_strlen}, + {"strpart", 2, 3, f_strpart}, + {"strridx", 2, 2, f_strridx}, + {"strtrans", 1, 1, f_strtrans}, + {"submatch", 1, 1, f_submatch}, + {"substitute", 4, 4, f_substitute}, + {"synID", 3, 3, f_synID}, + {"synIDattr", 2, 3, f_synIDattr}, + {"synIDtrans", 1, 1, f_synIDtrans}, + {"system", 1, 1, f_system}, + {"tempname", 0, 0, f_tempname}, + {"tolower", 1, 1, f_tolower}, + {"toupper", 1, 1, f_toupper}, + {"type", 1, 1, f_type}, + {"virtcol", 1, 1, f_virtcol}, + {"visualmode", 0, 1, f_visualmode}, + {"winbufnr", 1, 1, f_winbufnr}, + {"wincol", 0, 0, f_wincol}, + {"winheight", 1, 1, f_winheight}, + {"winline", 0, 0, f_winline}, + {"winnr", 0, 0, f_winnr}, + {"winrestcmd", 0, 0, f_winrestcmd}, + {"winwidth", 1, 1, f_winwidth}, +}; + +#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(xp, idx) + 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. + */ +/*ARGSUSED*/ + char_u * +get_expr_name(xp, idx) + 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 */ + +/* + * Find internal function in table above. + * Return index, or -1 if not found + */ + static int +find_internal_func(name) + 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; +} + +/* + * Allocate a variable for the result of a function. + * Return OK or FAIL. + */ + static int +get_func_var(name, len, retvar, arg, firstline, lastline, doesrange, evaluate) + char_u *name; /* name of the function */ + int len; /* length of "name" */ + VAR retvar; + char_u **arg; /* argument, pointing to the '(' */ + linenr_T firstline; /* first line of range */ + linenr_T lastline; /* last line of range */ + int *doesrange; /* return: function handled range */ + int evaluate; +{ + char_u *argp; + int ret = OK; +#define MAX_FUNC_ARGS 20 + var argvars[MAX_FUNC_ARGS]; /* vars for arguments */ + int argcount = 0; /* number of arguments found */ + + /* + * Get the arguments. + */ + argp = *arg; + while (argcount < MAX_FUNC_ARGS) + { + argp = skipwhite(argp + 1); /* skip the '(' or ',' */ + if (*argp == ')' || *argp == ',' || *argp == NUL) + break; + argvars[argcount].var_name = NULL; /* the name is not stored */ + if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) + { + ret = FAIL; + break; + } + ++argcount; + if (*argp != ',') + break; + } + if (*argp == ')') + ++argp; + else + ret = FAIL; + + if (ret == OK) + ret = call_func(name, len, retvar, argcount, argvars, + firstline, lastline, doesrange, evaluate); + else if (!aborting()) + EMSG2(_("E116: Invalid arguments for function %s"), name); + + while (--argcount >= 0) + clear_var(&argvars[argcount]); + + *arg = skipwhite(argp); + return ret; +} + + +/* + * Call a function with its resolved parameters + * Return OK or FAIL. + */ + static int +call_func(name, len, retvar, argcount, argvars, firstline, lastline, + doesrange, evaluate) + char_u *name; /* name of the function */ + int len; /* length of "name" */ + VAR retvar; /* return value goes here */ + int argcount; /* number of "argvars" */ + VAR argvars; /* vars for arguments */ + linenr_T firstline; /* first line of range */ + linenr_T lastline; /* last line of range */ + int *doesrange; /* return: function handled range */ + int evaluate; +{ + int ret = FAIL; + static char *errors[] = + {N_("E117: Unknown function: %s"), + N_("E118: Too many arguments for function: %s"), + N_("E119: Not enough arguments for function: %s"), + N_("E120: Using <SID> not in a script context: %s"), + }; +#define ERROR_UNKNOWN 0 +#define ERROR_TOOMANY 1 +#define ERROR_TOOFEW 2 +#define ERROR_SCRIPT 3 +#define ERROR_NONE 4 +#define ERROR_OTHER 5 + int error = ERROR_NONE; + int i; + int llen; + ufunc_T *fp; + int cc; +#define FLEN_FIXED 40 + char_u fname_buf[FLEN_FIXED + 1]; + char_u *fname; + + /* + * In a script change <SID>name() and s:name() to K_SNR 123_name(). + * Change <SNR>123_name() to K_SNR 123_name(). + * Use fname_buf[] when it fits, otherwise allocate memory (slow). + */ + cc = name[len]; + name[len] = NUL; + llen = eval_fname_script(name); + if (llen > 0) + { + fname_buf[0] = K_SPECIAL; + fname_buf[1] = KS_EXTRA; + fname_buf[2] = (int)KE_SNR; + i = 3; + if (eval_fname_sid(name)) /* "<SID>" or "s:" */ + { + if (current_SID <= 0) + error = ERROR_SCRIPT; + else + { + sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID); + i = (int)STRLEN(fname_buf); + } + } + if (i + STRLEN(name + llen) < FLEN_FIXED) + { + STRCPY(fname_buf + i, name + llen); + fname = fname_buf; + } + else + { + fname = alloc((unsigned)(i + STRLEN(name + llen) + 1)); + if (fname == NULL) + error = ERROR_OTHER; + else + { + mch_memmove(fname, fname_buf, (size_t)i); + STRCPY(fname + i, name + llen); + } + } + } + else + fname = name; + + *doesrange = FALSE; + + + /* execute the function if no errors detected and executing */ + if (evaluate && error == ERROR_NONE) + { + retvar->var_type = VAR_NUMBER; /* default is number retvar */ + error = ERROR_UNKNOWN; + + if (!ASCII_ISLOWER(fname[0])) + { + /* + * User defined function. + */ + fp = find_func(fname); +#ifdef FEAT_AUTOCMD + if (fp == NULL && apply_autocmds(EVENT_FUNCUNDEFINED, + fname, fname, TRUE, NULL) +#ifdef FEAT_EVAL + && !aborting() +#endif + ) + { + /* executed an autocommand, search for function again */ + fp = find_func(fname); + } +#endif + if (fp != NULL) + { + if (fp->flags & FC_RANGE) + *doesrange = TRUE; + if (argcount < fp->args.ga_len) + error = ERROR_TOOFEW; + else if (!fp->varargs && argcount > fp->args.ga_len) + error = ERROR_TOOMANY; + else + { + /* + * Call the user function. + * Save and restore search patterns, script variables and + * redo buffer. + */ + save_search_patterns(); + saveRedobuff(); + ++fp->calls; + call_user_func(fp, argcount, argvars, retvar, + firstline, lastline); + --fp->calls; + restoreRedobuff(); + restore_search_patterns(); + error = ERROR_NONE; + } + } + } + else + { + /* + * Find the function name in the table, call its implementation. + */ + i = find_internal_func(fname); + if (i >= 0) + { + if (argcount < functions[i].f_min_argc) + error = ERROR_TOOFEW; + else if (argcount > functions[i].f_max_argc) + error = ERROR_TOOMANY; + else + { + argvars[argcount].var_type = VAR_UNKNOWN; + functions[i].f_func(argvars, retvar); + error = ERROR_NONE; + } + } + } + /* + * The function call (or "FuncUndefined" autocommand sequence) might + * have been aborted by an error, an interrupt, or an explicitly thrown + * exception that has not been caught so far. This situation can be + * tested for by calling aborting(). For an error in an internal + * function or for the "E132" error in call_user_func(), however, the + * throw point at which the "force_abort" flag (temporarily reset by + * emsg()) is normally updated has not been reached yet. We need to + * update that flag first to make aborting() reliable. + */ + update_force_abort(); + } + if (error == ERROR_NONE) + ret = OK; + + /* + * Report an error unless the argument evaluation or function call has been + * cancelled due to an aborting error, an interrupt, or an exception. + */ + if (error < ERROR_NONE && !aborting()) + EMSG2((char_u *)_(errors[error]), name); + + name[len] = cc; + if (fname != name && fname != fname_buf) + vim_free(fname); + + return ret; +} + +/********************************************* + * Implementation of the built-in functions + */ + +/* + * "append(lnum, string)" function + */ + static void +f_append(argvars, retvar) + VAR argvars; + VAR retvar; +{ + long lnum; + + lnum = get_var_lnum(argvars); + retvar->var_val.var_number = 1; /* Default: Failed */ + + if (lnum >= 0 + && lnum <= curbuf->b_ml.ml_line_count + && u_save(lnum, lnum + 1) == OK) + { + ml_append(lnum, get_var_string(&argvars[1]), (colnr_T)0, FALSE); + if (curwin->w_cursor.lnum > lnum) + ++curwin->w_cursor.lnum; + appended_lines_mark(lnum, 1L); + retvar->var_val.var_number = 0; + } +} + +/* + * "argc()" function + */ +/* ARGSUSED */ + static void +f_argc(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = ARGCOUNT; +} + +/* + * "argidx()" function + */ +/* ARGSUSED */ + static void +f_argidx(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = curwin->w_arg_idx; +} + +/* + * "argv(nr)" function + */ + static void +f_argv(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int idx; + + idx = get_var_number(&argvars[0]); + if (idx >= 0 && idx < ARGCOUNT) + retvar->var_val.var_string = vim_strsave(alist_name(&ARGLIST[idx])); + else + retvar->var_val.var_string = NULL; + retvar->var_type = VAR_STRING; +} + +/* + * "browse(save, title, initdir, default)" function + */ +/* ARGSUSED */ + static void +f_browse(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_BROWSE + int save; + char_u *title; + char_u *initdir; + char_u *defname; + char_u buf[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + + save = get_var_number(&argvars[0]); + title = get_var_string(&argvars[1]); + initdir = get_var_string_buf(&argvars[2], buf); + defname = get_var_string_buf(&argvars[3], buf2); + + retvar->var_val.var_string = + do_browse(save, title, defname, NULL, initdir, NULL, curbuf); +#else + retvar->var_val.var_string = NULL; +#endif + retvar->var_type = VAR_STRING; +} + +/* + * Find a buffer by number or exact name. + */ + static buf_T * +find_buffer(avar) + VAR avar; +{ + buf_T *buf = NULL; + char_u *name; + + if (avar->var_type == VAR_NUMBER) + buf = buflist_findnr((int)avar->var_val.var_number); + else if (avar->var_val.var_string != NULL) + { + /* First make the name into a full path name */ + name = FullName_save(avar->var_val.var_string, +#ifdef UNIX + TRUE /* force expansion, get rid of symbolic links */ +#else + FALSE +#endif + ); + if (name != NULL) + { + buf = buflist_findname(name); + vim_free(name); + } + } + return buf; +} + +/* + * "bufexists(expr)" function + */ + static void +f_bufexists(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = (find_buffer(&argvars[0]) != NULL); +} + +/* + * "buflisted(expr)" function + */ + static void +f_buflisted(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + retvar->var_val.var_number = (buf != NULL && buf->b_p_bl); +} + +/* + * "bufloaded(expr)" function + */ + static void +f_bufloaded(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + retvar->var_val.var_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); +} + +/* + * Get buffer by number or pattern. + */ + static buf_T * +get_buf_var(avar) + VAR avar; +{ + char_u *name = avar->var_val.var_string; + int save_magic; + char_u *save_cpo; + buf_T *buf; + + if (avar->var_type == VAR_NUMBER) + return buflist_findnr((int)avar->var_val.var_number); + if (name == NULL || *name == NUL) + return curbuf; + if (name[0] == '$' && name[1] == NUL) + return lastbuf; + + /* 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)); + + p_magic = save_magic; + p_cpo = save_cpo; + + /* If not found, try expanding the name, like done for bufexists(). */ + if (buf == NULL) + buf = find_buffer(avar); + + return buf; +} + +/* + * "bufname(expr)" function + */ + static void +f_bufname(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; + + ++emsg_off; + buf = get_buf_var(&argvars[0]); + retvar->var_type = VAR_STRING; + if (buf != NULL && buf->b_fname != NULL) + retvar->var_val.var_string = vim_strsave(buf->b_fname); + else + retvar->var_val.var_string = NULL; + --emsg_off; +} + +/* + * "bufnr(expr)" function + */ + static void +f_bufnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; + + ++emsg_off; + buf = get_buf_var(&argvars[0]); + if (buf != NULL) + retvar->var_val.var_number = buf->b_fnum; + else + retvar->var_val.var_number = -1; + --emsg_off; +} + +/* + * "bufwinnr(nr)" function + */ + static void +f_bufwinnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_WINDOWS + win_T *wp; + int winnr = 0; +#endif + buf_T *buf; + + ++emsg_off; + buf = get_buf_var(&argvars[0]); +#ifdef FEAT_WINDOWS + for (wp = firstwin; wp; wp = wp->w_next) + { + ++winnr; + if (wp->w_buffer == buf) + break; + } + retvar->var_val.var_number = (wp != NULL ? winnr : -1); +#else + retvar->var_val.var_number = (curwin->w_buffer == buf ? 1 : -1); +#endif + --emsg_off; +} + +/* + * "byte2line(byte)" function + */ +/*ARGSUSED*/ + static void +f_byte2line(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifndef FEAT_BYTEOFF + retvar->var_val.var_number = -1; +#else + long boff = 0; + + boff = get_var_number(&argvars[0]) - 1; + if (boff < 0) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = ml_find_line_or_offset(curbuf, + (linenr_T)0, &boff); +#endif +} + +/* + * "char2nr(string)" function + */ + static void +f_char2nr(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_MBYTE + if (has_mbyte) + retvar->var_val.var_number = + (*mb_ptr2char)(get_var_string(&argvars[0])); + else +#endif + retvar->var_val.var_number = get_var_string(&argvars[0])[0]; +} + +/* + * "cindent(lnum)" function + */ + static void +f_cindent(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CINDENT + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = get_var_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + retvar->var_val.var_number = get_c_indent(); + curwin->w_cursor = pos; + } + else +#endif + retvar->var_val.var_number = -1; +} + +/* + * "col(string)" function + */ + static void +f_col(argvars, retvar) + VAR argvars; + VAR retvar; +{ + colnr_T col = 0; + pos_T *fp; + + fp = var2fpos(&argvars[0], FALSE); + if (fp != NULL) + { + if (fp->col == MAXCOL) + { + /* '> can be MAXCOL, get the length of the line then */ + if (fp->lnum <= curbuf->b_ml.ml_line_count) + col = 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_check)(p))] == NUL) + col += l; +# else + if (*p != NUL && p[1] == NUL) + ++col; +# endif + } + } +#endif + } + } + retvar->var_val.var_number = col; +} + +/* + * "confirm(message, buttons[, default [, type]])" function + */ +/*ARGSUSED*/ + static void +f_confirm(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#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; + int c; + + message = get_var_string(&argvars[0]); + if (argvars[1].var_type != VAR_UNKNOWN) + { + buttons = get_var_string_buf(&argvars[1], buf); + if (argvars[2].var_type != VAR_UNKNOWN) + { + def = get_var_number(&argvars[2]); + if (argvars[3].var_type != VAR_UNKNOWN) + { + /* avoid that TOUPPER_ASC calls get_var_string_buf() twice */ + c = *get_var_string_buf(&argvars[3], buf2); + switch (TOUPPER_ASC(c)) + { + 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"); + + retvar->var_val.var_number = do_dialog(type, NULL, message, buttons, + def, NULL); +#else + retvar->var_val.var_number = 0; +#endif +} + + +/* + * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function + * + * Checks the existence of a cscope connection. + */ +/*ARGSUSED*/ + static void +f_cscope_connection(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CSCOPE + int num = 0; + char_u *dbpath = NULL; + char_u *prepend = NULL; + char_u buf[NUMBUFLEN]; + + if (argvars[0].var_type != VAR_UNKNOWN + && argvars[1].var_type != VAR_UNKNOWN) + { + num = (int)get_var_number(&argvars[0]); + dbpath = get_var_string(&argvars[1]); + if (argvars[2].var_type != VAR_UNKNOWN) + prepend = get_var_string_buf(&argvars[2], buf); + } + + retvar->var_val.var_number = cs_connection(num, dbpath, prepend); +#else + retvar->var_val.var_number = 0; +#endif +} + +/* + * "cursor(lnum, col)" function + * + * Moves the cursor to the specified line and column + */ +/*ARGSUSED*/ + static void +f_cursor(argvars, retvar) + VAR argvars; + VAR retvar; +{ + long line, col; + + line = get_var_lnum(argvars); + if (line > 0) + curwin->w_cursor.lnum = line; + col = get_var_number(&argvars[1]); + if (col > 0) + curwin->w_cursor.col = col - 1; +#ifdef FEAT_VIRTUALEDIT + curwin->w_cursor.coladd = 0; +#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 +} + +/* + * "libcall()" function + */ + static void +f_libcall(argvars, retvar) + VAR argvars; + VAR retvar; +{ + libcall_common(argvars, retvar, VAR_STRING); +} + +/* + * "libcallnr()" function + */ + static void +f_libcallnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + libcall_common(argvars, retvar, VAR_NUMBER); +} + + static void +libcall_common(argvars, retvar, type) + VAR argvars; + VAR retvar; + int type; +{ +#ifdef FEAT_LIBCALL + char_u *string_in; + char_u **string_result; + int nr_result; +#endif + + retvar->var_type = type; + if (type == VAR_NUMBER) + retvar->var_val.var_number = 0; + else + retvar->var_val.var_string = NULL; + + if (check_restricted() || check_secure()) + return; + +#ifdef FEAT_LIBCALL + /* The first two args must be strings, otherwise its meaningless */ + if (argvars[0].var_type == VAR_STRING && argvars[1].var_type == VAR_STRING) + { + if (argvars[2].var_type == VAR_NUMBER) + string_in = NULL; + else + string_in = argvars[2].var_val.var_string; + if (type == VAR_NUMBER) + string_result = NULL; + else + string_result = &retvar->var_val.var_string; + if (mch_libcall(argvars[0].var_val.var_string, + argvars[1].var_val.var_string, + string_in, + argvars[2].var_val.var_number, + string_result, + &nr_result) == OK + && type == VAR_NUMBER) + retvar->var_val.var_number = nr_result; + } +#endif +} + +/* + * "delete()" function + */ + static void +f_delete(argvars, retvar) + VAR argvars; + VAR retvar; +{ + if (check_restricted() || check_secure()) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = mch_remove(get_var_string(&argvars[0])); +} + +/* + * "did_filetype()" function + */ +/*ARGSUSED*/ + static void +f_did_filetype(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_AUTOCMD + retvar->var_val.var_number = did_filetype; +#else + retvar->var_val.var_number = 0; +#endif +} + +/* + * "escape({string}, {chars})" function + */ + static void +f_escape(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[NUMBUFLEN]; + + retvar->var_val.var_string = + vim_strsave_escaped(get_var_string(&argvars[0]), + get_var_string_buf(&argvars[1], buf)); + retvar->var_type = VAR_STRING; +} + +/* + * "eventhandler()" function + */ +/*ARGSUSED*/ + static void +f_eventhandler(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = vgetc_busy; +} + +/* + * "executable()" function + */ + static void +f_executable(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = mch_can_exe(get_var_string(&argvars[0])); +} + +/* + * "exists()" function + */ + static void +f_exists(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + char_u *name; + int n = FALSE; + int len = 0; + + p = get_var_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_var(&p, NULL, TRUE) == OK); + else if (*p == '*') /* internal or user defined function */ + { + ++p; + p = trans_function_name(&p, FALSE, TRUE); + if (p != NULL) + { + if (ASCII_ISUPPER(*p) || p[0] == K_SPECIAL) + n = (find_func(p) != NULL); + else if (ASCII_ISLOWER(*p)) + n = (find_internal_func(p) >= 0); + vim_free(p); + } + } + else if (*p == ':') + { + n = cmd_exists(p + 1); + } + else if (*p == '#') + { +#ifdef FEAT_AUTOCMD + name = p + 1; + p = vim_strchr(name, '#'); + if (p != NULL) + n = au_exists(name, p, p + 1); + else + n = au_exists(name, name + STRLEN(name), NULL); +#endif + } + else /* internal variable */ + { +#ifdef FEAT_MAGIC_BRACES + char_u *expr_start; + char_u *expr_end; + char_u *temp_string = NULL; + char_u *s; +#endif + name = p; + +#ifdef FEAT_MAGIC_BRACES + /* Find the end of the name. */ + s = find_name_end(name, &expr_start, &expr_end); + if (expr_start != NULL) + { + temp_string = make_expanded_name(name, expr_start, expr_end, s); + if (temp_string != NULL) + { + len = STRLEN(temp_string); + name = temp_string; + } + } +#endif + if (len == 0) + len = get_id_len(&p); + if (len != 0) + n = (get_var_var(name, len, NULL) == OK); + +#ifdef FEAT_MAGIC_BRACES + vim_free(temp_string); +#endif + } + + retvar->var_val.var_number = n; +} + +/* + * "expand()" function + */ + static void +f_expand(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *s; + int len; + char_u *errormsg; + int flags = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; + expand_T xpc; + + retvar->var_type = VAR_STRING; + s = get_var_string(&argvars[0]); + if (*s == '%' || *s == '#' || *s == '<') + { + ++emsg_off; + retvar->var_val.var_string = eval_vars(s, &len, NULL, &errormsg, s); + --emsg_off; + } + else + { + /* When the optional second argument is non-zero, don't remove matches + * for 'suffixes' and 'wildignore' */ + if (argvars[1].var_type != VAR_UNKNOWN && get_var_number(&argvars[1])) + flags |= WILD_KEEP_ALL; + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + retvar->var_val.var_string = ExpandOne(&xpc, s, NULL, flags, WILD_ALL); + ExpandCleanup(&xpc); + } +} + +/* + * "filereadable()" function + */ + static void +f_filereadable(argvars, retvar) + VAR argvars; + VAR retvar; +{ + FILE *fd; + char_u *p; + int n; + + p = get_var_string(&argvars[0]); + if (*p && !mch_isdir(p) && (fd = mch_fopen((char *)p, "r")) != NULL) + { + n = TRUE; + fclose(fd); + } + else + n = FALSE; + + retvar->var_val.var_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(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + int retval = 0; +#if defined(UNIX) || defined(VMS) + int perm = 0; +#endif + + p = get_var_string(&argvars[0]); +#if defined(UNIX) || defined(VMS) + perm = mch_getperm(p); +#endif +#ifndef MACOS_CLASSIC /* TODO: get either mch_writable or mch_access */ + if ( +# ifdef WIN3264 + mch_writable(p) && +# else +# if defined(UNIX) || defined(VMS) + (perm & 0222) && +# endif +# endif + mch_access((char *)p, W_OK) == 0 + ) +#endif + { + ++retval; + if (mch_isdir(p)) + ++retval; + } + retvar->var_val.var_number = retval; +} + +/* + * "fnamemodify({fname}, {mods})" function + */ + static void +f_fnamemodify(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *fname; + char_u *mods; + int usedlen = 0; + int len; + char_u *fbuf = NULL; + char_u buf[NUMBUFLEN]; + + fname = get_var_string(&argvars[0]); + mods = get_var_string_buf(&argvars[1], buf); + len = (int)STRLEN(fname); + + (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + + retvar->var_type = VAR_STRING; + if (fname == NULL) + retvar->var_val.var_string = NULL; + else + retvar->var_val.var_string = vim_strnsave(fname, len); + vim_free(fbuf); +} + +/* + * "foldclosed()" function + */ + static void +f_foldclosed(argvars, retvar) + VAR argvars; + VAR retvar; +{ + foldclosed_both(argvars, retvar, FALSE); +} + +/* + * "foldclosedend()" function + */ + static void +f_foldclosedend(argvars, retvar) + VAR argvars; + VAR retvar; +{ + foldclosed_both(argvars, retvar, TRUE); +} + +/* + * "foldclosed()" function + */ + static void +foldclosed_both(argvars, retvar, end) + VAR argvars; + VAR retvar; + int end; +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + linenr_T first, last; + + lnum = get_var_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) + { + if (end) + retvar->var_val.var_number = (varnumber_T)last; + else + retvar->var_val.var_number = (varnumber_T)first; + return; + } + } +#endif + retvar->var_val.var_number = -1; +} + +/* + * "foldlevel()" function + */ + static void +f_foldlevel(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + + lnum = get_var_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + retvar->var_val.var_number = foldLevel(lnum); + else +#endif + retvar->var_val.var_number = 0; +} + +/* + * "foldtext()" function + */ +/*ARGSUSED*/ + static void +f_foldtext(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + char_u *s; + char_u *r; + int len; + char *txt; +#endif + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; +#ifdef FEAT_FOLDING + if ((linenr_T)vimvars[VV_FOLDSTART].val > 0 + && (linenr_T)vimvars[VV_FOLDEND].val <= curbuf->b_ml.ml_line_count + && vimvars[VV_FOLDDASHES].val != NULL) + { + /* Find first non-empty line in the fold. */ + lnum = (linenr_T)vimvars[VV_FOLDSTART].val; + while (lnum < (linenr_T)vimvars[VV_FOLDEND].val) + { + 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); + txt = _("+-%s%3ld lines: "); + r = alloc((unsigned)(STRLEN(txt) + + STRLEN(vimvars[VV_FOLDDASHES].val) /* for %s */ + + 20 /* for %3ld */ + + STRLEN(s))); /* concatenated */ + if (r != NULL) + { + sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].val, + (long)((linenr_T)vimvars[VV_FOLDEND].val + - (linenr_T)vimvars[VV_FOLDSTART].val + 1)); + len = (int)STRLEN(r); + STRCAT(r, s); + /* remove 'foldmarker' and 'commentstring' */ + foldtext_cleanup(r + len); + retvar->var_val.var_string = r; + } + } +#endif +} + +/* + * "foreground()" function + */ +/*ARGSUSED*/ + static void +f_foreground(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = 0; +#ifdef FEAT_GUI + if (gui.in_use) + gui_mch_set_foreground(); +#else +# ifdef WIN32 + win32_set_foreground(); +# endif +#endif +} + +/* + * "getchar()" function + */ + static void +f_getchar(argvars, retvar) + VAR argvars; + VAR retvar; +{ + varnumber_T n; + + ++no_mapping; + ++allow_keys; + if (argvars[0].var_type == VAR_UNKNOWN) + /* getchar(): blocking wait. */ + n = safe_vgetc(); + else if (get_var_number(&argvars[0]) == 1) + /* getchar(1): only check if char avail */ + n = vpeekc(); + else if (vpeekc() == NUL) + /* getchar(0) and no char avail: return zero */ + n = 0; + else + /* getchar(0) and char avail: return char */ + n = safe_vgetc(); + --no_mapping; + --allow_keys; + + retvar->var_val.var_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; + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strsave(temp); + } +} + +/* + * "getcharmod()" function + */ +/*ARGSUSED*/ + static void +f_getcharmod(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = mod_mask; +} + +/* + * "getcmdline()" function + */ +/*ARGSUSED*/ + static void +f_getcmdline(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = get_cmdline_str(); +} + +/* + * "getcmdpos()" function + */ +/*ARGSUSED*/ + static void +f_getcmdpos(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = get_cmdline_pos() + 1; +} + +/* + * "getbufvar()" function + */ + static void +f_getbufvar(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; + buf_T *save_curbuf; + char_u *varname; + VAR v; + + ++emsg_off; + buf = get_buf_var(&argvars[0]); + varname = get_var_string(&argvars[1]); + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + + if (buf != NULL && varname != NULL) + { + if (*varname == '&') /* buffer-local-option */ + { + /* set curbuf to be our buf, temporarily */ + save_curbuf = curbuf; + curbuf = buf; + + get_option_var(&varname, retvar, TRUE); + + /* restore previous notion of curbuf */ + curbuf = save_curbuf; + } + else + { + /* look up the variable */ + v = find_var_in_ga(&buf->b_vars, varname); + if (v != NULL) + copy_var(v, retvar); + } + } + + --emsg_off; +} + +/* + * "getcwd()" function + */ +/*ARGSUSED*/ + static void +f_getcwd(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u cwd[MAXPATHL]; + + retvar->var_type = VAR_STRING; + if (mch_dirname(cwd, MAXPATHL) == FAIL) + retvar->var_val.var_string = NULL; + else + { + retvar->var_val.var_string = vim_strsave(cwd); +#ifdef BACKSLASH_IN_FILENAME + slash_adjust(retvar->var_val.var_string); +#endif + } +} + +/* + * "getfsize({fname})" function + */ + static void +f_getfsize(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *fname; + struct stat st; + + fname = get_var_string(&argvars[0]); + + retvar->var_type = VAR_NUMBER; + + if (mch_stat((char *)fname, &st) >= 0) + { + if (mch_isdir(fname)) + retvar->var_val.var_number = 0; + else + retvar->var_val.var_number = (varnumber_T)st.st_size; + } + else + retvar->var_val.var_number = -1; +} + +/* + * "getftime({fname})" function + */ + static void +f_getftime(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *fname; + struct stat st; + + fname = get_var_string(&argvars[0]); + + if (mch_stat((char *)fname, &st) >= 0) + retvar->var_val.var_number = (varnumber_T)st.st_mtime; + else + retvar->var_val.var_number = -1; +} + +/* + * "getreg()" function + */ + static void +f_getreg(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *strregname; + int regname; + + if (argvars[0].var_type != VAR_UNKNOWN) + strregname = get_var_string(&argvars[0]); + else + strregname = vimvars[VV_REG].val; + regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0) + regname = '"'; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = get_reg_contents(regname, TRUE); +} + +/* + * "getregtype()" function + */ + static void +f_getregtype(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *strregname; + int regname; + char_u buf[NUMBUFLEN + 2]; + long reglen = 0; + + if (argvars[0].var_type != VAR_UNKNOWN) + strregname = get_var_string(&argvars[0]); + else + /* Default to v:register */ + strregname = vimvars[VV_REG].val; + + 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; +#ifdef FEAT_VISUAL + case MBLOCK: + buf[0] = Ctrl_V; + sprintf((char *)buf + 1, "%ld", reglen + 1); + break; +#endif + } + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strsave(buf); +} + +/* + * "getline(lnum)" function + */ + static void +f_getline(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum; + char_u *p; + + lnum = get_var_lnum(argvars); + + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + p = ml_get(lnum); + else + p = (char_u *)""; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strsave(p); +} + +/* + * "getwinposx()" function + */ +/*ARGSUSED*/ + static void +f_getwinposx(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + retvar->var_val.var_number = x; + } +#endif +} + +/* + * "getwinposy()" function + */ +/*ARGSUSED*/ + static void +f_getwinposy(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + retvar->var_val.var_number = y; + } +#endif +} + +/* + * "getwinvar()" function + */ + static void +f_getwinvar(argvars, retvar) + VAR argvars; + VAR retvar; +{ + win_T *win, *oldcurwin; + char_u *varname; + VAR v; + + ++emsg_off; + win = find_win_by_nr(&argvars[0]); + varname = get_var_string(&argvars[1]); + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + + if (win != NULL && varname != NULL) + { + if (*varname == '&') /* window-local-option */ + { + /* set curwin to be our win, temporarily */ + oldcurwin = curwin; + curwin = win; + + get_option_var(&varname, retvar , 1); + + /* restore previous notion of curwin */ + curwin = oldcurwin; + } + else + { + /* look up the variable */ + v = find_var_in_ga(&win->w_vars, varname); + if (v != NULL) + copy_var(v, retvar); + } + } + + --emsg_off; +} + +/* + * "glob()" function + */ + static void +f_glob(argvars, retvar) + VAR argvars; + VAR retvar; +{ + expand_T xpc; + + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = ExpandOne(&xpc, get_var_string(&argvars[0]), + NULL, WILD_USE_NL|WILD_SILENT, WILD_ALL); + ExpandCleanup(&xpc); +} + +/* + * "globpath()" function + */ + static void +f_globpath(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf1[NUMBUFLEN]; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = globpath(get_var_string(&argvars[0]), + get_var_string_buf(&argvars[1], buf1)); +} + +/* + * "has()" function + */ + static void +f_has(argvars, retvar) + VAR argvars; + VAR retvar; +{ + 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 MSDOS +# ifdef DJGPP + "dos32", +# else + "dos16", +# endif +#endif +#ifdef MACOS /* TODO: Should we add MACOS_CLASSIC, MACOS_X? (Dany) */ + "mac", +#endif +#if defined(MACOS_X_UNIX) + "macunix", +#endif +#ifdef OS2 + "os2", +#endif +#ifdef __QNX__ + "qnx", +#endif +#ifdef RISCOS + "riscos", +#endif +#ifdef UNIX + "unix", +#endif +#ifdef VMS + "vms", +#endif +#ifdef WIN16 + "win16", +#endif +#ifdef WIN32 + "win32", +#endif +#if defined(UNIX) && (defined(__CYGWIN32__) || defined(__CYGWIN__)) + "win32unix", +#endif +#ifdef WIN64 + "win64", +#endif +#ifdef EBCDIC + "ebcdic", +#endif +#ifndef CASE_INSENSITIVE_FILENAME + "fname_case", +#endif +#ifdef FEAT_ARABIC + "arabic", +#endif +#ifdef FEAT_AUTOCMD + "autocmd", +#endif +#ifdef FEAT_BEVAL + "balloon_eval", +#endif +#if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS) + "builtin_terms", +# ifdef ALL_BUILTIN_TCAPS + "all_builtin_terms", +# endif +#endif +#ifdef FEAT_BYTEOFF + "byte_offset", +#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_CRYPT + "cryptv", +#endif +#ifdef FEAT_CSCOPE + "cscope", +#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_DND + "dnd", +#endif +#ifdef FEAT_EMACS_TAGS + "emacs_tags", +#endif + "eval", /* always present, of course! */ +#ifdef FEAT_EX_EXTRA + "ex_extra", +#endif +#ifdef FEAT_SEARCH_EXTRA + "extra_search", +#endif +#ifdef FEAT_FKMAP + "farsi", +#endif +#ifdef FEAT_SEARCHPATH + "file_in_path", +#endif +#ifdef FEAT_FIND_ID + "find_in_path", +#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_BEOS + "gui_beos", +#endif +#ifdef FEAT_GUI_GTK + "gui_gtk", +# ifdef HAVE_GTK2 + "gui_gtk2", +# endif +#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_W16 + "gui_win16", +#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_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_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_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_OLE + "ole", +#endif +#ifdef FEAT_OSFILETYPE + "osfiletype", +#endif +#ifdef FEAT_PATH_EXTRA + "path_extra", +#endif +#ifdef FEAT_PERL +#ifndef DYNAMIC_PERL + "perl", +#endif +#endif +#ifdef FEAT_PYTHON +#ifndef DYNAMIC_PYTHON + "python", +#endif +#endif +#ifdef FEAT_POSTSCRIPT + "postscript", +#endif +#ifdef FEAT_PRINTER + "printer", +#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 FEAT_SNIFF + "sniff", +#endif +#ifdef FEAT_STL_OPT + "statusline", +#endif +#ifdef FEAT_SUN_WORKSHOP + "sun_workshop", +#endif +#ifdef FEAT_NETBEANS_INTG + "netbeans_intg", +#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 TERMINFO + "terminfo", +#endif +#ifdef FEAT_TERMRESPONSE + "termresponse", +#endif +#ifdef FEAT_TEXTOBJ + "textobjects", +#endif +#ifdef HAVE_TGETENT + "tgetent", +#endif +#ifdef FEAT_TITLE + "title", +#endif +#ifdef FEAT_TOOLBAR + "toolbar", +#endif +#ifdef FEAT_USR_CMDS + "user-commands", /* was accidentally included in 5.4 */ + "user_commands", +#endif +#ifdef FEAT_VIMINFO + "viminfo", +#endif +#ifdef FEAT_VERTSPLIT + "vertsplit", +#endif +#ifdef FEAT_VIRTUALEDIT + "virtualedit", +#endif +#ifdef FEAT_VISUAL + "visual", +#endif +#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 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_var_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) + n = has_patch(atoi((char *)name + 5)); + else if (STRICMP(name, "vim_starting") == 0) + n = (starting != 0); +#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_RUBY + else if (STRICMP(name, "ruby") == 0) + n = ruby_enabled(FALSE); +#endif +#ifdef DYNAMIC_PYTHON + else if (STRICMP(name, "python") == 0) + n = python_enabled(FALSE); +#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(curbuf); +#endif +#if defined(WIN3264) + else if (STRICMP(name, "win95") == 0) + n = mch_windows95(); +#endif + } + + retvar->var_val.var_number = n; +} + +/* + * "hasmapto()" function + */ + static void +f_hasmapto(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *name; + char_u *mode; + char_u buf[NUMBUFLEN]; + + name = get_var_string(&argvars[0]); + if (argvars[1].var_type == VAR_UNKNOWN) + mode = (char_u *)"nvo"; + else + mode = get_var_string_buf(&argvars[1], buf); + + if (map_to_exists(name, mode)) + retvar->var_val.var_number = TRUE; + else + retvar->var_val.var_number = FALSE; +} + +/* + * "histadd()" function + */ +/*ARGSUSED*/ + static void +f_histadd(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CMDHIST + int histype; + char_u *str; + char_u buf[NUMBUFLEN]; +#endif + + retvar->var_val.var_number = FALSE; + if (check_restricted() || check_secure()) + return; +#ifdef FEAT_CMDHIST + histype = get_histtype(get_var_string(&argvars[0])); + if (histype >= 0) + { + str = get_var_string_buf(&argvars[1], buf); + if (*str != NUL) + { + add_to_history(histype, str, FALSE, NUL); + retvar->var_val.var_number = TRUE; + return; + } + } +#endif +} + +/* + * "histdel()" function + */ +/*ARGSUSED*/ + static void +f_histdel(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CMDHIST + int n; + char_u buf[NUMBUFLEN]; + + if (argvars[1].var_type == VAR_UNKNOWN) + /* only one argument: clear entire history */ + n = clr_history(get_histtype(get_var_string(&argvars[0]))); + else if (argvars[1].var_type == VAR_NUMBER) + /* index given: remove that entry */ + n = del_history_idx(get_histtype(get_var_string(&argvars[0])), + (int)get_var_number(&argvars[1])); + else + /* string given: remove all matching entries */ + n = del_history_entry(get_histtype(get_var_string(&argvars[0])), + get_var_string_buf(&argvars[1], buf)); + retvar->var_val.var_number = n; +#else + retvar->var_val.var_number = 0; +#endif +} + +/* + * "histget()" function + */ +/*ARGSUSED*/ + static void +f_histget(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CMDHIST + int type; + int idx; + + type = get_histtype(get_var_string(&argvars[0])); + if (argvars[1].var_type == VAR_UNKNOWN) + idx = get_history_idx(type); + else + idx = (int)get_var_number(&argvars[1]); + retvar->var_val.var_string = vim_strsave(get_history_entry(type, idx)); +#else + retvar->var_val.var_string = NULL; +#endif + retvar->var_type = VAR_STRING; +} + +/* + * "histnr()" function + */ +/*ARGSUSED*/ + static void +f_histnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int i; + +#ifdef FEAT_CMDHIST + i = get_histtype(get_var_string(&argvars[0])); + if (i >= HIST_CMD && i < HIST_COUNT) + i = get_history_idx(i); + else +#endif + i = -1; + retvar->var_val.var_number = i; +} + +/* + * "highlight_exists()" function + */ + static void +f_hlexists(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = highlight_exists(get_var_string(&argvars[0])); +} + +/* + * "highlightID(name)" function + */ + static void +f_hlID(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = syn_name2id(get_var_string(&argvars[0])); +} + +/* + * "hostname()" function + */ +/*ARGSUSED*/ + static void +f_hostname(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u hostname[256]; + + mch_get_host_name(hostname, 256); + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strsave(hostname); +} + +/* + * iconv() function + */ +/*ARGSUSED*/ + static void +f_iconv(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_MBYTE + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *from, *to, *str; + vimconv_T vimconv; +#endif + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + +#ifdef FEAT_MBYTE + str = get_var_string(&argvars[0]); + from = enc_canonize(enc_skip(get_var_string_buf(&argvars[1], buf1))); + to = enc_canonize(enc_skip(get_var_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) + retvar->var_val.var_string = vim_strsave(str); + else + retvar->var_val.var_string = string_convert(&vimconv, str, NULL); + + convert_setup(&vimconv, NULL, NULL); + vim_free(from); + vim_free(to); +#endif +} + +/* + * "indent()" function + */ + static void +f_indent(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum; + + lnum = get_var_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + retvar->var_val.var_number = get_indent_lnum(lnum); + else + retvar->var_val.var_number = -1; +} + +static int inputsecret_flag = 0; + +/* + * "input()" function + * Also handles inputsecret() when inputsecret is set. + */ + static void +f_input(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *prompt = get_var_string(&argvars[0]); + char_u *p = NULL; + int c; + char_u buf[NUMBUFLEN]; + int cmd_silent_save = cmd_silent; + + retvar->var_type = VAR_STRING; + +#ifdef NO_CONSOLE_INPUT + /* While starting up, there is no place to enter text. */ + if (no_console_input()) + { + retvar->var_val.var_string = NULL; + 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].var_type != VAR_UNKNOWN) + stuffReadbuffSpec(get_var_string_buf(&argvars[1], buf)); + + retvar->var_val.var_string = + getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr); + + /* since the user typed this, no need to wait for return */ + need_wait_return = FALSE; + msg_didout = FALSE; + cmd_silent = cmd_silent_save; +} + +/* + * "inputdialog()" function + */ + static void +f_inputdialog(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#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]; + + message = get_var_string(&argvars[0]); + if (argvars[1].var_type != VAR_UNKNOWN) + { + STRNCPY(IObuff, get_var_string_buf(&argvars[1], buf), IOSIZE); + IObuff[IOSIZE - 1] = NUL; + } + else + IObuff[0] = NUL; + if (do_dialog(VIM_QUESTION, NULL, message, (char_u *)_("&OK\n&Cancel"), + 1, IObuff) == 1) + retvar->var_val.var_string = vim_strsave(IObuff); + else + { + if (argvars[1].var_type != VAR_UNKNOWN + && argvars[2].var_type != VAR_UNKNOWN) + retvar->var_val.var_string = vim_strsave( + get_var_string_buf(&argvars[2], buf)); + else + retvar->var_val.var_string = NULL; + } + retvar->var_type = VAR_STRING; + } + else +#endif + f_input(argvars, retvar); +} + +static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; + +/* + * "inputrestore()" function + */ +/*ARGSUSED*/ + static void +f_inputrestore(argvars, retvar) + VAR argvars; + VAR retvar; +{ + if (ga_userinput.ga_len > 0) + { + --ga_userinput.ga_len; + ++ga_userinput.ga_room; + restore_typeahead((tasave_T *)(ga_userinput.ga_data) + + ga_userinput.ga_len); + retvar->var_val.var_number = 0; /* OK */ + } + else if (p_verbose > 1) + { + msg((char_u *)_("called inputrestore() more often than inputsave()")); + retvar->var_val.var_number = 1; /* Failed */ + } +} + +/* + * "inputsave()" function + */ +/*ARGSUSED*/ + static void +f_inputsave(argvars, retvar) + VAR argvars; + VAR retvar; +{ + /* Add an entry to the stack of typehead storage. */ + if (ga_grow(&ga_userinput, 1) == OK) + { + save_typeahead((tasave_T *)(ga_userinput.ga_data) + + ga_userinput.ga_len); + ++ga_userinput.ga_len; + --ga_userinput.ga_room; + retvar->var_val.var_number = 0; /* OK */ + } + else + retvar->var_val.var_number = 1; /* Failed */ +} + +/* + * "inputsecret()" function + */ + static void +f_inputsecret(argvars, retvar) + VAR argvars; + VAR retvar; +{ + ++cmdline_star; + ++inputsecret_flag; + f_input(argvars, retvar); + --cmdline_star; + --inputsecret_flag; +} + +/* + * "isdirectory()" function + */ + static void +f_isdirectory(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = mch_isdir(get_var_string(&argvars[0])); +} + +/* + * "last_buffer_nr()" function. + */ +/*ARGSUSED*/ + static void +f_last_buffer_nr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int n = 0; + buf_T *buf; + + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + if (n < buf->b_fnum) + n = buf->b_fnum; + + retvar->var_val.var_number = n; +} + +/* + * "line(string)" function + */ + static void +f_line(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum = 0; + pos_T *fp; + + fp = var2fpos(&argvars[0], TRUE); + if (fp != NULL) + lnum = fp->lnum; + retvar->var_val.var_number = lnum; +} + +/* + * "line2byte(lnum)" function + */ +/*ARGSUSED*/ + static void +f_line2byte(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifndef FEAT_BYTEOFF + retvar->var_val.var_number = -1; +#else + linenr_T lnum; + + lnum = get_var_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = ml_find_line_or_offset(curbuf, lnum, NULL); + if (retvar->var_val.var_number >= 0) + ++retvar->var_val.var_number; +#endif +} + +/* + * "lispindent(lnum)" function + */ + static void +f_lispindent(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_LISP + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = get_var_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + retvar->var_val.var_number = get_lisp_indent(); + curwin->w_cursor = pos; + } + else +#endif + retvar->var_val.var_number = -1; +} + +/* + * "localtime()" function + */ +/*ARGSUSED*/ + static void +f_localtime(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = (varnumber_T)time(NULL); +} + +/* + * "maparg()" function + */ + static void +f_maparg(argvars, retvar) + VAR argvars; + VAR retvar; +{ + get_maparg(argvars, retvar, TRUE); +} + +/* + * "mapcheck()" function + */ + static void +f_mapcheck(argvars, retvar) + VAR argvars; + VAR retvar; +{ + get_maparg(argvars, retvar, FALSE); +} + + static void +get_maparg(argvars, retvar, exact) + VAR argvars; + VAR retvar; + int exact; +{ + char_u *keys; + char_u *which; + char_u buf[NUMBUFLEN]; + char_u *keys_buf = NULL; + char_u *rhs; + int mode; + garray_T ga; + + /* return empty string for failure */ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + + keys = get_var_string(&argvars[0]); + if (*keys == NUL) + return; + + if (argvars[1].var_type != VAR_UNKNOWN) + which = get_var_string_buf(&argvars[1], buf); + else + which = (char_u *)""; + mode = get_map_mode(&which, 0); + + keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE); + rhs = check_map(keys, mode, exact); + vim_free(keys_buf); + if (rhs != NULL) + { + ga_init(&ga); + ga.ga_itemsize = 1; + ga.ga_growsize = 40; + + while (*rhs != NUL) + ga_concat(&ga, str2special(&rhs, FALSE)); + + ga_append(&ga, NUL); + retvar->var_val.var_string = (char_u *)ga.ga_data; + } +} + +/* + * "match()" function + */ + static void +f_match(argvars, retvar) + VAR argvars; + VAR retvar; +{ + find_some_match(argvars, retvar, 1); +} + +/* + * "matchend()" function + */ + static void +f_matchend(argvars, retvar) + VAR argvars; + VAR retvar; +{ + find_some_match(argvars, retvar, 0); +} + +/* + * "matchstr()" function + */ + static void +f_matchstr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + find_some_match(argvars, retvar, 2); +} + + static void +find_some_match(argvars, retvar, type) + VAR argvars; + VAR retvar; + int type; +{ + char_u *str; + char_u *pat; + regmatch_T regmatch; + char_u patbuf[NUMBUFLEN]; + char_u *save_cpo; + long start = 0; + + /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + + str = get_var_string(&argvars[0]); + pat = get_var_string_buf(&argvars[1], patbuf); + + if (type == 2) + { + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; + } + else + retvar->var_val.var_number = -1; + + if (argvars[2].var_type != VAR_UNKNOWN) + { + start = get_var_number(&argvars[2]); + if (start < 0) + start = 0; + if (start > (long)STRLEN(str)) + goto theend; + str += start; + } + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = p_ic; + if (vim_regexec_nl(®match, str, (colnr_T)0)) + { + if (type == 2) + retvar->var_val.var_string = vim_strnsave(regmatch.startp[0], + (int)(regmatch.endp[0] - regmatch.startp[0])); + else + { + if (type != 0) + retvar->var_val.var_number = + (varnumber_T)(regmatch.startp[0] - str); + else + retvar->var_val.var_number = + (varnumber_T)(regmatch.endp[0] - str); + retvar->var_val.var_number += start; + } + } + vim_free(regmatch.regprog); + } + +theend: + p_cpo = save_cpo; +} + +/* + * "mode()" function + */ +/*ARGSUSED*/ + static void +f_mode(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[2]; + +#ifdef FEAT_VISUAL + if (VIsual_active) + { + if (VIsual_select) + buf[0] = VIsual_mode + 's' - 'v'; + else + buf[0] = VIsual_mode; + } + else +#endif + if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) + buf[0] = 'r'; + else if (State & INSERT) + { + if (State & REPLACE_FLAG) + buf[0] = 'R'; + else + buf[0] = 'i'; + } + else if (State & CMDLINE) + buf[0] = 'c'; + else + buf[0] = 'n'; + + buf[1] = NUL; + retvar->var_val.var_string = vim_strsave(buf); + retvar->var_type = VAR_STRING; +} + +/* + * "nr2char()" function + */ + static void +f_nr2char(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[NUMBUFLEN]; + +#ifdef FEAT_MBYTE + if (has_mbyte) + buf[(*mb_char2bytes)((int)get_var_number(&argvars[0]), buf)] = NUL; + else +#endif + { + buf[0] = (char_u)get_var_number(&argvars[0]); + buf[1] = NUL; + } + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strsave(buf); +} + +/* + * "rename({from}, {to})" function + */ + static void +f_rename(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = vim_rename(get_var_string(&argvars[0]), + get_var_string_buf(&argvars[1], buf)); +} + +/* + * "resolve()" function + */ + static void +f_resolve(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + + p = get_var_string(&argvars[0]); +#ifdef FEAT_SHORTCUT + { + char_u *v = NULL; + + v = mch_resolve_shortcut(p); + if (v != NULL) + retvar->var_val.var_string = v; + else + retvar->var_val.var_string = vim_strsave(p); + } +#else +# ifdef HAVE_READLINK + { + char_u buf[MAXPATHL + 1]; + 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 && vim_ispathsep(p[len-1])) + has_trailing_pathsep = TRUE; + + 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; + } + + 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?)")); + retvar->var_val.var_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 = vim_strnsave(q-1, STRLEN(q-1)+STRLEN(remain)); + if (cpy != NULL) + { + STRCAT(cpy, remain); + 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) + STRCPY(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 = vim_strnsave((char_u *)"./", 2 + STRLEN(p)); + if (cpy != NULL) + { + STRCAT(cpy, p); + 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) + mch_memmove(p, p + 2, STRLEN(p + 2) + (size_t)1); + } + } + + /* 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); + while ((q > p + 2 || (q == p + 2 && !vim_ispathsep(*p))) + && vim_ispathsep(q[-1])) + --q; + *q = NUL; + } + + retvar->var_val.var_string = p; + } +# else + retvar->var_val.var_string = vim_strsave(p); +# endif +#endif + + simplify_filename(retvar->var_val.var_string); + +#ifdef HAVE_READLINK +fail: +#endif + retvar->var_type = VAR_STRING; +} + +/* + * "simplify()" function + */ + static void +f_simplify(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + + p = get_var_string(&argvars[0]); + retvar->var_val.var_string = vim_strsave(p); + simplify_filename(retvar->var_val.var_string); /* simplify in place */ + retvar->var_type = VAR_STRING; +} + +/* + * "search()" function + */ + static void +f_search(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *pat; + pos_T pos; + int save_p_ws = p_ws; + int dir; + + pat = get_var_string(&argvars[0]); + dir = get_search_arg(&argvars[1], NULL); /* may set p_ws */ + + pos = curwin->w_cursor; + if (searchit(curwin, curbuf, &pos, dir, pat, 1L, + SEARCH_KEEP, RE_SEARCH) != FAIL) + { + retvar->var_val.var_number = pos.lnum; + curwin->w_cursor = pos; + /* "/$" will put the cursor after the end of the line, may need to + * correct that here */ + check_cursor(); + } + else + retvar->var_val.var_number = 0; + p_ws = save_p_ws; +} + +#define SP_NOMOVE 1 /* don't move cursor */ +#define SP_REPEAT 2 /* repeat to find outer pair */ +#define SP_RETCOUNT 4 /* return matchcount */ + +/* + * "searchpair()" function + */ + static void +f_searchpair(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *spat, *mpat, *epat; + char_u *skip; + char_u *pat, *pat2, *pat3; + pos_T pos; + pos_T firstpos; + pos_T save_cursor; + pos_T save_pos; + int save_p_ws = p_ws; + char_u *save_cpo; + int dir; + int flags = 0; + char_u nbuf1[NUMBUFLEN]; + char_u nbuf2[NUMBUFLEN]; + char_u nbuf3[NUMBUFLEN]; + int n; + int r; + int nest = 1; + int err; + + retvar->var_val.var_number = 0; /* default: FAIL */ + + /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + + /* Get the three pattern arguments: start, middle, end. */ + spat = get_var_string(&argvars[0]); + mpat = get_var_string_buf(&argvars[1], nbuf1); + epat = get_var_string_buf(&argvars[2], nbuf2); + + /* 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); + + /* Handle the optional fourth argument: flags */ + dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ + + /* Optional fifth argument: skip expresion */ + if (argvars[3].var_type == VAR_UNKNOWN + || argvars[4].var_type == VAR_UNKNOWN) + skip = (char_u *)""; + else + skip = get_var_string_buf(&argvars[4], nbuf3); + + save_cursor = curwin->w_cursor; + pos = curwin->w_cursor; + firstpos.lnum = 0; + pat = pat3; + for (;;) + { + n = searchit(curwin, curbuf, &pos, dir, pat, 1L, + SEARCH_KEEP, RE_SEARCH); + 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 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; + retvar->var_val.var_number = -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) + ++retvar->var_val.var_number; + else + retvar->var_val.var_number = pos.lnum; + curwin->w_cursor = pos; + if (!(flags & SP_REPEAT)) + break; + nest = 1; /* search for next unmatched */ + } + } + + /* If 'n' flag is used or search failed: restore cursor position. */ + if ((flags & SP_NOMOVE) || retvar->var_val.var_number == 0) + curwin->w_cursor = save_cursor; + +theend: + vim_free(pat2); + vim_free(pat3); + p_ws = save_p_ws; + p_cpo = save_cpo; +} + + static int +get_search_arg(varp, flagsp) + VAR varp; + int *flagsp; +{ + int dir = FORWARD; + char_u *flags; + char_u nbuf[NUMBUFLEN]; + + if (varp->var_type != VAR_UNKNOWN) + { + flags = get_var_string_buf(varp, nbuf); + if (vim_strchr(flags, 'b') != NULL) + dir = BACKWARD; + if (vim_strchr(flags, 'w') != NULL) + p_ws = TRUE; + if (vim_strchr(flags, 'W') != NULL) + p_ws = FALSE; + if (flagsp != NULL) + { + if (vim_strchr(flags, 'n') != NULL) + *flagsp |= SP_NOMOVE; + if (vim_strchr(flags, 'r') != NULL) + *flagsp |= SP_REPEAT; + if (vim_strchr(flags, 'm') != NULL) + *flagsp |= SP_RETCOUNT; + } + } + return dir; +} + +/* + * "setbufvar()" function + */ +/*ARGSUSED*/ + static void +f_setbufvar(argvars, retvar) + VAR argvars; + VAR retvar; +{ + buf_T *buf; +#ifdef FEAT_AUTOCMD + aco_save_T aco; +#else + buf_T *save_curbuf; +#endif + char_u *varname, *bufvarname; + VAR varp; + char_u nbuf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + ++emsg_off; + buf = get_buf_var(&argvars[0]); + varname = get_var_string(&argvars[1]); + varp = &argvars[2]; + + if (buf != NULL && varname != NULL && varp != NULL) + { + /* set curbuf to be our buf, temporarily */ +#ifdef FEAT_AUTOCMD + aucmd_prepbuf(&aco, buf); +#else + save_curbuf = curbuf; + curbuf = buf; +#endif + + if (*varname == '&') + { + ++varname; + set_option_value(varname, get_var_number(varp), + get_var_string_buf(varp, nbuf), OPT_LOCAL); + } + else + { + bufvarname = alloc((unsigned)STRLEN(varname) + 3); + if (bufvarname != NULL) + { + STRCPY(bufvarname, "b:"); + STRCPY(bufvarname + 2, varname); + set_var(bufvarname, varp); + vim_free(bufvarname); + } + } + + /* reset notion of buffer */ +#ifdef FEAT_AUTOCMD + aucmd_restbuf(&aco); +#else + curbuf = save_curbuf; +#endif + } + --emsg_off; +} + +/* + * "setcmdpos()" function + */ + static void +f_setcmdpos(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = set_cmdline_pos( + (int)get_var_number(&argvars[0]) - 1); +} + +/* + * "setline()" function + */ + static void +f_setline(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum; + char_u *line; + + lnum = get_var_lnum(argvars); + line = get_var_string(&argvars[1]); + retvar->var_val.var_number = 1; /* FAIL is default */ + + if (lnum >= 1 + && lnum <= curbuf->b_ml.ml_line_count + && u_savesub(lnum) == OK + && ml_replace(lnum, line, TRUE) == OK) + { + changed_bytes(lnum, 0); + check_cursor_col(); + retvar->var_val.var_number = 0; + } +} + +/* + * "setreg()" function + */ + static void +f_setreg(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int regname; + char_u *strregname; + char_u *stropt; + int append; + char_u yank_type; + long block_len; + + block_len = -1; + yank_type = MAUTO; + append = FALSE; + + strregname = get_var_string(argvars); + retvar->var_val.var_number = 1; /* FAIL is default */ + + regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0 || regname == '@') + regname = '"'; + else if (regname == '=') + return; + + if (argvars[2].var_type != VAR_UNKNOWN) + { + for (stropt = get_var_string(&argvars[2]); *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; +#ifdef FEAT_VISUAL + case 'b': case Ctrl_V: /* block-wise selection */ + yank_type = MBLOCK; + if (VIM_ISDIGIT(stropt[1])) + { + ++stropt; + block_len = getdigits(&stropt) - 1; + --stropt; + } + break; +#endif + } + } + + write_reg_contents_ex(regname, get_var_string(&argvars[1]), -1, + append, yank_type, block_len); + retvar->var_val.var_number = 0; +} + + +/* + * "setwinvar(expr)" function + */ +/*ARGSUSED*/ + static void +f_setwinvar(argvars, retvar) + VAR argvars; + VAR retvar; +{ + win_T *win; +#ifdef FEAT_WINDOWS + win_T *save_curwin; +#endif + char_u *varname, *winvarname; + VAR varp; + char_u nbuf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + ++emsg_off; + win = find_win_by_nr(&argvars[0]); + varname = get_var_string(&argvars[1]); + varp = &argvars[2]; + + if (win != NULL && varname != NULL && varp != NULL) + { +#ifdef FEAT_WINDOWS + /* set curwin to be our win, temporarily */ + save_curwin = curwin; + curwin = win; + curbuf = curwin->w_buffer; +#endif + + if (*varname == '&') + { + ++varname; + set_option_value(varname, get_var_number(varp), + get_var_string_buf(varp, nbuf), OPT_LOCAL); + } + else + { + winvarname = alloc((unsigned)STRLEN(varname) + 3); + if (winvarname != NULL) + { + STRCPY(winvarname, "w:"); + STRCPY(winvarname + 2, varname); + set_var(winvarname, varp); + vim_free(winvarname); + } + } + +#ifdef FEAT_WINDOWS + /* Restore current window, if it's still valid (autocomands can make + * it invalid). */ + if (win_valid(save_curwin)) + { + curwin = save_curwin; + curbuf = curwin->w_buffer; + } +#endif + } + --emsg_off; +} + +/* + * "nextnonblank()" function + */ + static void +f_nextnonblank(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum; + + for (lnum = get_var_lnum(argvars); ; ++lnum) + { + if (lnum > curbuf->b_ml.ml_line_count) + { + lnum = 0; + break; + } + if (*skipwhite(ml_get(lnum)) != NUL) + break; + } + retvar->var_val.var_number = lnum; +} + +/* + * "prevnonblank()" function + */ + static void +f_prevnonblank(argvars, retvar) + VAR argvars; + VAR retvar; +{ + linenr_T lnum; + + lnum = get_var_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + lnum = 0; + else + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) + --lnum; + retvar->var_val.var_number = lnum; +} + +#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) +static void make_connection __ARGS((void)); +static int check_connection __ARGS((void)); + + static void +make_connection() +{ + 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() +{ + make_connection(); + if (X_DISPLAY == NULL) + { + EMSG(_("E240: No connection to Vim server")); + return FAIL; + } + return OK; +} +#endif + +/*ARGSUSED*/ + static void +f_serverlist(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + r = serverGetVimNames(); +# else + make_connection(); + if (X_DISPLAY != NULL) + r = serverGetVimNames(X_DISPLAY); +# endif +#endif + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = r; +} + +/*ARGSUSED*/ + static void +f_remote_peek(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CLIENTSERVER + var v; + char_u *s = NULL; +# ifdef WIN32 + int n = 0; +# endif + + if (check_restricted() || check_secure()) + { + retvar->var_val.var_number = -1; + return; + } +# ifdef WIN32 + sscanf(get_var_string(&argvars[0]), "%x", &n); + if (n == 0) + retvar->var_val.var_number = -1; + else + { + s = serverGetReply((HWND)n, FALSE, FALSE, FALSE); + retvar->var_val.var_number = (s != NULL); + } +# else + retvar->var_val.var_number = 0; + if (check_connection() == FAIL) + return; + + retvar->var_val.var_number = serverPeekReply(X_DISPLAY, + serverStrToWin(get_var_string(&argvars[0])), &s); +# endif + + if (argvars[1].var_type != VAR_UNKNOWN && retvar->var_val.var_number > 0) + { + v.var_type = VAR_STRING; + v.var_val.var_string = vim_strsave(s); + set_var(get_var_string(&argvars[1]), &v); + } +#else + retvar->var_val.var_number = -1; +#endif +} + +/*ARGSUSED*/ + static void +f_remote_read(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER + if (!check_restricted() && !check_secure()) + { +# ifdef WIN32 + /* The server's HWND is encoded in the 'id' parameter */ + int n = 0; + + sscanf(get_var_string(&argvars[0]), "%x", &n); + if (n != 0) + r = serverGetReply((HWND)n, FALSE, TRUE, TRUE); + if (r == NULL) +# else + if (check_connection() == FAIL || serverReadReply(X_DISPLAY, + serverStrToWin(get_var_string(&argvars[0])), &r, FALSE) < 0) +# endif + EMSG(_("E277: Unable to read a server reply")); + } +#endif + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = r; +} + +/*ARGSUSED*/ + static void +f_server2client(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_CLIENTSERVER + char_u buf[NUMBUFLEN]; + char_u *server = get_var_string(&argvars[0]); + char_u *reply = get_var_string_buf(&argvars[1], buf); + + retvar->var_val.var_number = -1; + 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; + } + retvar->var_val.var_number = 0; +#else + retvar->var_val.var_number = -1; +#endif +} + +#ifdef FEAT_CLIENTSERVER +static void remote_common __ARGS((VAR argvars, VAR retvar, int expr)); + + static void +remote_common(argvars, retvar, expr) + VAR argvars; + VAR retvar; + 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_var_string(&argvars[0]); + keys = get_var_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; + } + + retvar->var_val.var_string = r; + + if (argvars[2].var_type != VAR_UNKNOWN) + { + var v; + char_u str[30]; + + sprintf((char *)str, "0x%x", (unsigned int)w); + v.var_type = VAR_STRING; + v.var_val.var_string = vim_strsave(str); + set_var(get_var_string(&argvars[2]), &v); + } +} +#endif + +/* + * "remote_expr()" function + */ +/*ARGSUSED*/ + static void +f_remote_expr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, retvar, TRUE); +#endif +} + +/* + * "remote_send()" function + */ +/*ARGSUSED*/ + static void +f_remote_send(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, retvar, FALSE); +#endif +} + +/* + * "remote_foreground()" function + */ +/*ARGSUSED*/ + static void +f_remote_foreground(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = 0; +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + /* On Win32 it's done in this application. */ + serverForeground(get_var_string(&argvars[0])); +# else + /* Send a foreground() expression to the server. */ + argvars[1].var_type = VAR_STRING; + argvars[1].var_val.var_string = vim_strsave((char_u *)"foreground()"); + argvars[2].var_type = VAR_UNKNOWN; + remote_common(argvars, retvar, TRUE); + vim_free(argvars[1].var_val.var_string); +# endif +#endif +} + +#ifdef HAVE_STRFTIME +/* + * "strftime({format}[, {time}])" function + */ + static void +f_strftime(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u result_buf[256]; + struct tm *curtime; + time_t seconds; + char_u *p; + + retvar->var_type = VAR_STRING; + + p = get_var_string(&argvars[0]); + if (argvars[1].var_type == VAR_UNKNOWN) + seconds = time(NULL); + else + seconds = (time_t)get_var_number(&argvars[1]); + curtime = localtime(&seconds); + /* MSVC returns NULL for an invalid value of seconds. */ + if (curtime == NULL) + retvar->var_val.var_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) + retvar->var_val.var_string = + string_convert(&conv, result_buf, NULL); + else +# endif + retvar->var_val.var_string = vim_strsave(result_buf); + +# ifdef FEAT_MBYTE + /* Release conversion descriptors */ + convert_setup(&conv, NULL, NULL); + vim_free(enc); +# endif + } +} +#endif + +/* + * "stridx()" function + */ + static void +f_stridx(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[NUMBUFLEN]; + char_u *needle; + char_u *haystack; + char_u *pos; + + needle = get_var_string(&argvars[1]); + haystack = get_var_string_buf(&argvars[0], buf); + pos = (char_u *)strstr((char *)haystack, (char *)needle); + + if (pos == NULL) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = (varnumber_T) (pos - haystack); +} + +/* + * "strridx()" function + */ + static void +f_strridx(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u buf[NUMBUFLEN]; + char_u *needle; + char_u *haystack; + char_u *rest; + char_u *lastmatch = NULL; + + needle = get_var_string(&argvars[1]); + haystack = get_var_string_buf(&argvars[0], buf); + rest = haystack; + while (*haystack != '\0') + { + rest = (char_u *)strstr((char *)rest, (char *)needle); + if (rest == NULL) + break; + lastmatch = rest++; + } + + if (lastmatch == NULL) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = (varnumber_T) (lastmatch - haystack); +} + +/* + * "strlen()" function + */ + static void +f_strlen(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_val.var_number = (varnumber_T) (STRLEN(get_var_string(&argvars[0]))); +} + +/* + * "strpart()" function + */ + static void +f_strpart(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + int n; + int len; + int slen; + + p = get_var_string(&argvars[0]); + slen = (int)STRLEN(p); + + n = get_var_number(&argvars[1]); + if (argvars[2].var_type != VAR_UNKNOWN) + len = get_var_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; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_strnsave(p + n, len); +} + +/* + * "strtrans()" function + */ + static void +f_strtrans(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = transstr(get_var_string(&argvars[0])); +} + +/* + * "synID(line, col, trans)" function + */ +/*ARGSUSED*/ + static void +f_synID(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int id = 0; +#ifdef FEAT_SYN_HL + long line; + long col; + int trans; + + line = get_var_lnum(argvars); + col = get_var_number(&argvars[1]) - 1; + trans = get_var_number(&argvars[2]); + + if (line >= 1 && line <= curbuf->b_ml.ml_line_count + && col >= 0 && col < (long)STRLEN(ml_get(line))) + id = syn_get_id(line, col, trans); +#endif + + retvar->var_val.var_number = id; +} + +/* + * "synIDattr(id, what [, mode])" function + */ +/*ARGSUSED*/ + static void +f_synIDattr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p = NULL; +#ifdef FEAT_SYN_HL + int id; + char_u *what; + char_u *mode; + char_u modebuf[NUMBUFLEN]; + int modec; + + id = get_var_number(&argvars[0]); + what = get_var_string(&argvars[1]); + if (argvars[2].var_type != VAR_UNKNOWN) + { + mode = get_var_string_buf(&argvars[2], modebuf); + modec = TOLOWER_ASC(mode[0]); + if (modec != 't' && modec != 'c' +#ifdef FEAT_GUI + && modec != 'g' +#endif + ) + modec = 0; /* replace invalid with current */ + } + else + { +#ifdef FEAT_GUI + if (gui.in_use) + 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[#] */ + 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': /* standout */ + p = highlight_has_attr(id, HL_STANDOUT, modec); + break; + + case 'u': /* underline */ + p = highlight_has_attr(id, HL_UNDERLINE, modec); + break; + } + + if (p != NULL) + p = vim_strsave(p); +#endif + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = p; +} + +/* + * "synIDtrans(id)" function + */ +/*ARGSUSED*/ + static void +f_synIDtrans(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int id; + +#ifdef FEAT_SYN_HL + id = get_var_number(&argvars[0]); + + if (id > 0) + id = syn_get_final_id(id); + else +#endif + id = 0; + + retvar->var_val.var_number = id; +} + +/* + * "system()" function + */ + static void +f_system(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + + p = get_cmd_output(get_var_string(&argvars[0]), SHELL_SILENT); +#ifdef USE_CR + /* translate <CR> into <NL> */ + if (p != NULL) + { + char_u *s; + + for (s = p; *s; ++s) + { + if (*s == CAR) + *s = NL; + } + } +#else +# ifdef USE_CRNL + /* translate <CR><NL> into <NL> */ + if (p != NULL) + { + char_u *s, *d; + + d = p; + for (s = p; *s; ++s) + { + if (s[0] == CAR && s[1] == NL) + ++s; + *d++ = *s; + } + *d = NUL; + } +# endif +#endif + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = p; +} + +/* + * "submatch()" function + */ + static void +f_submatch(argvars, retvar) + VAR argvars; + VAR retvar; +{ + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = reg_submatch((int)get_var_number(&argvars[0])); +} + +/* + * "substitute()" function + */ + static void +f_substitute(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u patbuf[NUMBUFLEN]; + char_u subbuf[NUMBUFLEN]; + char_u flagsbuf[NUMBUFLEN]; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = do_string_sub( + get_var_string(&argvars[0]), + get_var_string_buf(&argvars[1], patbuf), + get_var_string_buf(&argvars[2], subbuf), + get_var_string_buf(&argvars[3], flagsbuf)); +} + +/* + * "tempname()" function + */ +/*ARGSUSED*/ + static void +f_tempname(argvars, retvar) + VAR argvars; + VAR retvar; +{ + static int x = 'A'; + + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = vim_tempname(x); + + /* 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'); +} + +/* + * "tolower(string)" function + */ + static void +f_tolower(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + + p = vim_strsave(get_var_string(&argvars[0])); + retvar->var_type = VAR_STRING; + retvar->var_val.var_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_check(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_check)(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(argvars, retvar) + VAR argvars; + VAR retvar; +{ + char_u *p; + + p = vim_strsave(get_var_string(&argvars[0])); + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = p; + + if (p != NULL) + while (*p != NUL) + { +#ifdef FEAT_MBYTE + int l; + + if (enc_utf8) + { + int c, uc; + + c = utf_ptr2char(p); + uc = utf_toupper(c); + l = utf_ptr2len_check(p); + /* TODO: reallocate string when byte count changes. */ + if (utf_char2len(uc) == l) + utf_char2bytes(uc, p); + p += l; + } + else if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1) + p += l; /* skip multi-byte character */ + else +#endif + { + *p = TOUPPER_LOC(*p); /* note that toupper() can be a macro */ + p++; + } + } +} + +/* + * "type(expr)" function + */ + static void +f_type(argvars, retvar) + VAR argvars; + VAR retvar; +{ + if (argvars[0].var_type == VAR_NUMBER) + retvar->var_val.var_number = 0; + else + retvar->var_val.var_number = 1; +} + +/* + * "virtcol(string)" function + */ + static void +f_virtcol(argvars, retvar) + VAR argvars; + VAR retvar; +{ + colnr_T vcol = 0; + pos_T *fp; + + fp = var2fpos(&argvars[0], FALSE); + if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count) + { + getvvcol(curwin, fp, NULL, NULL, &vcol); + ++vcol; + } + + retvar->var_val.var_number = vcol; +} + +/* + * "visualmode()" function + */ +/*ARGSUSED*/ + static void +f_visualmode(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#ifdef FEAT_VISUAL + char_u str[2]; + + retvar->var_type = VAR_STRING; + str[0] = curbuf->b_visual_mode_eval; + str[1] = NUL; + retvar->var_val.var_string = vim_strsave(str); + + /* A non-zero number or non-empty string argument: reset mode. */ + if ((argvars[0].var_type == VAR_NUMBER + && argvars[0].var_val.var_number != 0) + || (argvars[0].var_type == VAR_STRING + && *get_var_string(&argvars[0]) != NUL)) + curbuf->b_visual_mode_eval = NUL; +#else + retvar->var_val.var_number = 0; /* return anything, it won't work anyway */ +#endif +} + +/* + * "winbufnr(nr)" function + */ + static void +f_winbufnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0]); + if (wp == NULL) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = wp->w_buffer->b_fnum; +} + +/* + * "wincol()" function + */ +/*ARGSUSED*/ + static void +f_wincol(argvars, retvar) + VAR argvars; + VAR retvar; +{ + validate_cursor(); + retvar->var_val.var_number = curwin->w_wcol + 1; +} + +/* + * "winheight(nr)" function + */ + static void +f_winheight(argvars, retvar) + VAR argvars; + VAR retvar; +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0]); + if (wp == NULL) + retvar->var_val.var_number = -1; + else + retvar->var_val.var_number = wp->w_height; +} + +/* + * "winline()" function + */ +/*ARGSUSED*/ + static void +f_winline(argvars, retvar) + VAR argvars; + VAR retvar; +{ + validate_cursor(); + retvar->var_val.var_number = curwin->w_wrow + 1; +} + +/* + * "winnr()" function + */ +/* ARGSUSED */ + static void +f_winnr(argvars, retvar) + VAR argvars; + VAR retvar; +{ + int nr = 1; +#ifdef FEAT_WINDOWS + win_T *wp; + + for (wp = firstwin; wp != curwin; wp = wp->w_next) + ++nr; +#endif + retvar->var_val.var_number = nr; +} + +/* + * "winrestcmd()" function + */ +/* ARGSUSED */ + static void +f_winrestcmd(argvars, retvar) + VAR argvars; + VAR retvar; +{ +#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); +# ifdef FEAT_VERTSPLIT + sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); + ga_concat(&ga, buf); +# endif + ++winnr; + } + + retvar->var_val.var_string = ga.ga_data; +#else + retvar->var_val.var_string = NULL; +#endif + retvar->var_type = VAR_STRING; +} + +/* + * "winwidth(nr)" function + */ + static void +f_winwidth(argvars, retvar) + VAR argvars; + VAR retvar; +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0]); + if (wp == NULL) + retvar->var_val.var_number = -1; + else +#ifdef FEAT_VERTSPLIT + retvar->var_val.var_number = wp->w_width; +#else + retvar->var_val.var_number = Columns; +#endif +} + + static win_T * +find_win_by_nr(vp) + VAR vp; +{ +#ifdef FEAT_WINDOWS + win_T *wp; +#endif + int nr; + + nr = get_var_number(vp); + +#ifdef FEAT_WINDOWS + if (nr == 0) + return curwin; + + for (wp = firstwin; wp != NULL; wp = wp->w_next) + if (--nr <= 0) + break; + return wp; +#else + if (nr == 0 || nr == 1) + return curwin; + return NULL; +#endif +} + +/* + * Translate a String variable into a position. + */ + static pos_T * +var2fpos(varp, lnum) + VAR varp; + int lnum; /* TRUE when $ is last line */ +{ + char_u *name; + static pos_T pos; + pos_T *pp; + + name = get_var_string(varp); + if (name[0] == '.') /* cursor */ + return &curwin->w_cursor; + if (name[0] == '\'') /* mark */ + { + pp = getmark(name[1], FALSE); + if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) + return NULL; + return pp; + } + if (name[0] == '$') /* last column or line */ + { + if (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; +} + +/* + * Get the length of an environment variable name. + * Advance "arg" to the first character after the name. + * Return 0 for error. + */ + static int +get_env_len(arg) + char_u **arg; +{ + char_u *p; + int len; + + for (p = *arg; vim_isIDc(*p); ++p) + ; + if (p == *arg) /* no name found */ + return 0; + + len = (int)(p - *arg); + *arg = p; + return len; +} + +/* + * Get the length of the name of a function or internal variable. + * "arg" is advanced to the first non-white character after the name. + * Return 0 if something is wrong. + */ + static int +get_id_len(arg) + char_u **arg; +{ + char_u *p; + int len; + + /* Find the end of the name. */ + for (p = *arg; eval_isnamec(*p); ++p) + ; + if (p == *arg) /* no name found */ + return 0; + + len = (int)(p - *arg); + *arg = skipwhite(p); + + return len; +} + +/* + * Get the length of the name of a function. + * "arg" is advanced to the first non-white character after the name. + * Return 0 if something is wrong. + * 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_func_len(arg, alias, evaluate) + char_u **arg; + char_u **alias; + int evaluate; +{ + int len; +#ifdef FEAT_MAGIC_BRACES + char_u *p; + char_u *expr_start; + char_u *expr_end; +#endif + + *alias = NULL; /* default to no alias */ + + if ((*arg)[0] == K_SPECIAL && (*arg)[1] == KS_EXTRA + && (*arg)[2] == (int)KE_SNR) + { + /* hard coded <SNR>, already translated */ + *arg += 3; + return get_id_len(arg) + 3; + } + len = eval_fname_script(*arg); + if (len > 0) + { + /* literal "<SID>", "s:" or "<SNR>" */ + *arg += len; + } + +#ifdef FEAT_MAGIC_BRACES + /* + * Find the end of the name; + */ + p = find_name_end(*arg, &expr_start, &expr_end); + /* check for {} construction */ + if (expr_start != NULL) + { + char_u *temp_string; + + if (!evaluate) + { + len += (int)(p - *arg); + *arg = skipwhite(p); + return len; + } + + /* + * Include any <SID> etc in the expanded string: + * Thus the -len here. + */ + temp_string = make_expanded_name(*arg - len, expr_start, expr_end, p); + if (temp_string == NULL) + return 0; + *alias = temp_string; + *arg = skipwhite(p); + return (int)STRLEN(temp_string); + } +#endif + + len += get_id_len(arg); + if (len == 0) + EMSG2(_(e_invexpr2), *arg); + + return len; +} + + static char_u * +find_name_end(arg, expr_start, expr_end) + char_u *arg; + char_u **expr_start; + char_u **expr_end; +{ + int nesting = 0; + char_u *p; + + *expr_start = NULL; + *expr_end = NULL; + + for (p = arg; (*p != NUL && (eval_isnamec(*p) || nesting != 0)); ++p) + { +#ifdef FEAT_MAGIC_BRACES + if (*p == '{') + { + nesting++; + if (*expr_start == NULL) + *expr_start = p; + } + else if (*p == '}') + { + nesting--; + if (nesting == 0 && *expr_end == NULL) + *expr_end = p; + } +#endif + } + + return p; +} + +/* + * Return TRUE if character "c" can be used in a variable or function name. + */ + static int +eval_isnamec(c) + int c; +{ + return (ASCII_ISALNUM(c) || c == '_' || c == ':' +#ifdef FEAT_MAGIC_BRACES + || c == '{' || c == '}' +#endif + ); +} + +/* + * Find a v: variable. + * Return it's index, or -1 if not found. + */ + static int +find_vim_var(name, len) + char_u *name; + int len; /* length of "name" */ +{ + char_u *vname; + int vlen; + int i; + + /* + * Ignore "v:" for old built-in variables, require it for new ones. + */ + if (name[0] == 'v' && name[1] == ':') + { + vname = name + 2; + vlen = len - 2; + } + else + { + vname = name; + vlen = len; + } + for (i = 0; i < VV_LEN; ++i) + if (vlen == vimvars[i].len && STRCMP(vname, vimvars[i].name) == 0 + && ((vimvars[i].flags & VV_COMPAT) || vname != name)) + return i; + return -1; +} + +/* + * Set number v: variable to "val". + */ + void +set_vim_var_nr(idx, val) + int idx; + long val; +{ + vimvars[idx].val = (char_u *)val; +} + +/* + * Get number v: variable value; + */ + long +get_vim_var_nr(idx) + int idx; +{ + return (long)vimvars[idx].val; +} + +/* + * Set v:count, v:count1 and v:prevcount. + */ + void +set_vcount(count, count1) + long count; + long count1; +{ + vimvars[VV_PREVCOUNT].val = vimvars[VV_COUNT].val; + vimvars[VV_COUNT].val = (char_u *)count; + vimvars[VV_COUNT1].val = (char_u *)count1; +} + +/* + * Set string v: variable to a copy of "val". + */ + void +set_vim_var_string(idx, val, len) + int idx; + char_u *val; + int len; /* length of "val" to use or -1 (whole string) */ +{ + vim_free(vimvars[idx].val); + if (val == NULL) + vimvars[idx].val = NULL; + else if (len == -1) + vimvars[idx].val = vim_strsave(val); + else + vimvars[idx].val = vim_strnsave(val, len); +} + +/* + * Set v:register if needed. + */ + void +set_reg_var(c) + int c; +{ + char_u regname; + + if (c == 0 || c == ' ') + regname = '"'; + else + regname = c; + /* Avoid free/alloc when the value is already right. */ + if (vimvars[VV_REG].val == NULL || vimvars[VV_REG].val[0] != c) + set_vim_var_string(VV_REG, ®name, 1); +} + +/* + * Get or set v:exception. If "oldval" == NULL, return the current value. + * Otherwise, restore the value to "oldval" and return NULL. + * Must always be called in pairs to save and restore v:exception! Does not + * take care of memory allocations. + */ + char_u * +v_exception(oldval) + char_u *oldval; +{ + if (oldval == NULL) + return vimvars[VV_EXCEPTION].val; + + vimvars[VV_EXCEPTION].val = oldval; + return NULL; +} + +/* + * Get or set v:throwpoint. If "oldval" == NULL, return the current value. + * Otherwise, restore the value to "oldval" and return NULL. + * Must always be called in pairs to save and restore v:throwpoint! Does not + * take care of memory allocations. + */ + char_u * +v_throwpoint(oldval) + char_u *oldval; +{ + if (oldval == NULL) + return vimvars[VV_THROWPOINT].val; + + vimvars[VV_THROWPOINT].val = oldval; + return NULL; +} + +#if defined(FEAT_AUTOCMD) || defined(PROTO) +/* + * Set v:cmdarg. + * If "eap" != NULL, use "eap" to generate the value and return the old value. + * If "oldarg" != NULL, restore the value to "oldarg" and return NULL. + * Must always be called in pairs! + */ + char_u * +set_cmdarg(eap, oldarg) + exarg_T *eap; + char_u *oldarg; +{ + char_u *oldval; + char_u *newval; + unsigned len; + + oldval = vimvars[VV_CMDARG].val; + if (eap != NULL) + { + if (eap->force_bin == FORCE_BIN) + len = 6; + else if (eap->force_bin == FORCE_NOBIN) + len = 8; + else + len = 0; + if (eap->force_ff != 0) + len += (unsigned)STRLEN(eap->cmd + eap->force_ff) + 6; +# ifdef FEAT_MBYTE + if (eap->force_enc != 0) + len += (unsigned)STRLEN(eap->cmd + eap->force_enc) + 7; +# endif + + newval = alloc(len + 1); + if (newval == NULL) + return NULL; + + if (eap->force_bin == FORCE_BIN) + sprintf((char *)newval, " ++bin"); + else if (eap->force_bin == FORCE_NOBIN) + sprintf((char *)newval, " ++nobin"); + else + *newval = NUL; + if (eap->force_ff != 0) + sprintf((char *)newval + STRLEN(newval), " ++ff=%s", + eap->cmd + eap->force_ff); +# ifdef FEAT_MBYTE + if (eap->force_enc != 0) + sprintf((char *)newval + STRLEN(newval), " ++enc=%s", + eap->cmd + eap->force_enc); +# endif + vimvars[VV_CMDARG].val = newval; + return oldval; + } + + vim_free(oldval); + vimvars[VV_CMDARG].val = oldarg; + return NULL; +} +#endif + +/* + * Get the value of internal variable "name". + * Return OK or FAIL. + */ + static int +get_var_var(name, len, retvar) + char_u *name; + int len; /* length of "name" */ + VAR retvar; /* NULL when only checking existence */ +{ + int ret = OK; + int type = VAR_UNKNOWN; + long number = 1; + char_u *string = NULL; + VAR v; + int cc; + int i; + + /* truncate the name, so that we can use strcmp() */ + cc = name[len]; + name[len] = NUL; + + /* + * Check for "b:changedtick". + */ + if (STRCMP(name, "b:changedtick") == 0) + { + type = VAR_NUMBER; + number = curbuf->b_changedtick; + } + + /* + * Check for built-in v: variables. + */ + else if ((i = find_vim_var(name, len)) >= 0) + { + type = vimvars[i].type; + number = (long)vimvars[i].val; + string = vimvars[i].val; + } + + /* + * Check for user-defined variables. + */ + else + { + v = find_var(name, FALSE); + if (v != NULL) + { + type = v->var_type; + number = v->var_val.var_number; + string = v->var_val.var_string; + } + } + + if (type == VAR_UNKNOWN) + { + if (retvar != NULL) + EMSG2(_("E121: Undefined variable: %s"), name); + ret = FAIL; + } + else if (retvar != NULL) + { + retvar->var_type = type; + if (type == VAR_NUMBER) + retvar->var_val.var_number = number; + else if (type == VAR_STRING) + { + if (string != NULL) + string = vim_strsave(string); + retvar->var_val.var_string = string; + } + } + + name[len] = cc; + + return ret; +} + +/* + * Allocate memory for a variable, and make it emtpy (0 or NULL value). + */ + static VAR +alloc_var() +{ + return (VAR)alloc_clear((unsigned)sizeof(var)); +} + +/* + * Allocate memory for a variable, and assign a string to it. + * The string "s" must have been allocated, it is consumed. + * Return NULL for out of memory, the variable otherwise. + */ + static VAR +alloc_string_var(s) + char_u *s; +{ + VAR retvar; + + retvar = alloc_var(); + if (retvar != NULL) + { + retvar->var_type = VAR_STRING; + retvar->var_val.var_string = s; + } + else + vim_free(s); + return retvar; +} + +/* + * Free the memory for a variable. + */ + static void +free_var(varp) + VAR varp; +{ + if (varp != NULL) + { + if (varp->var_type == VAR_STRING) + vim_free(varp->var_val.var_string); + vim_free(varp->var_name); + vim_free(varp); + } +} + +/* + * Free the memory for a variable value and set the value to NULL or 0. + */ + static void +clear_var(varp) + VAR varp; +{ + if (varp != NULL) + { + if (varp->var_type == VAR_STRING) + { + vim_free(varp->var_val.var_string); + varp->var_val.var_string = NULL; + } + else + varp->var_val.var_number = 0; + } +} + +/* + * Get the number value of a variable. + * If it is a String variable, uses vim_str2nr(). + */ + static long +get_var_number(varp) + VAR varp; +{ + long n; + + if (varp->var_type == VAR_NUMBER) + return (long)(varp->var_val.var_number); + else if (varp->var_type == VAR_UNKNOWN || varp->var_val.var_string == NULL) + return 0L; + else + { + vim_str2nr(varp->var_val.var_string, NULL, NULL, TRUE, TRUE, &n, NULL); + return n; + } +} + +/* + * Get the lnum from the first argument. Also accepts ".", "$", etc. + */ + static linenr_T +get_var_lnum(argvars) + VAR argvars; +{ + var retvar; + linenr_T lnum; + + lnum = get_var_number(&argvars[0]); + if (lnum == 0) /* no valid number, try using line() */ + { + retvar.var_type = VAR_NUMBER; + f_line(argvars, &retvar); + lnum = retvar.var_val.var_number; + clear_var(&retvar); + } + return lnum; +} + +/* + * Get the string value of a variable. + * If it is a Number variable, the number is converted into a string. + * get_var_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! + * get_var_string_buf() uses a given buffer. + * If the String variable has never been set, return an empty string. + * Never returns NULL; + */ + static char_u * +get_var_string(varp) + VAR varp; +{ + static char_u mybuf[NUMBUFLEN]; + + return get_var_string_buf(varp, mybuf); +} + + static char_u * +get_var_string_buf(varp, buf) + VAR varp; + char_u *buf; +{ + if (varp->var_type == VAR_NUMBER) + { + sprintf((char *)buf, "%ld", (long)varp->var_val.var_number); + return buf; + } + else if (varp->var_val.var_string == NULL) + return (char_u *)""; + else + return varp->var_val.var_string; +} + +/* + * Find variable "name" in the list of variables. + * Return a pointer to it if found, NULL if not found. + */ + static VAR +find_var(name, writing) + char_u *name; + int writing; +{ + int i; + char_u *varname; + garray_T *gap; + + /* Check for function arguments "a:" */ + if (name[0] == 'a' && name[1] == ':') + { + if (writing) + { + EMSG2(_(e_readonlyvar), name); + return NULL; + } + name += 2; + if (current_funccal == NULL) + return NULL; + if (VIM_ISDIGIT(*name)) + { + i = atol((char *)name); + if (i == 0) /* a:0 */ + return ¤t_funccal->a0_var; + i += current_funccal->func->args.ga_len; + if (i > current_funccal->argcount) /* a:999 */ + return NULL; + return &(current_funccal->argvars[i - 1]); /* a:1, a:2, etc. */ + } + if (STRCMP(name, "firstline") == 0) + return &(current_funccal->firstline); + if (STRCMP(name, "lastline") == 0) + return &(current_funccal->lastline); + for (i = 0; i < current_funccal->func->args.ga_len; ++i) + if (STRCMP(name, ((char_u **) + (current_funccal->func->args.ga_data))[i]) == 0) + return &(current_funccal->argvars[i]); /* a:name */ + return NULL; + } + + gap = find_var_ga(name, &varname); + if (gap == NULL) + return NULL; + return find_var_in_ga(gap, varname); +} + + static VAR +find_var_in_ga(gap, varname) + garray_T *gap; + char_u *varname; +{ + int i; + + for (i = gap->ga_len; --i >= 0; ) + if (VAR_GAP_ENTRY(i, gap).var_name != NULL + && STRCMP(VAR_GAP_ENTRY(i, gap).var_name, varname) == 0) + break; + if (i < 0) + return NULL; + return &VAR_GAP_ENTRY(i, gap); +} + +/* + * Find the growarray and start of name without ':' for a variable name. + */ + static garray_T * +find_var_ga(name, varname) + char_u *name; + char_u **varname; +{ + if (name[1] != ':') + { + /* If not "x:name" there must not be any ":" in the name. */ + if (vim_strchr(name, ':') != NULL) + return NULL; + *varname = name; + if (current_funccal == NULL) + return &variables; /* global variable */ + return ¤t_funccal->l_vars; /* local function variable */ + } + *varname = name + 2; + if (*name == 'b') /* buffer variable */ + return &curbuf->b_vars; + if (*name == 'w') /* window variable */ + return &curwin->w_vars; + if (*name == 'g') /* global variable */ + return &variables; + if (*name == 'l' && current_funccal != NULL)/* local function variable */ + return ¤t_funccal->l_vars; + if (*name == 's' /* script variable */ + && current_SID > 0 && current_SID <= ga_scripts.ga_len) + return &SCRIPT_VARS(current_SID); + return NULL; +} + +/* + * Get the string value of a (global/local) variable. + * Returns NULL when it doesn't exist. + */ + char_u * +get_var_value(name) + char_u *name; +{ + VAR v; + + v = find_var(name, FALSE); + if (v == NULL) + return NULL; + return get_var_string(v); +} + +/* + * Allocate a new growarry for a sourced script. It will be used while + * sourcing this script and when executing functions defined in the script. + */ + void +new_script_vars(id) + scid_T id; +{ + if (ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)) == OK) + { + while (ga_scripts.ga_len < id) + { + var_init(&SCRIPT_VARS(ga_scripts.ga_len + 1)); + ++ga_scripts.ga_len; + --ga_scripts.ga_room; + } + } +} + +/* + * Initialize internal variables for use. + */ + void +var_init(gap) + garray_T *gap; +{ + ga_init2(gap, (int)sizeof(var), 4); +} + +/* + * Clean up a list of internal variables. + */ + void +var_clear(gap) + garray_T *gap; +{ + int i; + + for (i = gap->ga_len; --i >= 0; ) + var_free_one(&VAR_GAP_ENTRY(i, gap)); + ga_clear(gap); +} + + static void +var_free_one(v) + VAR v; +{ + vim_free(v->var_name); + v->var_name = NULL; + if (v->var_type == VAR_STRING) + vim_free(v->var_val.var_string); + v->var_val.var_string = NULL; +} + +/* + * List the value of one internal variable. + */ + static void +list_one_var(v, prefix) + VAR v; + char_u *prefix; +{ + list_one_var_a(prefix, v->var_name, v->var_type, get_var_string(v)); +} + +/* + * List the value of one "v:" variable. + */ + static void +list_vim_var(i) + int i; /* index in vimvars[] */ +{ + char_u *p; + char_u numbuf[NUMBUFLEN]; + + if (vimvars[i].type == VAR_NUMBER) + { + p = numbuf; + sprintf((char *)p, "%ld", (long)vimvars[i].val); + } + else if (vimvars[i].val == NULL) + p = (char_u *)""; + else + p = vimvars[i].val; + list_one_var_a((char_u *)"v:", (char_u *)vimvars[i].name, + vimvars[i].type, p); +} + + static void +list_one_var_a(prefix, name, type, string) + char_u *prefix; + char_u *name; + int type; + char_u *string; +{ + msg_attr(prefix, 0); /* don't use msg(), it overwrites "v:statusmsg" */ + if (name != NULL) /* "a:" vars don't have a name stored */ + msg_puts(name); + msg_putchar(' '); + msg_advance(22); + if (type == VAR_NUMBER) + msg_putchar('#'); + else + msg_putchar(' '); + msg_outtrans(string); +} + +/* + * Set variable "name" to value in "varp". + * If the variable already exists, the value is updated. + * Otherwise the variable is created. + */ + static void +set_var(name, varp) + char_u *name; + VAR varp; +{ + int i; + VAR v; + char_u *varname; + garray_T *gap; + + /* + * Handle setting internal v: variables. + */ + i = find_vim_var(name, (int)STRLEN(name)); + if (i >= 0) + { + if (vimvars[i].flags & VV_RO) + EMSG2(_(e_readonlyvar), name); + else + { + if (vimvars[i].type == VAR_STRING) + { + vim_free(vimvars[i].val); + vimvars[i].val = vim_strsave(get_var_string(varp)); + } + else + vimvars[i].val = (char_u *)(long)varp->var_val.var_number; + } + return; + } + + v = find_var(name, TRUE); + if (v != NULL) /* existing variable, only need to free string */ + { + if (v->var_type == VAR_STRING) + vim_free(v->var_val.var_string); + } + else /* add a new variable */ + { + gap = find_var_ga(name, &varname); + if (gap == NULL) /* illegal name */ + { + EMSG2(_("E461: Illegal variable name: %s"), name); + return; + } + + /* Try to use an empty entry */ + for (i = gap->ga_len; --i >= 0; ) + if (VAR_GAP_ENTRY(i, gap).var_name == NULL) + break; + if (i < 0) /* need to allocate more room */ + { + if (ga_grow(gap, 1) == FAIL) + return; + i = gap->ga_len; + } + v = &VAR_GAP_ENTRY(i, gap); + if ((v->var_name = vim_strsave(varname)) == NULL) + return; + if (i == gap->ga_len) + { + ++gap->ga_len; + --gap->ga_room; + } + } + copy_var(varp, v); +} + + static void +copy_var(from, to) + VAR from; + VAR to; +{ + to->var_type = from->var_type; + if (from->var_type == VAR_STRING) + to->var_val.var_string = vim_strsave(get_var_string(from)); + else + to->var_val.var_number = from->var_val.var_number; +} + +/* + * ":echo expr1 ..." print each argument separated with a space, add a + * newline at the end. + * ":echon expr1 ..." print each argument plain. + */ + void +ex_echo(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + var retvar; + char_u *p; + int needclr = TRUE; + int atstart = TRUE; + + if (eap->skip) + ++emsg_skip; + while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) + { + p = arg; + if (eval1(&arg, &retvar, !eap->skip) == FAIL) + { + /* + * Report the invalid expression unless the expression evaluation + * has been cancelled due to an aborting error, an interrupt, or an + * exception. + */ + if (!aborting()) + EMSG2(_(e_invexpr2), p); + break; + } + if (!eap->skip) + { + if (atstart) + { + atstart = FALSE; + /* Call msg_start() after eval1(), evaluating the expression + * may cause a message to appear. */ + if (eap->cmdidx == CMD_echo) + msg_start(); + } + else if (eap->cmdidx == CMD_echo) + msg_puts_attr((char_u *)" ", echo_attr); + for (p = get_var_string(&retvar); *p != NUL && !got_int; ++p) + if (*p == '\n' || *p == '\r' || *p == TAB) + { + if (*p != TAB && needclr) + { + /* remove any text still there from the command */ + msg_clr_eos(); + needclr = FALSE; + } + msg_putchar_attr(*p, echo_attr); + } + else + { +#ifdef FEAT_MBYTE + if (has_mbyte) + { + int i = (*mb_ptr2len_check)(p); + + (void)msg_outtrans_len_attr(p, i, echo_attr); + p += i - 1; + } + else +#endif + (void)msg_outtrans_len_attr(p, 1, echo_attr); + } + } + clear_var(&retvar); + arg = skipwhite(arg); + } + eap->nextcmd = check_nextcmd(arg); + + if (eap->skip) + --emsg_skip; + else + { + /* remove text that may still be there from the command */ + if (needclr) + msg_clr_eos(); + if (eap->cmdidx == CMD_echo) + msg_end(); + } +} + +/* + * ":echohl {name}". + */ + void +ex_echohl(eap) + exarg_T *eap; +{ + int id; + + id = syn_name2id(eap->arg); + if (id == 0) + echo_attr = 0; + else + echo_attr = syn_id2attr(id); +} + +/* + * ":execute expr1 ..." execute the result of an expression. + * ":echomsg expr1 ..." Print a message + * ":echoerr expr1 ..." Print an error + * Each gets spaces around each argument and a newline at the end for + * echo commands + */ + void +ex_execute(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + var retvar; + int ret = OK; + char_u *p; + garray_T ga; + int len; + int save_did_emsg; + + ga_init2(&ga, 1, 80); + + if (eap->skip) + ++emsg_skip; + while (*arg != NUL && *arg != '|' && *arg != '\n') + { + p = arg; + if (eval1(&arg, &retvar, !eap->skip) == FAIL) + { + /* + * Report the invalid expression unless the expression evaluation + * has been cancelled due to an aborting error, an interrupt, or an + * exception. + */ + if (!aborting()) + EMSG2(_(e_invexpr2), p); + ret = FAIL; + break; + } + + if (!eap->skip) + { + p = get_var_string(&retvar); + len = (int)STRLEN(p); + if (ga_grow(&ga, len + 2) == FAIL) + { + clear_var(&retvar); + ret = FAIL; + break; + } + if (ga.ga_len) + { + ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; + --ga.ga_room; + } + STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); + ga.ga_room -= len; + ga.ga_len += len; + } + + clear_var(&retvar); + arg = skipwhite(arg); + } + + if (ret != FAIL && ga.ga_data != NULL) + { + if (eap->cmdidx == CMD_echomsg) + MSG_ATTR(ga.ga_data, echo_attr); + 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); +} + +/* + * Skip over the name of an option: "&option", "&g:option" or "&l:option". + * "arg" points to the "&" or '+' when called, to "option" when returning. + * Returns NULL when no option name found. Otherwise pointer to the char + * after the option name. + */ + static char_u * +find_option_end(arg, opt_flags) + char_u **arg; + int *opt_flags; +{ + char_u *p = *arg; + + ++p; + if (*p == 'g' && p[1] == ':') + { + *opt_flags = OPT_GLOBAL; + p += 2; + } + else if (*p == 'l' && p[1] == ':') + { + *opt_flags = OPT_LOCAL; + p += 2; + } + else + *opt_flags = 0; + + if (!ASCII_ISALPHA(*p)) + return NULL; + *arg = p; + + if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) + p += 4; /* termcap option */ + else + while (ASCII_ISALPHA(*p)) + ++p; + return p; +} + +/* + * ":function" + */ + void +ex_function(eap) + exarg_T *eap; +{ + char_u *theline; + int j; + int c; +#ifdef FEAT_MAGIC_BRACES + int saved_did_emsg; +#endif + char_u *name = NULL; + char_u *p; + char_u *arg; + garray_T newargs; + garray_T newlines; + int varargs = FALSE; + int mustend = FALSE; + int flags = 0; + ufunc_T *fp; + int indent; + int nesting; + char_u *skip_until = NULL; + static char_u e_funcexts[] = N_("E122: Function %s already exists, add ! to replace it"); + + /* + * ":function" without argument: list functions. + */ + if (ends_excmd(*eap->arg)) + { + if (!eap->skip) + for (fp = firstfunc; fp != NULL && !got_int; fp = fp->next) + list_func_head(fp, FALSE); + eap->nextcmd = check_nextcmd(eap->arg); + return; + } + + p = eap->arg; + name = trans_function_name(&p, eap->skip, FALSE); + if (name == NULL && !eap->skip) + { + /* + * Return on an invalid expression in braces, unless the expression + * evaluation has been cancelled due to an aborting error, an + * interrupt, or an exception. + */ + if (!aborting()) + return; + else + eap->skip = TRUE; + } +#ifdef FEAT_MAGIC_BRACES + /* An error in a function call during evaluation of an expression in magic + * braces should not cause the function not to be defined. */ + saved_did_emsg = did_emsg; + did_emsg = FALSE; +#endif + + /* + * ":function func" with only function name: list function. + */ + if (vim_strchr(p, '(') == NULL) + { + if (!ends_excmd(*skipwhite(p))) + { + EMSG(_(e_trailing)); + goto erret_name; + } + eap->nextcmd = check_nextcmd(p); + if (eap->nextcmd != NULL) + *p = NUL; + if (!eap->skip && !got_int) + { + fp = find_func(name); + if (fp != NULL) + { + list_func_head(fp, TRUE); + for (j = 0; j < fp->lines.ga_len && !got_int; ++j) + { + msg_putchar('\n'); + msg_outnum((long)(j + 1)); + if (j < 9) + msg_putchar(' '); + if (j < 99) + msg_putchar(' '); + msg_prt_line(FUNCLINE(fp, j)); + out_flush(); /* show a line at a time */ + ui_breakcheck(); + } + if (!got_int) + { + msg_putchar('\n'); + msg_puts((char_u *)" endfunction"); + } + } + else + EMSG2(_("E123: Undefined function: %s"), eap->arg); + } + goto erret_name; + } + + /* + * ":function name(arg1, arg2)" Define function. + */ + p = skipwhite(p); + if (*p != '(') + { + if (!eap->skip) + { + EMSG2(_("E124: Missing '(': %s"), eap->arg); + goto erret_name; + } + /* attempt to continue by skipping some text */ + if (vim_strchr(p, '(') != NULL) + p = vim_strchr(p, '('); + } + p = skipwhite(p + 1); + + ga_init2(&newargs, (int)sizeof(char_u *), 3); + ga_init2(&newlines, (int)sizeof(char_u *), 3); + + /* + * Isolate the arguments: "arg1, arg2, ...)" + */ + while (*p != ')') + { + if (p[0] == '.' && p[1] == '.' && p[2] == '.') + { + varargs = TRUE; + p += 3; + mustend = TRUE; + } + else + { + arg = p; + while (ASCII_ISALNUM(*p) || *p == '_') + ++p; + if (arg == p || isdigit(*arg) + || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) + || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) + { + if (!eap->skip) + EMSG2(_("E125: Illegal argument: %s"), arg); + break; + } + if (ga_grow(&newargs, 1) == FAIL) + goto erret; + c = *p; + *p = NUL; + arg = vim_strsave(arg); + if (arg == NULL) + goto erret; + ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg; + *p = c; + newargs.ga_len++; + newargs.ga_room--; + if (*p == ',') + ++p; + else + mustend = TRUE; + } + p = skipwhite(p); + if (mustend && *p != ')') + { + if (!eap->skip) + EMSG2(_(e_invarg2), eap->arg); + break; + } + } + ++p; /* skip the ')' */ + + /* find extra arguments "range" and "abort" */ + for (;;) + { + p = skipwhite(p); + if (STRNCMP(p, "range", 5) == 0) + { + flags |= FC_RANGE; + p += 5; + } + else if (STRNCMP(p, "abort", 5) == 0) + { + flags |= FC_ABORT; + p += 5; + } + else + break; + } + + if (*p != NUL && *p != '"' && *p != '\n' && !eap->skip && !did_emsg) + EMSG(_(e_trailing)); + + /* + * Read the body of the function, until ":endfunction" is found. + */ + if (KeyTyped) + { + /* Check if the function already exists, don't let the user type the + * whole function before telling him it doesn't work! For a script we + * need to skip the body to be able to find what follows. */ + if (!eap->skip && !eap->forceit && find_func(name) != NULL) + EMSG2(_(e_funcexts), name); + + msg_putchar('\n'); /* don't overwrite the function name */ + cmdline_row = msg_row; + } + + indent = 2; + nesting = 0; + for (;;) + { + msg_scroll = TRUE; + need_wait_return = FALSE; + if (eap->getline == NULL) + theline = getcmdline(':', 0L, indent); + else + theline = eap->getline(':', eap->cookie, indent); + if (KeyTyped) + lines_left = Rows - 1; + if (theline == NULL) + { + EMSG(_("E126: Missing :endfunction")); + goto erret; + } + + if (skip_until != NULL) + { + /* between ":append" and "." and between ":python <<EOF" and "EOF" + * don't check for ":endfunc". */ + if (STRCMP(theline, skip_until) == 0) + { + vim_free(skip_until); + skip_until = NULL; + } + } + else + { + /* skip ':' and blanks*/ + for (p = theline; vim_iswhite(*p) || *p == ':'; ++p) + ; + + /* Check for "endfunction" (should be more strict...). */ + if (STRNCMP(p, "endf", 4) == 0 && nesting-- == 0) + { + vim_free(theline); + break; + } + + /* Increase indent inside "if", "while", and "try", decrease + * at "end". */ + if (indent > 2 && STRNCMP(p, "end", 3) == 0) + indent -= 2; + else if (STRNCMP(p, "if", 2) == 0 || STRNCMP(p, "wh", 2) == 0 + || STRNCMP(p, "try", 3) == 0) + indent += 2; + + /* Check for defining a function inside this function. */ + if (STRNCMP(p, "fu", 2) == 0) + { + p = skipwhite(skiptowhite(p)); + p += eval_fname_script(p); + if (ASCII_ISALPHA(*p)) + { + vim_free(trans_function_name(&p, TRUE, FALSE)); + if (*skipwhite(p) == '(') + { + ++nesting; + indent += 2; + } + } + } + + /* Check for ":append" or ":insert". */ + p = skip_range(p, NULL); + if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) + || (p[0] == 'i' + && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' + && (!ASCII_ISALPHA(p[2]) || (p[2] == 's')))))) + skip_until = vim_strsave((char_u *)"."); + + /* Check for ":python <<EOF", ":tcl <<EOF", etc. */ + arg = skipwhite(skiptowhite(p)); + if (arg[0] == '<' && arg[1] =='<' + && ((p[0] == 'p' && p[1] == 'y' + && (!ASCII_ISALPHA(p[2]) || p[2] == 't')) + || (p[0] == 'p' && p[1] == 'e' + && (!ASCII_ISALPHA(p[2]) || p[2] == 'r')) + || (p[0] == 't' && p[1] == 'c' + && (!ASCII_ISALPHA(p[2]) || p[2] == 'l')) + || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b' + && (!ASCII_ISALPHA(p[3]) || p[3] == 'y')) + )) + { + /* ":python <<" continues until a dot, like ":append" */ + p = skipwhite(arg + 2); + if (*p == NUL) + skip_until = vim_strsave((char_u *)"."); + else + skip_until = vim_strsave(p); + } + } + + /* Add the line to the function. */ + if (ga_grow(&newlines, 1) == FAIL) + goto erret; + ((char_u **)(newlines.ga_data))[newlines.ga_len] = theline; + newlines.ga_len++; + newlines.ga_room--; + } + + /* Don't define the function when skipping commands or when an error was + * detected. */ + if (eap->skip || did_emsg) + goto erret; + + /* + * If there are no errors, add the function + */ + fp = find_func(name); + if (fp != NULL) + { + if (!eap->forceit) + { + EMSG2(_(e_funcexts), name); + goto erret; + } + if (fp->calls) + { + EMSG2(_("E127: Cannot redefine function %s: It is in use"), name); + goto erret; + } + /* redefine existing function */ + ga_clear_strings(&(fp->args)); + ga_clear_strings(&(fp->lines)); + vim_free(name); + } + else + { + fp = (ufunc_T *)alloc((unsigned)sizeof(ufunc_T)); + if (fp == NULL) + goto erret; + /* insert the new function in the function list */ + fp->next = firstfunc; + firstfunc = fp; + fp->name = name; + } + fp->args = newargs; + fp->lines = newlines; + fp->varargs = varargs; + fp->flags = flags; + fp->calls = 0; + fp->script_ID = current_SID; +#ifdef FEAT_MAGIC_BRACES + did_emsg |= saved_did_emsg; +#endif + vim_free(skip_until); + return; + +erret: + vim_free(skip_until); + ga_clear_strings(&newargs); + ga_clear_strings(&newlines); +erret_name: + vim_free(name); +#ifdef FEAT_MAGIC_BRACES + did_emsg |= saved_did_emsg; +#endif +} + +/* + * Get a function name, translating "<SID>" and "<SNR>". + * Returns the function name in allocated memory, or NULL for failure. + * Advances "pp" to just after the function name (if no error). + */ + static char_u * +trans_function_name(pp, skip, internal) + char_u **pp; + int skip; /* only find the end, don't evaluate */ + int internal; /* TRUE if internal function name OK */ +{ + char_u *name; + char_u *start; + char_u *end; + int lead; + char_u sid_buf[20]; + char_u *temp_string = NULL; + char_u *expr_start, *expr_end; + int len; + + /* A name starting with "<SID>" or "<SNR>" is local to a script. */ + start = *pp; + lead = eval_fname_script(start); + if (lead > 0) + start += lead; + end = find_name_end(start, &expr_start, &expr_end); + if (end == start) + { + if (!skip) + EMSG(_("E129: Function name required")); + return NULL; + } +#ifdef FEAT_MAGIC_BRACES + if (expr_start != NULL && !skip) + { + /* expand magic curlies */ + temp_string = make_expanded_name(start, expr_start, expr_end, end); + if (temp_string == NULL) + { + /* + * Report an invalid expression in braces, unless the expression + * evaluation has been cancelled due to an aborting error, an + * interrupt, or an exception. + */ + if (!aborting()) + EMSG2(_(e_invarg2), start); + else + *pp = end; + return NULL; + } + start = temp_string; + len = (int)STRLEN(temp_string); + } + else +#endif + len = (int)(end - start); + + /* + * Copy the function name to allocated memory. + * Accept <SID>name() inside a script, translate into <SNR>123_name(). + * Accept <SNR>123_name() outside a script. + */ + if (skip) + lead = 0; /* do nothing */ + else if (lead > 0) + { + lead = 3; + if (eval_fname_sid(*pp)) /* If it's "<SID>" */ + { + if (current_SID <= 0) + { + EMSG(_(e_usingsid)); + return NULL; + } + sprintf((char *)sid_buf, "%ld_", (long)current_SID); + lead += (int)STRLEN(sid_buf); + } + } + else if (!internal && !ASCII_ISUPPER(*start)) + { + EMSG2(_("E128: Function name must start with a capital: %s"), start); + return NULL; + } + name = alloc((unsigned)(len + lead + 1)); + if (name != NULL) + { + if (lead > 0) + { + name[0] = K_SPECIAL; + name[1] = KS_EXTRA; + name[2] = (int)KE_SNR; + if (eval_fname_sid(*pp)) /* If it's "<SID>" */ + STRCPY(name + 3, sid_buf); + } + mch_memmove(name + lead, start, (size_t)len); + name[len + lead] = NUL; + } + *pp = end; + + vim_free(temp_string); + return name; +} + +/* + * Return 5 if "p" starts with "<SID>" or "<SNR>" (ignoring case). + * Return 2 if "p" starts with "s:". + * Return 0 otherwise. + */ + static int +eval_fname_script(p) + char_u *p; +{ + if (p[0] == '<' && (STRNICMP(p + 1, "SID>", 4) == 0 + || STRNICMP(p + 1, "SNR>", 4) == 0)) + return 5; + if (p[0] == 's' && p[1] == ':') + return 2; + return 0; +} + +/* + * Return TRUE if "p" starts with "<SID>" or "s:". + * Only works if eval_fname_script() returned non-zero for "p"! + */ + static int +eval_fname_sid(p) + char_u *p; +{ + return (*p == 's' || TOUPPER_ASC(p[2]) == 'I'); +} + +/* + * List the head of the function: "name(arg1, arg2)". + */ + static void +list_func_head(fp, indent) + ufunc_T *fp; + int indent; +{ + int j; + + msg_start(); + if (indent) + MSG_PUTS(" "); + MSG_PUTS("function "); + if (fp->name[0] == K_SPECIAL) + { + MSG_PUTS_ATTR("<SNR>", hl_attr(HLF_8)); + msg_puts(fp->name + 3); + } + else + msg_puts(fp->name); + msg_putchar('('); + for (j = 0; j < fp->args.ga_len; ++j) + { + if (j) + MSG_PUTS(", "); + msg_puts(FUNCARG(fp, j)); + } + if (fp->varargs) + { + if (j) + MSG_PUTS(", "); + MSG_PUTS("..."); + } + msg_putchar(')'); +} + +/* + * Find a function by name, return pointer to it in ufuncs. + * Return NULL for unknown function. + */ + static ufunc_T * +find_func(name) + char_u *name; +{ + ufunc_T *fp; + + for (fp = firstfunc; fp != NULL; fp = fp->next) + if (STRCMP(name, fp->name) == 0) + break; + return fp; +} + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * Function given to ExpandGeneric() to obtain the list of user defined + * function names. + */ + char_u * +get_user_func_name(xp, idx) + expand_T *xp; + int idx; +{ + static ufunc_T *fp = NULL; + + if (idx == 0) + fp = firstfunc; + if (fp != NULL) + { + if (STRLEN(fp->name) + 4 >= IOSIZE) + return fp->name; /* prevents overflow */ + + cat_func_name(IObuff, fp); + if (xp->xp_context != EXPAND_USER_FUNC) + { + STRCAT(IObuff, "("); + if (!fp->varargs && fp->args.ga_len == 0) + STRCAT(IObuff, ")"); + } + + fp = fp->next; + return IObuff; + } + return NULL; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * Copy the function name of "fp" to buffer "buf". + * "buf" must be able to hold the function name plus three bytes. + * Takes care of script-local function names. + */ + static void +cat_func_name(buf, fp) + char_u *buf; + ufunc_T *fp; +{ + if (fp->name[0] == K_SPECIAL) + { + STRCPY(buf, "<SNR>"); + STRCAT(buf, fp->name + 3); + } + else + STRCPY(buf, fp->name); +} + +/* + * ":delfunction {name}" + */ + void +ex_delfunction(eap) + exarg_T *eap; +{ + ufunc_T *fp = NULL, *pfp; + char_u *p; + char_u *name; + + p = eap->arg; + name = trans_function_name(&p, eap->skip, FALSE); + if (name == NULL) + return; + if (!ends_excmd(*skipwhite(p))) + { + vim_free(name); + EMSG(_(e_trailing)); + return; + } + eap->nextcmd = check_nextcmd(p); + if (eap->nextcmd != NULL) + *p = NUL; + + if (!eap->skip) + fp = find_func(name); + vim_free(name); + + if (!eap->skip) + { + if (fp == NULL) + { + EMSG2(_("E130: Undefined function: %s"), eap->arg); + return; + } + if (fp->calls) + { + EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg); + return; + } + + /* clear this function */ + vim_free(fp->name); + ga_clear_strings(&(fp->args)); + ga_clear_strings(&(fp->lines)); + + /* remove the function from the function list */ + if (firstfunc == fp) + firstfunc = fp->next; + else + { + for (pfp = firstfunc; pfp != NULL; pfp = pfp->next) + if (pfp->next == fp) + { + pfp->next = fp->next; + break; + } + } + vim_free(fp); + } +} + +/* + * Call a user function. + */ + static void +call_user_func(fp, argcount, argvars, retvar, firstline, lastline) + ufunc_T *fp; /* pointer to function */ + int argcount; /* nr of args */ + VAR argvars; /* arguments */ + VAR retvar; /* return value */ + linenr_T firstline; /* first line of range */ + linenr_T lastline; /* last line of range */ +{ + char_u *save_sourcing_name; + linenr_T save_sourcing_lnum; + scid_T save_current_SID; + struct funccall fc; + struct funccall *save_fcp = current_funccal; + int save_did_emsg; + static int depth = 0; + + /* If depth of calling is getting too high, don't execute the function */ + if (depth >= p_mfd) + { + EMSG(_("E132: Function call depth is higher than 'maxfuncdepth'")); + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = -1; + return; + } + ++depth; + + line_breakcheck(); /* check for CTRL-C hit */ + + /* set local variables */ + var_init(&fc.l_vars); + fc.func = fp; + fc.argcount = argcount; + fc.argvars = argvars; + fc.retvar = retvar; + retvar->var_val.var_number = 0; + fc.linenr = 0; + fc.returned = FALSE; + fc.level = ex_nesting_level; + fc.a0_var.var_type = VAR_NUMBER; + fc.a0_var.var_val.var_number = argcount - fp->args.ga_len; + fc.a0_var.var_name = NULL; + current_funccal = &fc; + fc.firstline.var_type = VAR_NUMBER; + fc.firstline.var_val.var_number = firstline; + fc.firstline.var_name = NULL; + fc.lastline.var_type = VAR_NUMBER; + fc.lastline.var_val.var_number = lastline; + fc.lastline.var_name = NULL; + /* Check if this function has a breakpoint. */ + fc.breakpoint = dbg_find_breakpoint(FALSE, fp->name, (linenr_T)0); + fc.dbg_tick = debug_tick; + + /* Don't redraw while executing the function. */ + ++RedrawingDisabled; + save_sourcing_name = sourcing_name; + save_sourcing_lnum = sourcing_lnum; + sourcing_lnum = 1; + sourcing_name = alloc((unsigned)((save_sourcing_name == NULL ? 0 + : STRLEN(save_sourcing_name)) + STRLEN(fp->name) + 13)); + if (sourcing_name != NULL) + { + if (save_sourcing_name != NULL + && STRNCMP(save_sourcing_name, "function ", 9) == 0) + sprintf((char *)sourcing_name, "%s..", save_sourcing_name); + else + STRCPY(sourcing_name, "function "); + cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); + + if (p_verbose >= 12) + { + ++no_wait_return; + msg_scroll = TRUE; /* always scroll up, don't overwrite */ + msg_str((char_u *)_("calling %s"), sourcing_name); + if (p_verbose >= 14) + { + int i; + char_u buf[MSG_BUF_LEN]; + + msg_puts((char_u *)"("); + for (i = 0; i < argcount; ++i) + { + if (i > 0) + msg_puts((char_u *)", "); + if (argvars[i].var_type == VAR_NUMBER) + msg_outnum((long)argvars[i].var_val.var_number); + else + { + trunc_string(get_var_string(&argvars[i]), + buf, MSG_BUF_LEN); + msg_puts((char_u *)"\""); + msg_puts(buf); + msg_puts((char_u *)"\""); + } + } + msg_puts((char_u *)")"); + } + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + cmdline_row = msg_row; + --no_wait_return; + } + } + save_current_SID = current_SID; + current_SID = fp->script_ID; + save_did_emsg = did_emsg; + did_emsg = FALSE; + + /* call do_cmdline() to execute the lines */ + do_cmdline(NULL, get_func_line, (void *)&fc, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + + --RedrawingDisabled; + + /* when the function was aborted because of an error, return -1 */ + if ((did_emsg && (fp->flags & FC_ABORT)) || retvar->var_type == VAR_UNKNOWN) + { + clear_var(retvar); + retvar->var_type = VAR_NUMBER; + retvar->var_val.var_number = -1; + } + + /* when being verbose, mention the return value */ + if (p_verbose >= 12) + { + char_u *sn, *val; + + ++no_wait_return; + msg_scroll = TRUE; /* always scroll up, don't overwrite */ + + /* Make sure the output fits in IObuff. */ + sn = sourcing_name; + if (STRLEN(sourcing_name) > IOSIZE / 2 - 50) + sn = sourcing_name + STRLEN(sourcing_name) - (IOSIZE / 2 - 50); + + if (aborting()) + smsg((char_u *)_("%s aborted"), sn); + else if (fc.retvar->var_type == VAR_NUMBER) + smsg((char_u *)_("%s returning #%ld"), sn, + (long)fc.retvar->var_val.var_number); + else if (fc.retvar->var_type == VAR_STRING) + { + val = get_var_string(fc.retvar); + if (STRLEN(val) > IOSIZE / 2 - 50) + val = val + STRLEN(val) - (IOSIZE / 2 - 50); + smsg((char_u *)_("%s returning \"%s\""), sn, val); + } + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + cmdline_row = msg_row; + --no_wait_return; + } + + vim_free(sourcing_name); + sourcing_name = save_sourcing_name; + sourcing_lnum = save_sourcing_lnum; + current_SID = save_current_SID; + + if (p_verbose >= 12 && sourcing_name != NULL) + { + ++no_wait_return; + msg_scroll = TRUE; /* always scroll up, don't overwrite */ + msg_str((char_u *)_("continuing in %s"), sourcing_name); + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + cmdline_row = msg_row; + --no_wait_return; + } + + did_emsg |= save_did_emsg; + current_funccal = save_fcp; + + var_clear(&fc.l_vars); /* free all local variables */ + --depth; +} + +/* + * ":return [expr]" + */ + void +ex_return(eap) + exarg_T *eap; +{ + char_u *arg = eap->arg; + var retvar; + int returning = FALSE; + + if (current_funccal == NULL) + { + EMSG(_("E133: :return not inside a function")); + return; + } + + if (eap->skip) + ++emsg_skip; + + eap->nextcmd = NULL; + if ((*arg != NUL && *arg != '|' && *arg != '\n') + && eval0(arg, &retvar, &eap->nextcmd, !eap->skip) != FAIL) + { + if (!eap->skip) + returning = do_return(eap, FALSE, TRUE, &retvar); + else + clear_var(&retvar); + } + /* It's safer to return also on error. */ + else if (!eap->skip) + { + /* + * Return unless the expression evaluation has been cancelled due to an + * aborting error, an interrupt, or an exception. + */ + if (!aborting()) + returning = do_return(eap, FALSE, TRUE, NULL); + } + + /* When skipping or the return gets pending, advance to the next command + * in this line (!returning). Otherwise, ignore the rest of the line. + * Following lines will be ignored by get_func_line(). */ + if (returning) + eap->nextcmd = NULL; + else if (eap->nextcmd == NULL) /* no argument */ + eap->nextcmd = check_nextcmd(arg); + + if (eap->skip) + --emsg_skip; +} + +/* + * Return from a function. Possibly makes the return pending. Also called + * for a pending return at the ":endtry" or after returning from an extra + * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set + * when called due to a ":return" command. "value" may point to a variable + * with the return value. Returns TRUE when the return can be carried out, + * FALSE when the return gets pending. + */ + int +do_return(eap, reanimate, is_cmd, value) + exarg_T *eap; + int reanimate; + int is_cmd; + void *value; +{ + int idx; + struct condstack *cstack = eap->cstack; + + if (reanimate) + /* Undo the return. */ + current_funccal->returned = FALSE; + + /* + * Cleanup (and inactivate) conditionals, but stop when a try conditional + * not in its finally clause (which then is to be executed next) is found. + * In this case, make the ":return" pending for execution at the ":endtry". + * Otherwise, return normally. + */ + idx = cleanup_conditionals(eap->cstack, 0, TRUE); + if (idx >= 0) + { + cstack->cs_pending[idx] = CSTP_RETURN; + + if (!is_cmd && !reanimate) + /* A pending return again gets pending. "value" points to an + * allocated variable with the value of the original ":return"'s + * argument if present or is NULL else. */ + cstack->cs_retvar[idx] = value; + else + { + /* When undoing a return in order to make it pending, get the stored + * return value. */ + if (reanimate) + value = current_funccal->retvar; + + if (value != NULL) + { + /* Store the value of the pending return. */ + if ((cstack->cs_retvar[idx] = alloc_var()) != NULL) + *(VAR)cstack->cs_retvar[idx] = *(VAR)value; + else + EMSG(_(e_outofmem)); + } + else + cstack->cs_retvar[idx] = NULL; + + if (reanimate) + { + /* The pending return value could be overwritten by a ":return" + * without argument in a finally clause; reset the default + * return value. */ + current_funccal->retvar->var_type = VAR_NUMBER; + current_funccal->retvar->var_val.var_number = 0; + } + } + report_make_pending(CSTP_RETURN, value); + } + else + { + current_funccal->returned = TRUE; + + /* If the return is carried out now, store the return value. For + * a return immediately after reanimation, the value is already + * there. */ + if (!reanimate && value != NULL) + { + clear_var(current_funccal->retvar); + *current_funccal->retvar = *(VAR)value; + if (!is_cmd) + vim_free(value); + } + } + + return idx < 0; +} + +/* + * Free the variable with a pending return value. + */ + void +discard_pending_return(retvar) + void *retvar; +{ + /* The variable was copied from one with an undefined var_name. So we can't + * use free_var() to clear and free it. */ + clear_var((VAR)retvar); + vim_free(retvar); +} + +/* + * Generate a return command for producing the value of "retvar". The result + * is an allocated string. Used by report_pending() for verbose messages. + */ + char_u * +get_return_cmd(retvar) + void *retvar; +{ + char_u *s = IObuff; + + if (retvar == NULL || ((VAR)retvar)->var_type == VAR_UNKNOWN) + s = (char_u *)":return"; + else if (((VAR)retvar)->var_type == VAR_STRING) + sprintf((char *)IObuff, ":return \"%s\"", + ((VAR)retvar)->var_val.var_string); + else + sprintf((char *)IObuff, ":return %ld", + (long)(((VAR)retvar)->var_val.var_number)); + return vim_strsave(s); +} + +/* + * Get next function line. + * Called by do_cmdline() to get the next line. + * Returns allocated string, or NULL for end of function. + */ +/* ARGSUSED */ + char_u * +get_func_line(c, cookie, indent) + int c; /* not used */ + void *cookie; + int indent; /* not used */ +{ + struct funccall *fcp = (struct funccall *)cookie; + char_u *retval; + garray_T *gap; /* growarray with function lines */ + + /* If breakpoints have been added/deleted need to check for it. */ + if (fcp->dbg_tick != debug_tick) + { + fcp->breakpoint = dbg_find_breakpoint(FALSE, fcp->func->name, + sourcing_lnum); + fcp->dbg_tick = debug_tick; + } + + gap = &fcp->func->lines; + if ((fcp->func->flags & FC_ABORT) && did_emsg && !aborted_in_try()) + retval = NULL; + else if (fcp->returned || fcp->linenr >= gap->ga_len) + retval = NULL; + else + { + retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); + sourcing_lnum = fcp->linenr; + } + + /* Did we encounter a breakpoint? */ + if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) + { + dbg_breakpoint(fcp->func->name, sourcing_lnum); + /* Find next breakpoint. */ + fcp->breakpoint = dbg_find_breakpoint(FALSE, fcp->func->name, + sourcing_lnum); + fcp->dbg_tick = debug_tick; + } + + return retval; +} + +/* + * Return TRUE if the currently active function should be ended, because a + * return was encountered or an error occured. Used inside a ":while". + */ + int +func_has_ended(cookie) + void *cookie; +{ + struct funccall *fcp = (struct funccall *)cookie; + + /* Ignore the "abort" flag if the abortion behavior has been changed due to + * an error inside a try conditional. */ + return (((fcp->func->flags & FC_ABORT) && did_emsg && !aborted_in_try()) + || fcp->returned); +} + +/* + * return TRUE if cookie indicates a function which "abort"s on errors. + */ + int +func_has_abort(cookie) + void *cookie; +{ + return ((struct funccall *)cookie)->func->flags & FC_ABORT; +} + +#if defined(FEAT_VIMINFO) || defined(FEAT_SESSION) +typedef enum +{ + VAR_FLAVOUR_DEFAULT, + VAR_FLAVOUR_SESSION, + VAR_FLAVOUR_VIMINFO +} var_flavour_T; + +static var_flavour_T var_flavour __ARGS((char_u *varname)); + + static var_flavour_T +var_flavour(varname) + char_u *varname; +{ + char_u *p = varname; + + if (ASCII_ISUPPER(*p)) + { + while (*(++p)) + if (ASCII_ISLOWER(*p)) + return VAR_FLAVOUR_SESSION; + return VAR_FLAVOUR_VIMINFO; + } + else + return VAR_FLAVOUR_DEFAULT; +} +#endif + +#if defined(FEAT_VIMINFO) || defined(PROTO) +/* + * Restore global vars that start with a capital from the viminfo file + */ + int +read_viminfo_varlist(virp, writing) + vir_T *virp; + int writing; +{ + char_u *tab; + int is_string = FALSE; + VAR varp = NULL; + char_u *val; + + if (!writing && (find_viminfo_parameter('!') != NULL)) + { + tab = vim_strchr(virp->vir_line + 1, '\t'); + if (tab != NULL) + { + *tab++ = '\0'; /* isolate the variable name */ + if (*tab == 'S') /* string var */ + is_string = TRUE; + + tab = vim_strchr(tab, '\t'); + if (tab != NULL) + { + /* create a nameless variable to hold the value */ + if (is_string) + { + val = viminfo_readstring(virp, + (int)(tab - virp->vir_line + 1), TRUE); + if (val != NULL) + varp = alloc_string_var(val); + } + else + { + varp = alloc_var(); + if (varp != NULL) + { + varp->var_type = VAR_NUMBER; + varp->var_val.var_number = atol((char *)tab + 1); + } + } + /* assign the value to the variable */ + if (varp != NULL) + { + set_var(virp->vir_line + 1, varp); + free_var(varp); + } + } + } + } + + return viminfo_readline(virp); +} + +/* + * Write global vars that start with a capital to the viminfo file + */ + void +write_viminfo_varlist(fp) + FILE *fp; +{ + garray_T *gap = &variables; /* global variable */ + VAR this_var; + int i; + + if (find_viminfo_parameter('!') == NULL) + return; + + fprintf(fp, _("\n# global variables:\n")); + for (i = gap->ga_len; --i >= 0; ) + { + this_var = &VAR_GAP_ENTRY(i, gap); + if (this_var->var_name != NULL + && var_flavour(this_var->var_name) == VAR_FLAVOUR_VIMINFO) + { + fprintf(fp, "!%s\t%s\t", this_var->var_name, + (this_var->var_type == VAR_STRING) ? "STR" : "NUM"); + viminfo_writestring(fp, get_var_string(this_var)); + } + } +} +#endif + +#if defined(FEAT_SESSION) || defined(PROTO) + int +store_session_globals(fd) + FILE *fd; +{ + garray_T *gap = &variables; /* global variable */ + VAR this_var; + int i; + char_u *p, *t; + + for (i = gap->ga_len; --i >= 0; ) + { + this_var = &VAR_GAP_ENTRY(i, gap); + if (this_var->var_name != NULL) + { + if (var_flavour(this_var->var_name) == VAR_FLAVOUR_SESSION) + { + /* Escapse special characters with a backslash. Turn a LF and + * CR into \n and \r. */ + p = vim_strsave_escaped(get_var_string(this_var), + (char_u *)"\\\"\n\r"); + if (p == NULL) /* out of memory */ + continue; + for (t = p; *t != NUL; ++t) + if (*t == '\n') + *t = 'n'; + else if (*t == '\r') + *t = 'r'; + if ((fprintf(fd, "let %s = %c%s%c", + this_var->var_name, + (this_var->var_type == VAR_STRING) ? '"' : ' ', + p, + (this_var->var_type == VAR_STRING) ? '"' : ' ') < 0) + || put_eol(fd) == FAIL) + { + vim_free(p); + return FAIL; + } + vim_free(p); + } + + } + } + return OK; +} +#endif + +#endif /* FEAT_EVAL */ + +#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO) + + +#ifdef WIN3264 +/* + * Functions for ":8" filename modifier: get 8.3 version of a filename. + */ +static int get_short_pathname __ARGS((char_u **fnamep, char_u **bufp, int *fnamelen)); +static int shortpath_for_invalid_fname __ARGS((char_u **fname, char_u **bufp, int *fnamelen)); +static int shortpath_for_partial __ARGS((char_u **fnamep, char_u **bufp, int *fnamelen)); + +/* + * Get the short pathname of a file. + * Returns 1 on success. *fnamelen is 0 for nonexistant path. + */ + static int +get_short_pathname(fnamep, bufp, fnamelen) + char_u **fnamep; + char_u **bufp; + int *fnamelen; +{ + int l,len; + char_u *newbuf; + + len = *fnamelen; + + l = GetShortPathName(*fnamep, *fnamep, len); + if (l > len - 1) + { + /* If that doesn't work (not enough space), then save the string + * and try again with a new buffer big enough + */ + newbuf = vim_strnsave(*fnamep, l); + if (newbuf == NULL) + return 0; + + vim_free(*bufp); + *fnamep = *bufp = newbuf; + + l = GetShortPathName(*fnamep,*fnamep,l+1); + + /* Really should always succeed, as the buffer is big enough */ + } + + *fnamelen = l; + return 1; +} + +/* + * Create a short path name. Returns the length of the buffer it needs. + * Doesn't copy over the end of the buffer passed in. + */ + static int +shortpath_for_invalid_fname(fname, bufp, fnamelen) + char_u **fname; + char_u **bufp; + int *fnamelen; +{ + char_u *s, *p, *pbuf2, *pbuf3; + char_u ch; + int l,len,len2,plen,slen; + + /* Make a copy */ + len2 = *fnamelen; + pbuf2 = vim_strnsave(*fname, len2); + pbuf3 = NULL; + + s = pbuf2 + len2 - 1; /* Find the end */ + slen = 1; + plen = len2; + + l = 0; + if (vim_ispathsep(*s)) + { + --s; + ++slen; + --plen; + } + + do + { + /* Go back one path-seperator */ + while (s > pbuf2 && !vim_ispathsep(*s)) + { + --s; + ++slen; + --plen; + } + if (s <= pbuf2) + break; + + /* Remeber the character that is about to be blatted */ + ch = *s; + *s = 0; /* get_short_pathname requires a null-terminated string */ + + /* Try it in situ */ + p = pbuf2; + if (!get_short_pathname(&p, &pbuf3, &plen)) + { + vim_free(pbuf2); + return -1; + } + *s = ch; /* Preserve the string */ + } while (plen == 0); + + if (plen > 0) + { + /* Remeber the length of the new string. */ + *fnamelen = len = plen + slen; + vim_free(*bufp); + if (len > len2) + { + /* If there's not enough space in the currently allocated string, + * then copy it to a buffer big enough. + */ + *fname= *bufp = vim_strnsave(p, len); + if (*fname == NULL) + return -1; + } + else + { + /* Transfer pbuf2 to being the main buffer (it's big enough) */ + *fname = *bufp = pbuf2; + if (p != pbuf2) + strncpy(*fname, p, plen); + pbuf2 = NULL; + } + /* Concat the next bit */ + strncpy(*fname + plen, s, slen); + (*fname)[len] = '\0'; + } + vim_free(pbuf3); + vim_free(pbuf2); + return 0; +} + +/* + * Get a pathname for a partial path. + */ + static int +shortpath_for_partial(fnamep, bufp, fnamelen) + char_u **fnamep; + char_u **bufp; + int *fnamelen; +{ + int sepcount, len, tflen; + char_u *p; + char_u *pbuf, *tfname; + int hasTilde; + + /* Count up the path seperators from the RHS.. so we know which part + * of the path to return. + */ + sepcount = 0; + for (p = *fnamep + *fnamelen - 1; p >= *fnamep; --p) + if (vim_ispathsep(*p)) + ++sepcount; + + /* Need full path first (use expand_env() to remove a "~/") */ + hasTilde = (**fnamep == '~'); + if (hasTilde) + pbuf = tfname = expand_env_save(*fnamep); + else + pbuf = tfname = FullName_save(*fnamep, FALSE); + + len = tflen = STRLEN(tfname); + + if (!get_short_pathname(&tfname, &pbuf, &len)) + return -1; + + if (len == 0) + { + /* Don't have a valid filename, so shorten the rest of the + * path if we can. This CAN give us invalid 8.3 filenames, but + * there's not a lot of point in guessing what it might be. + */ + len = tflen; + if (shortpath_for_invalid_fname(&tfname, &pbuf, &len) == -1) + return -1; + } + + /* Count the paths backward to find the beginning of the desired string. */ + for (p = tfname + len - 1; p >= tfname; --p) + if (vim_ispathsep(*p)) + { + if (sepcount == 0 || (hasTilde && sepcount == 1)) + break; + else + sepcount --; + } + if (hasTilde) + { + --p; + if (p >= tfname) + *p = '~'; + else + return -1; + } + else + ++p; + + /* Copy in the string - p indexes into tfname - allocated at pbuf */ + vim_free(*bufp); + *fnamelen = (int)STRLEN(p); + *bufp = pbuf; + *fnamep = p; + + return 0; +} +#endif /* WIN3264 */ + +/* + * Adjust a filename, according to a string of modifiers. + * *fnamep must be NUL terminated when called. When returning, the length is + * determined by *fnamelen. + * Returns valid flags. + * When there is an error, *fnamep is set to NULL. + */ + int +modify_fname(src, usedlen, fnamep, bufp, fnamelen) + char_u *src; /* string with modifiers */ + int *usedlen; /* characters after src that are used */ + char_u **fnamep; /* file name so far */ + char_u **bufp; /* buffer for allocated file name or NULL */ + int *fnamelen; /* length of fnamep */ +{ + int valid = 0; + char_u *tail; + char_u *s, *p, *pbuf; + char_u dirname[MAXPATHL]; + int c; + int has_fullname = 0; +#ifdef WIN3264 + int has_shortname = 0; +#endif + +repeat: + /* ":p" - full path/file_name */ + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') + { + has_fullname = 1; + + valid |= VALID_PATH; + *usedlen += 2; + + /* Expand "~/path" for all systems and "~user/path" for Unix and VMS */ + if ((*fnamep)[0] == '~' +#if !defined(UNIX) && !(defined(VMS) && defined(USER_HOME)) + && ((*fnamep)[1] == '/' +# ifdef BACKSLASH_IN_FILENAME + || (*fnamep)[1] == '\\' +# endif + || (*fnamep)[1] == NUL) + +#endif + ) + { + *fnamep = expand_env_save(*fnamep); + vim_free(*bufp); /* free any allocated file name */ + *bufp = *fnamep; + if (*fnamep == NULL) + return -1; + } + + /* When "/." or "/.." is used: force expansion to get rid of it. */ + for (p = *fnamep; *p != NUL; ++p) + { + if (vim_ispathsep(*p) + && p[1] == '.' + && (p[2] == NUL + || vim_ispathsep(p[2]) + || (p[2] == '.' + && (p[3] == NUL || vim_ispathsep(p[3]))))) + break; + } + + /* FullName_save() is slow, don't use it when not needed. */ + if (*p != NUL || !vim_isAbsName(*fnamep)) + { + *fnamep = FullName_save(*fnamep, *p != NUL); + vim_free(*bufp); /* free any allocated file name */ + *bufp = *fnamep; + if (*fnamep == NULL) + return -1; + } + + /* Append a path separator to a directory. */ + if (mch_isdir(*fnamep)) + { + /* Make room for one or two extra characters. */ + *fnamep = vim_strnsave(*fnamep, (int)STRLEN(*fnamep) + 2); + vim_free(*bufp); /* free any allocated file name */ + *bufp = *fnamep; + if (*fnamep == NULL) + return -1; + add_pathsep(*fnamep); + } + } + + /* ":." - path relative to the current directory */ + /* ":~" - path relative to the home directory */ + /* ":8" - shortname path - postponed till after */ + while (src[*usedlen] == ':' + && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8')) + { + *usedlen += 2; + if (c == '8') + { +#ifdef WIN3264 + has_shortname = 1; /* Postpone this. */ +#endif + continue; + } + pbuf = NULL; + /* Need full path first (use expand_env() to remove a "~/") */ + if (!has_fullname) + { + if (c == '.' && **fnamep == '~') + p = pbuf = expand_env_save(*fnamep); + else + p = pbuf = FullName_save(*fnamep, FALSE); + } + else + p = *fnamep; + + has_fullname = 0; + + if (p != NULL) + { + if (c == '.') + { + mch_dirname(dirname, MAXPATHL); + s = shorten_fname(p, dirname); + if (s != NULL) + { + *fnamep = s; + if (pbuf != NULL) + { + vim_free(*bufp); /* free any allocated file name */ + *bufp = pbuf; + pbuf = NULL; + } + } + } + else + { + home_replace(NULL, p, dirname, MAXPATHL, TRUE); + /* Only replace it when it starts with '~' */ + if (*dirname == '~') + { + s = vim_strsave(dirname); + if (s != NULL) + { + *fnamep = s; + vim_free(*bufp); + *bufp = s; + } + } + } + vim_free(pbuf); + } + } + + tail = gettail(*fnamep); + *fnamelen = (int)STRLEN(*fnamep); + + /* ":h" - head, remove "/file_name", can be repeated */ + /* Don't remove the first "/" or "c:\" */ + while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') + { + valid |= VALID_HEAD; + *usedlen += 2; + s = get_past_head(*fnamep); + while (tail > s && vim_ispathsep(tail[-1])) + --tail; + *fnamelen = (int)(tail - *fnamep); +#ifdef VMS + if (*fnamelen > 0) + *fnamelen += 1; /* the path separator is part of the path */ +#endif + while (tail > s && !vim_ispathsep(tail[-1])) + --tail; + } + + /* ":8" - shortname */ + if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') + { + *usedlen += 2; +#ifdef WIN3264 + has_shortname = 1; +#endif + } + +#ifdef WIN3264 + /* Check shortname after we have done 'heads' and before we do 'tails' + */ + if (has_shortname) + { + pbuf = NULL; + /* Copy the string if it is shortened by :h */ + if (*fnamelen < (int)STRLEN(*fnamep)) + { + p = vim_strnsave(*fnamep, *fnamelen); + if (p == 0) + return -1; + vim_free(*bufp); + *bufp = *fnamep = p; + } + + /* Split into two implementations - makes it easier. First is where + * there isn't a full name already, second is where there is. + */ + if (!has_fullname && !vim_isAbsName(*fnamep)) + { + if (shortpath_for_partial(fnamep, bufp, fnamelen) == -1) + return -1; + } + else + { + int l; + + /* Simple case, already have the full-name + * Nearly always shorter, so try first time. */ + l = *fnamelen; + if (!get_short_pathname(fnamep, bufp, &l)) + return -1; + + if (l == 0) + { + /* Couldn't find the filename.. search the paths. + */ + l = *fnamelen; + if (shortpath_for_invalid_fname(fnamep, bufp, &l ) == -1) + return -1; + } + *fnamelen = l; + } + } +#endif /* WIN3264 */ + + /* ":t" - tail, just the basename */ + if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') + { + *usedlen += 2; + *fnamelen -= (int)(tail - *fnamep); + *fnamep = tail; + } + + /* ":e" - extension, can be repeated */ + /* ":r" - root, without extension, can be repeated */ + while (src[*usedlen] == ':' + && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) + { + /* find a '.' in the tail: + * - for second :e: before the current fname + * - otherwise: The last '.' + */ + if (src[*usedlen + 1] == 'e' && *fnamep > tail) + s = *fnamep - 2; + else + s = *fnamep + *fnamelen - 1; + for ( ; s > tail; --s) + if (s[0] == '.') + break; + if (src[*usedlen + 1] == 'e') /* :e */ + { + if (s > tail) + { + *fnamelen += (int)(*fnamep - (s + 1)); + *fnamep = s + 1; +#ifdef VMS + /* cut version from the extension */ + s = *fnamep + *fnamelen - 1; + for ( ; s > *fnamep; --s) + if (s[0] == ';') + break; + if (s > *fnamep) + *fnamelen = s - *fnamep; +#endif + } + else if (*fnamep <= tail) + *fnamelen = 0; + } + else /* :r */ + { + if (s > tail) /* remove one extension */ + *fnamelen = (int)(s - *fnamep); + } + *usedlen += 2; + } + + /* ":s?pat?foo?" - substitute */ + /* ":gs?pat?foo?" - global substitute */ + if (src[*usedlen] == ':' + && (src[*usedlen + 1] == 's' + || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) + { + char_u *str; + char_u *pat; + char_u *sub; + int sep; + char_u *flags; + int didit = FALSE; + + flags = (char_u *)""; + s = src + *usedlen + 2; + if (src[*usedlen + 1] == 'g') + { + flags = (char_u *)"g"; + ++s; + } + + sep = *s++; + if (sep) + { + /* find end of pattern */ + p = vim_strchr(s, sep); + if (p != NULL) + { + pat = vim_strnsave(s, (int)(p - s)); + if (pat != NULL) + { + s = p + 1; + /* find end of substitution */ + p = vim_strchr(s, sep); + if (p != NULL) + { + sub = vim_strnsave(s, (int)(p - s)); + str = vim_strnsave(*fnamep, *fnamelen); + if (sub != NULL && str != NULL) + { + *usedlen = (int)(p + 1 - src); + s = do_string_sub(str, pat, sub, flags); + if (s != NULL) + { + *fnamep = s; + *fnamelen = (int)STRLEN(s); + vim_free(*bufp); + *bufp = s; + didit = TRUE; + } + } + vim_free(sub); + vim_free(str); + } + vim_free(pat); + } + } + /* after using ":s", repeat all the modifiers */ + if (didit) + goto repeat; + } + } + + return valid; +} + +/* + * Perform a substitution on "str" with pattern "pat" and substitute "sub". + * "flags" can be "g" to do a global substitute. + * Returns an allocated string, NULL for error. + */ + char_u * +do_string_sub(str, pat, sub, flags) + char_u *str; + char_u *pat; + char_u *sub; + char_u *flags; +{ + int sublen; + regmatch_T regmatch; + int i; + int do_all; + char_u *tail; + garray_T ga; + char_u *ret; + char_u *save_cpo; + + /* Make 'cpoptions' empty, so that the 'l' flag doesn't work here */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + + ga_init2(&ga, 1, 200); + + do_all = (flags[0] == 'g'); + + regmatch.rm_ic = p_ic; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + tail = str; + while (vim_regexec_nl(®match, str, (colnr_T)(tail - str))) + { + /* + * Get some space for a temporary buffer to do the substitution + * into. It will contain: + * - The text up to where the match is. + * - The substituted text. + * - The text after the match. + */ + sublen = vim_regsub(®match, sub, tail, FALSE, TRUE, FALSE); + if (ga_grow(&ga, (int)(STRLEN(tail) + sublen - + (regmatch.endp[0] - regmatch.startp[0]))) == FAIL) + { + ga_clear(&ga); + break; + } + + /* copy the text up to where the match is */ + i = (int)(regmatch.startp[0] - tail); + mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + /* add the substituted text */ + (void)vim_regsub(®match, sub, (char_u *)ga.ga_data + + ga.ga_len + i, TRUE, TRUE, FALSE); + ga.ga_len += i + sublen - 1; + ga.ga_room -= i + sublen - 1; + /* avoid getting stuck on a match with an empty string */ + if (tail == regmatch.endp[0]) + { + if (*tail == NUL) + break; + *((char_u *)ga.ga_data + ga.ga_len) = *tail++; + ++ga.ga_len; + --ga.ga_room; + } + else + { + tail = regmatch.endp[0]; + if (*tail == NUL) + break; + } + if (!do_all) + break; + } + + if (ga.ga_data != NULL) + STRCPY((char *)ga.ga_data + ga.ga_len, tail); + + vim_free(regmatch.regprog); + } + + ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data); + ga_clear(&ga); + p_cpo = save_cpo; + + return ret; +} + +#endif /* defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) */ |