Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

24091 lines (22042 sloc) 544.342 kb
/* 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.
*/
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
#ifdef AMIGA
# include <time.h> /* for strftime() */
#endif
#ifdef VMS
# include <float.h>
#endif
#ifdef MACOS
# include <time.h> /* for time_t */
#endif
#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
# include <math.h>
#endif
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not
be freed. */
/*
* In a hashtab item "hi_key" points to "di_key" in a dictitem.
* This avoids adding a pointer to the hashtab item.
* DI2HIKEY() converts a dictitem pointer to a hashitem key pointer.
* HIKEY2DI() converts a hashitem key pointer to a dictitem pointer.
* HI2DI() converts a hashitem pointer to a dictitem pointer.
*/
static dictitem_T dumdi;
#define DI2HIKEY(di) ((di)->di_key)
#define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi)))
#define HI2DI(hi) HIKEY2DI((hi)->hi_key)
/*
* Structure returned by get_lval() and used by set_var_lval().
* For a plain name:
* "name" points to the variable name.
* "exp_name" is NULL.
* "tv" is NULL
* For a magic braces name:
* "name" points to the expanded variable name.
* "exp_name" is non-NULL, to be freed later.
* "tv" is NULL
* For an index in a list:
* "name" points to the (expanded) variable name.
* "exp_name" NULL or non-NULL, to be freed later.
* "tv" points to the (first) list item value
* "li" points to the (first) list item
* "range", "n1", "n2" and "empty2" indicate what items are used.
* For an existing Dict item:
* "name" points to the (expanded) variable name.
* "exp_name" NULL or non-NULL, to be freed later.
* "tv" points to the dict item value
* "newkey" is NULL
* For a non-existing Dict item:
* "name" points to the (expanded) variable name.
* "exp_name" NULL or non-NULL, to be freed later.
* "tv" points to the Dictionary typval_T
* "newkey" is the key for the new item.
*/
typedef struct lval_S
{
char_u *ll_name; /* start of variable name (can be NULL) */
char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */
typval_T *ll_tv; /* Typeval of item being used. If "newkey"
isn't NULL it's the Dict to which to add
the item. */
listitem_T *ll_li; /* The list item or NULL. */
list_T *ll_list; /* The list or NULL. */
int ll_range; /* TRUE when a [i:j] range was used */
long ll_n1; /* First index for list */
long ll_n2; /* Second index for list range */
int ll_empty2; /* Second index is empty: [i:] */
dict_T *ll_dict; /* The Dictionary or NULL */
dictitem_T *ll_di; /* The dictitem or NULL */
char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
} lval_T;
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_listidx = N_("E684: list index out of range: %ld");
static char *e_undefvar = N_("E121: Undefined variable: %s");
static char *e_missbrac = N_("E111: Missing ']'");
static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionary");
static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary");
static char *e_listreq = N_("E714: List required");
static char *e_dictreq = N_("E715: Dictionary required");
static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
static char *e_dictkey = N_("E716: Key not present in Dictionary: %s");
static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
static char *e_funcdict = N_("E717: Dictionary entry already exists");
static char *e_funcref = N_("E718: Funcref required");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
static char *e_letwrong = N_("E734: Wrong variable type for %s=");
static char *e_nofunc = N_("E130: Unknown function: %s");
static char *e_illvar = N_("E461: Illegal variable name: %s");
/*
* All user-defined global variables are stored in dictionary "globvardict".
* "globvars_var" is the variable that is used for "g:".
*/
static dict_T globvardict;
static dictitem_T globvars_var;
#define globvarht globvardict.dv_hashtab
/*
* Old Vim variables such as "v:version" are also available without the "v:".
* Also in functions. We need a special hashtable for them.
*/
static hashtab_T compat_hashtab;
/* When using exists() don't auto-load a script. */
static int no_autoload = FALSE;
/*
* When recursively copying lists and dicts we need to remember which ones we
* have done to avoid endless recursiveness. This unique ID is used for that.
* The last bit is used for previous_funccal, ignored when comparing.
*/
static int current_copyID = 0;
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
/*
* Array to hold the hashtab with variables local to each sourced script.
* Each item holds a variable (nameless) that points to the dict_T.
*/
typedef struct
{
dictitem_T sv_var;
dict_T sv_dict;
} scriptvar_T;
static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};
#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1])
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
static int echo_attr = 0; /* attributes used for ":echo" */
/* Values for trans_function_name() argument: */
#define TFN_INT 1 /* internal function name OK */
#define TFN_QUIET 2 /* no error messages */
/*
* Structure to hold info for a user function.
*/
typedef struct ufunc ufunc_T;
struct ufunc
{
int uf_varargs; /* variable nr of arguments */
int uf_flags;
int uf_calls; /* nr of active calls */
garray_T uf_args; /* arguments */
garray_T uf_lines; /* function lines */
#ifdef FEAT_PROFILE
int uf_profiling; /* TRUE when func is being profiled */
/* profiling the function as a whole */
int uf_tm_count; /* nr of calls */
proftime_T uf_tm_total; /* time spent in function + children */
proftime_T uf_tm_self; /* time spent in function itself */
proftime_T uf_tm_children; /* time spent in children this call */
/* profiling the function per line */
int *uf_tml_count; /* nr of times line was executed */
proftime_T *uf_tml_total; /* time spent in a line + children */
proftime_T *uf_tml_self; /* time spent in a line itself */
proftime_T uf_tml_start; /* start time for current line */
proftime_T uf_tml_children; /* time spent in children for this line */
proftime_T uf_tml_wait; /* start wait time for current line */
int uf_tml_idx; /* index of line being timed; -1 if none */
int uf_tml_execed; /* line being timed was executed */
#endif
scid_T uf_script_ID; /* ID of script where function was defined,
used for s: variables */
int uf_refcount; /* for numbered function: reference count */
char_u uf_name[1]; /* name of function (actually longer); can
start with <SNR>123_ (<SNR> is K_SPECIAL
KS_EXTRA KE_SNR) */
};
/* function flags */
#define FC_ABORT 1 /* abort function on error */
#define FC_RANGE 2 /* function accepts range */
#define FC_DICT 4 /* Dict function, uses "self" */
/*
* All user-defined functions are found in this hashtable.
*/
static hashtab_T func_hashtab;
/* The names of packages that once were loaded are remembered. */
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
/* list heads for garbage collection */
static dict_T *first_dict = NULL; /* list of all dicts */
static list_T *first_list = NULL; /* list of all lists */
/* From user function to hashitem and back. */
static ufunc_T dumuf;
#define UF2HIKEY(fp) ((fp)->uf_name)
#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf)))
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
#define VAR_SHORT_LEN 20 /* short variable name length */
#define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */
typedef struct funccall_S funccall_T;
struct funccall_S
{
ufunc_T *func; /* function being called */
int linenr; /* next line to be executed */
int returned; /* ":return" used */
struct /* fixed variables for arguments */
{
dictitem_T var; /* variable (without room for name) */
char_u room[VAR_SHORT_LEN]; /* room for the name */
} fixvar[FIXVAR_CNT];
dict_T l_vars; /* l: local function variables */
dictitem_T l_vars_var; /* variable for l: scope */
dict_T l_avars; /* a: argument variables */
dictitem_T l_avars_var; /* variable for a: scope */
list_T l_varlist; /* list for a:000 */
listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */
typval_T *rettv; /* return value */
linenr_T breakpoint; /* next line with breakpoint or zero */
int dbg_tick; /* debug_tick when breakpoint was set */
int level; /* top nesting level of executed function */
#ifdef FEAT_PROFILE
proftime_T prof_child; /* time spent in a child */
#endif
funccall_T *caller; /* calling function or NULL */
};
/*
* Info used by a ":for" loop.
*/
typedef struct
{
int fi_semicolon; /* TRUE if ending in '; var]' */
int fi_varcount; /* nr of variables in the list */
listwatch_T fi_lw; /* keep an eye on the item used. */
list_T *fi_list; /* list being used */
} forinfo_T;
/*
* Struct used by trans_function_name()
*/
typedef struct
{
dict_T *fd_dict; /* Dictionary used */
char_u *fd_newkey; /* new key in "dict" in allocated memory */
dictitem_T *fd_di; /* Dictionary item used */
} funcdict_T;
/*
* Array to hold the value of v: variables.
* The value is in a dictitem, so that it can also be used in the v: scope.
* The reason to use this table anyway is for very quick access to the
* variables with the VV_ defines.
*/
#include "version.h"
/* values for vv_flags: */
#define VV_COMPAT 1 /* compatible, also used without "v:" */
#define VV_RO 2 /* read-only */
#define VV_RO_SBX 4 /* read-only in the sandbox */
#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0}
static struct vimvar
{
char *vv_name; /* name of variable, without v: */
dictitem_T vv_di; /* value and name for key */
char vv_filler[16]; /* space for LONGEST name below!!! */
char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */
} vimvars[VV_LEN] =
{
/*
* The order here must match the VV_ defines in vim.h!
* Initializing a union does not work, leave tv.vval empty to get zero's.
*/
{VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO},
{VV_NAME("count1", VAR_NUMBER), VV_RO},
{VV_NAME("prevcount", VAR_NUMBER), VV_RO},
{VV_NAME("errmsg", VAR_STRING), VV_COMPAT},
{VV_NAME("warningmsg", VAR_STRING), 0},
{VV_NAME("statusmsg", VAR_STRING), 0},
{VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO},
{VV_NAME("this_session", VAR_STRING), VV_COMPAT},
{VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO},
{VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX},
{VV_NAME("termresponse", VAR_STRING), VV_RO},
{VV_NAME("fname", VAR_STRING), VV_RO},
{VV_NAME("lang", VAR_STRING), VV_RO},
{VV_NAME("lc_time", VAR_STRING), VV_RO},
{VV_NAME("ctype", VAR_STRING), VV_RO},
{VV_NAME("charconvert_from", VAR_STRING), VV_RO},
{VV_NAME("charconvert_to", VAR_STRING), VV_RO},
{VV_NAME("fname_in", VAR_STRING), VV_RO},
{VV_NAME("fname_out", VAR_STRING), VV_RO},
{VV_NAME("fname_new", VAR_STRING), VV_RO},
{VV_NAME("fname_diff", VAR_STRING), VV_RO},
{VV_NAME("cmdarg", VAR_STRING), VV_RO},
{VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX},
{VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX},
{VV_NAME("folddashes", VAR_STRING), VV_RO_SBX},
{VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX},
{VV_NAME("progname", VAR_STRING), VV_RO},
{VV_NAME("servername", VAR_STRING), VV_RO},
{VV_NAME("dying", VAR_NUMBER), VV_RO},
{VV_NAME("exception", VAR_STRING), VV_RO},
{VV_NAME("throwpoint", VAR_STRING), VV_RO},
{VV_NAME("register", VAR_STRING), VV_RO},
{VV_NAME("cmdbang", VAR_NUMBER), VV_RO},
{VV_NAME("insertmode", VAR_STRING), VV_RO},
{VV_NAME("val", VAR_UNKNOWN), VV_RO},
{VV_NAME("key", VAR_UNKNOWN), VV_RO},
{VV_NAME("profiling", VAR_NUMBER), VV_RO},
{VV_NAME("fcs_reason", VAR_STRING), VV_RO},
{VV_NAME("fcs_choice", VAR_STRING), 0},
{VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO},
{VV_NAME("beval_winnr", VAR_NUMBER), VV_RO},
{VV_NAME("beval_lnum", VAR_NUMBER), VV_RO},
{VV_NAME("beval_col", VAR_NUMBER), VV_RO},
{VV_NAME("beval_text", VAR_STRING), VV_RO},
{VV_NAME("scrollstart", VAR_STRING), 0},
{VV_NAME("swapname", VAR_STRING), VV_RO},
{VV_NAME("swapchoice", VAR_STRING), 0},
{VV_NAME("swapcommand", VAR_STRING), VV_RO},
{VV_NAME("char", VAR_STRING), 0},
{VV_NAME("mouse_win", VAR_NUMBER), 0},
{VV_NAME("mouse_lnum", VAR_NUMBER), 0},
{VV_NAME("mouse_col", VAR_NUMBER), 0},
{VV_NAME("operator", VAR_STRING), VV_RO},
{VV_NAME("searchforward", VAR_NUMBER), 0},
{VV_NAME("oldfiles", VAR_LIST), 0},
{VV_NAME("windowid", VAR_NUMBER), VV_RO},
};
/* shorthand */
#define vv_type vv_di.di_tv.v_type
#define vv_nr vv_di.di_tv.vval.v_number
#define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string
#define vv_list vv_di.di_tv.vval.v_list
#define vv_tv vv_di.di_tv
/*
* The v: variables are stored in dictionary "vimvardict".
* "vimvars_var" is the variable that is used for the "l:" scope.
*/
static dict_T vimvardict;
static dictitem_T vimvars_var;
#define vimvarht vimvardict.dv_hashtab
static void prepare_vimvar __ARGS((int idx, typval_T *save_tv));
static void restore_vimvar __ARGS((int idx, typval_T *save_tv));
static int ex_let_vars __ARGS((char_u *arg, typval_T *tv, int copy, int semicolon, int var_count, char_u *nextchars));
static char_u *skip_var_list __ARGS((char_u *arg, int *var_count, int *semicolon));
static char_u *skip_var_one __ARGS((char_u *arg));
static void list_hashtable_vars __ARGS((hashtab_T *ht, char_u *prefix, int empty, int *first));
static void list_glob_vars __ARGS((int *first));
static void list_buf_vars __ARGS((int *first));
static void list_win_vars __ARGS((int *first));
#ifdef FEAT_WINDOWS
static void list_tab_vars __ARGS((int *first));
#endif
static void list_vim_vars __ARGS((int *first));
static void list_script_vars __ARGS((int *first));
static void list_func_vars __ARGS((int *first));
static char_u *list_arg_vars __ARGS((exarg_T *eap, char_u *arg, int *first));
static char_u *ex_let_one __ARGS((char_u *arg, typval_T *tv, int copy, char_u *endchars, char_u *op));
static int check_changedtick __ARGS((char_u *arg));
static char_u *get_lval __ARGS((char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int quiet, int fne_flags));
static void clear_lval __ARGS((lval_T *lp));
static void set_var_lval __ARGS((lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op));
static int tv_op __ARGS((typval_T *tv1, typval_T *tv2, char_u *op));
static void list_add_watch __ARGS((list_T *l, listwatch_T *lw));
static void list_rem_watch __ARGS((list_T *l, listwatch_T *lwrem));
static void list_fix_watch __ARGS((list_T *l, listitem_T *item));
static void ex_unletlock __ARGS((exarg_T *eap, char_u *argstart, int deep));
static int do_unlet_var __ARGS((lval_T *lp, char_u *name_end, int forceit));
static int do_lock_var __ARGS((lval_T *lp, char_u *name_end, int deep, int lock));
static void item_lock __ARGS((typval_T *tv, int deep, int lock));
static int tv_islocked __ARGS((typval_T *tv));
static int eval0 __ARGS((char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate));
static int eval1 __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int eval2 __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int eval3 __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int eval4 __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int eval5 __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int eval6 __ARGS((char_u **arg, typval_T *rettv, int evaluate, int want_string));
static int eval7 __ARGS((char_u **arg, typval_T *rettv, int evaluate, int want_string));
static int eval_index __ARGS((char_u **arg, typval_T *rettv, int evaluate, int verbose));
static int get_option_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int get_string_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int get_lit_string_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int get_list_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int rettv_list_alloc __ARGS((typval_T *rettv));
static void listitem_free __ARGS((listitem_T *item));
static long list_len __ARGS((list_T *l));
static int list_equal __ARGS((list_T *l1, list_T *l2, int ic, int recursive));
static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic, int recursive));
static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic, int recursive));
static long list_find_nr __ARGS((list_T *l, long idx, int *errorp));
static long list_idx_of_item __ARGS((list_T *l, listitem_T *item));
static int list_append_number __ARGS((list_T *l, varnumber_T n));
static int list_extend __ARGS((list_T *l1, list_T *l2, listitem_T *bef));
static int list_concat __ARGS((list_T *l1, list_T *l2, typval_T *tv));
static list_T *list_copy __ARGS((list_T *orig, int deep, int copyID));
static char_u *list2string __ARGS((typval_T *tv, int copyID));
static int list_join_inner __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo_style, int copyID, garray_T *join_gap));
static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo, int copyID));
static int free_unref_items __ARGS((int copyID));
static int rettv_dict_alloc __ARGS((typval_T *rettv));
static void dict_free __ARGS((dict_T *d, int recurse));
static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
static dict_T *dict_copy __ARGS((dict_T *orig, int deep, int copyID));
static long dict_len __ARGS((dict_T *d));
static char_u *dict2string __ARGS((typval_T *tv, int copyID));
static int get_dict_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static char_u *echo_string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID));
static char_u *tv2string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID));
static char_u *string_quote __ARGS((char_u *str, int function));
#ifdef FEAT_FLOAT
static int string2float __ARGS((char_u *text, float_T *value));
#endif
static int get_env_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
static int find_internal_func __ARGS((char_u *name));
static char_u *deref_func_name __ARGS((char_u *name, int *lenp));
static int get_func_tv __ARGS((char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
static int call_func __ARGS((char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
static void emsg_funcname __ARGS((char *ermsg, char_u *name));
static int non_zero_arg __ARGS((typval_T *argvars));
#ifdef FEAT_FLOAT
static void f_abs __ARGS((typval_T *argvars, typval_T *rettv));
static void f_acos __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_add __ARGS((typval_T *argvars, typval_T *rettv));
static void f_and __ARGS((typval_T *argvars, typval_T *rettv));
static void f_append __ARGS((typval_T *argvars, typval_T *rettv));
static void f_argc __ARGS((typval_T *argvars, typval_T *rettv));
static void f_argidx __ARGS((typval_T *argvars, typval_T *rettv));
static void f_argv __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_asin __ARGS((typval_T *argvars, typval_T *rettv));
static void f_atan __ARGS((typval_T *argvars, typval_T *rettv));
static void f_atan2 __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_browse __ARGS((typval_T *argvars, typval_T *rettv));
static void f_browsedir __ARGS((typval_T *argvars, typval_T *rettv));
static void f_bufexists __ARGS((typval_T *argvars, typval_T *rettv));
static void f_buflisted __ARGS((typval_T *argvars, typval_T *rettv));
static void f_bufloaded __ARGS((typval_T *argvars, typval_T *rettv));
static void f_bufname __ARGS((typval_T *argvars, typval_T *rettv));
static void f_bufnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_bufwinnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_byte2line __ARGS((typval_T *argvars, typval_T *rettv));
static void f_byteidx __ARGS((typval_T *argvars, typval_T *rettv));
static void f_call __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_ceil __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_changenr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_char2nr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_cindent __ARGS((typval_T *argvars, typval_T *rettv));
static void f_clearmatches __ARGS((typval_T *argvars, typval_T *rettv));
static void f_col __ARGS((typval_T *argvars, typval_T *rettv));
#if defined(FEAT_INS_EXPAND)
static void f_complete __ARGS((typval_T *argvars, typval_T *rettv));
static void f_complete_add __ARGS((typval_T *argvars, typval_T *rettv));
static void f_complete_check __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_confirm __ARGS((typval_T *argvars, typval_T *rettv));
static void f_copy __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_cos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_cosh __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_count __ARGS((typval_T *argvars, typval_T *rettv));
static void f_cscope_connection __ARGS((typval_T *argvars, typval_T *rettv));
static void f_cursor __ARGS((typval_T *argsvars, typval_T *rettv));
static void f_deepcopy __ARGS((typval_T *argvars, typval_T *rettv));
static void f_delete __ARGS((typval_T *argvars, typval_T *rettv));
static void f_did_filetype __ARGS((typval_T *argvars, typval_T *rettv));
static void f_diff_filler __ARGS((typval_T *argvars, typval_T *rettv));
static void f_diff_hlID __ARGS((typval_T *argvars, typval_T *rettv));
static void f_empty __ARGS((typval_T *argvars, typval_T *rettv));
static void f_escape __ARGS((typval_T *argvars, typval_T *rettv));
static void f_eval __ARGS((typval_T *argvars, typval_T *rettv));
static void f_eventhandler __ARGS((typval_T *argvars, typval_T *rettv));
static void f_executable __ARGS((typval_T *argvars, typval_T *rettv));
static void f_exists __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_exp __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_expand __ARGS((typval_T *argvars, typval_T *rettv));
static void f_extend __ARGS((typval_T *argvars, typval_T *rettv));
static void f_feedkeys __ARGS((typval_T *argvars, typval_T *rettv));
static void f_filereadable __ARGS((typval_T *argvars, typval_T *rettv));
static void f_filewritable __ARGS((typval_T *argvars, typval_T *rettv));
static void f_filter __ARGS((typval_T *argvars, typval_T *rettv));
static void f_finddir __ARGS((typval_T *argvars, typval_T *rettv));
static void f_findfile __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_float2nr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_floor __ARGS((typval_T *argvars, typval_T *rettv));
static void f_fmod __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_fnameescape __ARGS((typval_T *argvars, typval_T *rettv));
static void f_fnamemodify __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foldclosed __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foldclosedend __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foldlevel __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foldtext __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foldtextresult __ARGS((typval_T *argvars, typval_T *rettv));
static void f_foreground __ARGS((typval_T *argvars, typval_T *rettv));
static void f_function __ARGS((typval_T *argvars, typval_T *rettv));
static void f_garbagecollect __ARGS((typval_T *argvars, typval_T *rettv));
static void f_get __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getbufline __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getbufvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getchar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getcharmod __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getcmdline __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getcmdpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getcmdtype __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getcwd __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getfontname __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getfperm __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getfsize __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getftime __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getftype __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getline __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getmatches __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getpid __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getqflist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getreg __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getregtype __ARGS((typval_T *argvars, typval_T *rettv));
static void f_gettabvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_gettabwinvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getwinposx __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getwinposy __ARGS((typval_T *argvars, typval_T *rettv));
static void f_getwinvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_glob __ARGS((typval_T *argvars, typval_T *rettv));
static void f_globpath __ARGS((typval_T *argvars, typval_T *rettv));
static void f_has __ARGS((typval_T *argvars, typval_T *rettv));
static void f_has_key __ARGS((typval_T *argvars, typval_T *rettv));
static void f_haslocaldir __ARGS((typval_T *argvars, typval_T *rettv));
static void f_hasmapto __ARGS((typval_T *argvars, typval_T *rettv));
static void f_histadd __ARGS((typval_T *argvars, typval_T *rettv));
static void f_histdel __ARGS((typval_T *argvars, typval_T *rettv));
static void f_histget __ARGS((typval_T *argvars, typval_T *rettv));
static void f_histnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_hlID __ARGS((typval_T *argvars, typval_T *rettv));
static void f_hlexists __ARGS((typval_T *argvars, typval_T *rettv));
static void f_hostname __ARGS((typval_T *argvars, typval_T *rettv));
static void f_iconv __ARGS((typval_T *argvars, typval_T *rettv));
static void f_indent __ARGS((typval_T *argvars, typval_T *rettv));
static void f_index __ARGS((typval_T *argvars, typval_T *rettv));
static void f_input __ARGS((typval_T *argvars, typval_T *rettv));
static void f_inputdialog __ARGS((typval_T *argvars, typval_T *rettv));
static void f_inputlist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_inputrestore __ARGS((typval_T *argvars, typval_T *rettv));
static void f_inputsave __ARGS((typval_T *argvars, typval_T *rettv));
static void f_inputsecret __ARGS((typval_T *argvars, typval_T *rettv));
static void f_insert __ARGS((typval_T *argvars, typval_T *rettv));
static void f_invert __ARGS((typval_T *argvars, typval_T *rettv));
static void f_isdirectory __ARGS((typval_T *argvars, typval_T *rettv));
static void f_islocked __ARGS((typval_T *argvars, typval_T *rettv));
static void f_items __ARGS((typval_T *argvars, typval_T *rettv));
static void f_join __ARGS((typval_T *argvars, typval_T *rettv));
static void f_keys __ARGS((typval_T *argvars, typval_T *rettv));
static void f_last_buffer_nr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_len __ARGS((typval_T *argvars, typval_T *rettv));
static void f_libcall __ARGS((typval_T *argvars, typval_T *rettv));
static void f_libcallnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_line __ARGS((typval_T *argvars, typval_T *rettv));
static void f_line2byte __ARGS((typval_T *argvars, typval_T *rettv));
static void f_lispindent __ARGS((typval_T *argvars, typval_T *rettv));
static void f_localtime __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_log __ARGS((typval_T *argvars, typval_T *rettv));
static void f_log10 __ARGS((typval_T *argvars, typval_T *rettv));
#endif
#ifdef FEAT_LUA
static void f_luaeval __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_map __ARGS((typval_T *argvars, typval_T *rettv));
static void f_maparg __ARGS((typval_T *argvars, typval_T *rettv));
static void f_mapcheck __ARGS((typval_T *argvars, typval_T *rettv));
static void f_match __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matchadd __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matcharg __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matchdelete __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matchend __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matchlist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_matchstr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_max __ARGS((typval_T *argvars, typval_T *rettv));
static void f_min __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef vim_mkdir
static void f_mkdir __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_mode __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_MZSCHEME
static void f_mzeval __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_nextnonblank __ARGS((typval_T *argvars, typval_T *rettv));
static void f_nr2char __ARGS((typval_T *argvars, typval_T *rettv));
static void f_or __ARGS((typval_T *argvars, typval_T *rettv));
static void f_pathshorten __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_pow __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_prevnonblank __ARGS((typval_T *argvars, typval_T *rettv));
static void f_printf __ARGS((typval_T *argvars, typval_T *rettv));
static void f_pumvisible __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_PYTHON3
static void f_py3eval __ARGS((typval_T *argvars, typval_T *rettv));
#endif
#ifdef FEAT_PYTHON
static void f_pyeval __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_range __ARGS((typval_T *argvars, typval_T *rettv));
static void f_readfile __ARGS((typval_T *argvars, typval_T *rettv));
static void f_reltime __ARGS((typval_T *argvars, typval_T *rettv));
static void f_reltimestr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remote_expr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remote_foreground __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remote_peek __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remote_read __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remote_send __ARGS((typval_T *argvars, typval_T *rettv));
static void f_remove __ARGS((typval_T *argvars, typval_T *rettv));
static void f_rename __ARGS((typval_T *argvars, typval_T *rettv));
static void f_repeat __ARGS((typval_T *argvars, typval_T *rettv));
static void f_resolve __ARGS((typval_T *argvars, typval_T *rettv));
static void f_reverse __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_round __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_search __ARGS((typval_T *argvars, typval_T *rettv));
static void f_searchdecl __ARGS((typval_T *argvars, typval_T *rettv));
static void f_searchpair __ARGS((typval_T *argvars, typval_T *rettv));
static void f_searchpairpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_searchpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_server2client __ARGS((typval_T *argvars, typval_T *rettv));
static void f_serverlist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setbufvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setcmdpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setline __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setloclist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setmatches __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setpos __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setqflist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setreg __ARGS((typval_T *argvars, typval_T *rettv));
static void f_settabvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_settabwinvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_setwinvar __ARGS((typval_T *argvars, typval_T *rettv));
static void f_shellescape __ARGS((typval_T *argvars, typval_T *rettv));
static void f_simplify __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_sin __ARGS((typval_T *argvars, typval_T *rettv));
static void f_sinh __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_sort __ARGS((typval_T *argvars, typval_T *rettv));
static void f_soundfold __ARGS((typval_T *argvars, typval_T *rettv));
static void f_spellbadword __ARGS((typval_T *argvars, typval_T *rettv));
static void f_spellsuggest __ARGS((typval_T *argvars, typval_T *rettv));
static void f_split __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_sqrt __ARGS((typval_T *argvars, typval_T *rettv));
static void f_str2float __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_str2nr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strchars __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef HAVE_STRFTIME
static void f_strftime __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_stridx __ARGS((typval_T *argvars, typval_T *rettv));
static void f_string __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strlen __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strpart __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strridx __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strtrans __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strdisplaywidth __ARGS((typval_T *argvars, typval_T *rettv));
static void f_strwidth __ARGS((typval_T *argvars, typval_T *rettv));
static void f_submatch __ARGS((typval_T *argvars, typval_T *rettv));
static void f_substitute __ARGS((typval_T *argvars, typval_T *rettv));
static void f_synID __ARGS((typval_T *argvars, typval_T *rettv));
static void f_synIDattr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_synIDtrans __ARGS((typval_T *argvars, typval_T *rettv));
static void f_synstack __ARGS((typval_T *argvars, typval_T *rettv));
static void f_synconcealed __ARGS((typval_T *argvars, typval_T *rettv));
static void f_system __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tabpagebuflist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tabpagenr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tabpagewinnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_taglist __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tagfiles __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tempname __ARGS((typval_T *argvars, typval_T *rettv));
static void f_test __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_tan __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tanh __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_tolower __ARGS((typval_T *argvars, typval_T *rettv));
static void f_toupper __ARGS((typval_T *argvars, typval_T *rettv));
static void f_tr __ARGS((typval_T *argvars, typval_T *rettv));
#ifdef FEAT_FLOAT
static void f_trunc __ARGS((typval_T *argvars, typval_T *rettv));
#endif
static void f_type __ARGS((typval_T *argvars, typval_T *rettv));
static void f_undofile __ARGS((typval_T *argvars, typval_T *rettv));
static void f_undotree __ARGS((typval_T *argvars, typval_T *rettv));
static void f_values __ARGS((typval_T *argvars, typval_T *rettv));
static void f_virtcol __ARGS((typval_T *argvars, typval_T *rettv));
static void f_visualmode __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winbufnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_wincol __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winheight __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winline __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winnr __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winrestcmd __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winrestview __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winsaveview __ARGS((typval_T *argvars, typval_T *rettv));
static void f_winwidth __ARGS((typval_T *argvars, typval_T *rettv));
static void f_writefile __ARGS((typval_T *argvars, typval_T *rettv));
static void f_xor __ARGS((typval_T *argvars, typval_T *rettv));
static int list2fpos __ARGS((typval_T *arg, pos_T *posp, int *fnump));
static pos_T *var2fpos __ARGS((typval_T *varp, int dollar_lnum, int *fnum));
static int get_env_len __ARGS((char_u **arg));
static int get_id_len __ARGS((char_u **arg));
static int get_name_len __ARGS((char_u **arg, char_u **alias, int evaluate, int verbose));
static char_u *find_name_end __ARGS((char_u *arg, char_u **expr_start, char_u **expr_end, int flags));
#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */
#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
valid character */
static char_u * make_expanded_name __ARGS((char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end));
static int eval_isnamec __ARGS((int c));
static int eval_isnamec1 __ARGS((int c));
static int get_var_tv __ARGS((char_u *name, int len, typval_T *rettv, int verbose));
static int handle_subscript __ARGS((char_u **arg, typval_T *rettv, int evaluate, int verbose));
static typval_T *alloc_tv __ARGS((void));
static typval_T *alloc_string_tv __ARGS((char_u *string));
static void init_tv __ARGS((typval_T *varp));
static long get_tv_number __ARGS((typval_T *varp));
static linenr_T get_tv_lnum __ARGS((typval_T *argvars));
static linenr_T get_tv_lnum_buf __ARGS((typval_T *argvars, buf_T *buf));
static char_u *get_tv_string __ARGS((typval_T *varp));
static char_u *get_tv_string_buf __ARGS((typval_T *varp, char_u *buf));
static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp));
static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, char_u *varname, int writing));
static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
static void vars_clear_ext __ARGS((hashtab_T *ht, int free_val));
static void delete_var __ARGS((hashtab_T *ht, hashitem_T *hi));
static void list_one_var __ARGS((dictitem_T *v, char_u *prefix, int *first));
static void list_one_var_a __ARGS((char_u *prefix, char_u *name, int type, char_u *string, int *first));
static void set_var __ARGS((char_u *name, typval_T *varp, int copy));
static int var_check_ro __ARGS((int flags, char_u *name));
static int var_check_fixed __ARGS((int flags, char_u *name));
static int var_check_func_name __ARGS((char_u *name, int new_var));
static int valid_varname __ARGS((char_u *varname));
static int tv_check_lock __ARGS((int lock, char_u *name));
static int item_copy __ARGS((typval_T *from, typval_T *to, int deep, int copyID));
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 flags, funcdict_T *fd));
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 ufunc_T *find_func __ARGS((char_u *name));
static int function_exists __ARGS((char_u *name));
static int builtin_function __ARGS((char_u *name));
#ifdef FEAT_PROFILE
static void func_do_profile __ARGS((ufunc_T *fp));
static void prof_sort_list __ARGS((FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self));
static void prof_func_line __ARGS((FILE *fd, int count, proftime_T *total, proftime_T *self, int prefer_self));
static int
# ifdef __BORLANDC__
_RTLENTRYF
# endif
prof_total_cmp __ARGS((const void *s1, const void *s2));
static int
# ifdef __BORLANDC__
_RTLENTRYF
# endif
prof_self_cmp __ARGS((const void *s1, const void *s2));
#endif
static int script_autoload __ARGS((char_u *name, int reload));
static char_u *autoload_name __ARGS((char_u *name));
static void cat_func_name __ARGS((char_u *buf, ufunc_T *fp));
static void func_free __ARGS((ufunc_T *fp));
static void call_user_func __ARGS((ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, linenr_T firstline, linenr_T lastline, dict_T *selfdict));
static int can_free_funccal __ARGS((funccall_T *fc, int copyID)) ;
static void free_funccal __ARGS((funccall_T *fc, int free_val));
static void add_nr_var __ARGS((dict_T *dp, dictitem_T *v, char *name, varnumber_T nr));
static win_T *find_win_by_nr __ARGS((typval_T *vp, tabpage_T *tp));
static void getwinvar __ARGS((typval_T *argvars, typval_T *rettv, int off));
static int searchpair_cmn __ARGS((typval_T *argvars, pos_T *match_pos));
static int search_cmn __ARGS((typval_T *argvars, pos_T *match_pos, int *flagsp));
static void setwinvar __ARGS((typval_T *argvars, typval_T *rettv, int off));
#ifdef EBCDIC
static int compare_func_name __ARGS((const void *s1, const void *s2));
static void sortFunctions __ARGS(());
#endif
/* Character used as separated in autoload function/variable names. */
#define AUTOLOAD_CHAR '#'
/*
* Initialize the global and v: variables.
*/
void
eval_init()
{
int i;
struct vimvar *p;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
vimvardict.dv_lock = VAR_FIXED;
hash_init(&compat_hashtab);
hash_init(&func_hashtab);
for (i = 0; i < VV_LEN; ++i)
{
p = &vimvars[i];
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO)
p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
else if (p->vv_flags & VV_RO_SBX)
p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX;
else
p->vv_di.di_flags = DI_FLAGS_FIX;
/* add to v: scope dict, unless the value is not always available */
if (p->vv_type != VAR_UNKNOWN)
hash_add(&vimvarht, p->vv_di.di_key);
if (p->vv_flags & VV_COMPAT)
/* add to compat scope dict */
hash_add(&compat_hashtab, p->vv_di.di_key);
}
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_reg_var(0); /* default for v:register is not 0 but '"' */
#ifdef EBCDIC
/*
* Sort the function table, to enable binary search.
*/
sortFunctions();
#endif
}
#if defined(EXITFREE) || defined(PROTO)
void
eval_clear()
{
int i;
struct vimvar *p;
for (i = 0; i < VV_LEN; ++i)
{
p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING)
{
vim_free(p->vv_str);
p->vv_str = NULL;
}
else if (p->vv_di.di_tv.v_type == VAR_LIST)
{
list_unref(p->vv_list);
p->vv_list = NULL;
}
}
hash_clear(&vimvarht);
hash_init(&vimvarht); /* garbage_collect() will access it */
hash_clear(&compat_hashtab);
free_scriptnames();
free_locales();
/* global variables */
vars_clear(&globvarht);
/* autoloaded script names */
ga_clear_strings(&ga_loaded);
/* script-local variables */
for (i = 1; i <= ga_scripts.ga_len; ++i)
{
vars_clear(&SCRIPT_VARS(i));
vim_free(SCRIPT_SV(i));
}
ga_clear(&ga_scripts);
/* unreferenced lists and dicts */
(void)garbage_collect();
/* functions */
free_all_functions();
hash_clear(&func_hashtab);
}
#endif
/*
* Return the name of the executed function.
*/
char_u *
func_name(cookie)
void *cookie;
{
return ((funccall_T *)cookie)->func->uf_name;
}
/*
* Return the address holding the next breakpoint line for a funccall cookie.
*/
linenr_T *
func_breakpoint(cookie)
void *cookie;
{
return &((funccall_T *)cookie)->breakpoint;
}
/*
* Return the address holding the debug tick for a funccall cookie.
*/
int *
func_dbg_tick(cookie)
void *cookie;
{
return &((funccall_T *)cookie)->dbg_tick;
}
/*
* Return the nesting level for a funccall cookie.
*/
int
func_level(cookie)
void *cookie;
{
return ((funccall_T *)cookie)->level;
}
/* pointer to funccal for currently active function */
funccall_T *current_funccal = NULL;
/* pointer to list of previously used funccal, still around because some
* item in it is still being used. */
funccall_T *previous_funccal = NULL;
/*
* Return TRUE when a function was ended by a ":return" command.
*/
int
current_func_returned()
{
return current_funccal->returned;
}
/*
* 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;
typval_T *tvp;
val = vim_strsave(value);
if (val != NULL)
{
tvp = alloc_string_tv(val);
if (tvp != NULL)
{
set_var(name, tvp, FALSE);
free_tv(tvp);
}
}
}
static lval_T *redir_lval = NULL;
static garray_T redir_ga; /* only valid when redir_lval is not NULL */
static char_u *redir_endp = NULL;
static char_u *redir_varname = NULL;
/*
* Start recording command output to a variable
* Returns OK if successfully completed the setup. FAIL otherwise.
*/
int
var_redir_start(name, append)
char_u *name;
int append; /* append to an existing variable */
{
int save_emsg;
int err;
typval_T tv;
/* Catch a bad name early. */
if (!eval_isnamec1(*name))
{
EMSG(_(e_invarg));
return FAIL;
}
/* Make a copy of the name, it is used in redir_lval until redir ends. */
redir_varname = vim_strsave(name);
if (redir_varname == NULL)
return FAIL;
redir_lval = (lval_T *)alloc_clear((unsigned)sizeof(lval_T));
if (redir_lval == NULL)
{
var_redir_stop();
return FAIL;
}
/* The output is stored in growarray "redir_ga" until redirection ends. */
ga_init2(&redir_ga, (int)sizeof(char), 500);
/* Parse the variable name (can be a dict or list entry). */
redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, FALSE,
FNE_CHECK_START);
if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL)
{
clear_lval(redir_lval);
if (redir_endp != NULL && *redir_endp != NUL)
/* Trailing characters are present after the variable name */
EMSG(_(e_trailing));
else
EMSG(_(e_invarg));
redir_endp = NULL; /* don't store a value, only cleanup */
var_redir_stop();
return FAIL;
}
/* check if we can write to the variable: set it to or append an empty
* string */
save_emsg = did_emsg;
did_emsg = FALSE;
tv.v_type = VAR_STRING;
tv.vval.v_string = (char_u *)"";
if (append)
set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)".");
else
set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)"=");
clear_lval(redir_lval);
err = did_emsg;
did_emsg |= save_emsg;
if (err)
{
redir_endp = NULL; /* don't store a value, only cleanup */
var_redir_stop();
return FAIL;
}
return OK;
}
/*
* Append "value[value_len]" to the variable set by var_redir_start().
* The actual appending is postponed until redirection ends, because the value
* appended may in fact be the string we write to, changing it may cause freed
* memory to be used:
* :redir => foo
* :let foo
* :redir END
*/
void
var_redir_str(value, value_len)
char_u *value;
int value_len;
{
int len;
if (redir_lval == NULL)
return;
if (value_len == -1)
len = (int)STRLEN(value); /* Append the entire string */
else
len = value_len; /* Append only "value_len" characters */
if (ga_grow(&redir_ga, len) == OK)
{
mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len);
redir_ga.ga_len += len;
}
else
var_redir_stop();
}
/*
* Stop redirecting command output to a variable.
* Frees the allocated memory.
*/
void
var_redir_stop()
{
typval_T tv;
if (redir_lval != NULL)
{
/* If there was no error: assign the text to the variable. */
if (redir_endp != NULL)
{
ga_append(&redir_ga, NUL); /* Append the trailing NUL. */
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
/* Call get_lval() again, if it's inside a Dict or List it may
* have changed. */
redir_endp = get_lval(redir_varname, NULL, redir_lval,
FALSE, FALSE, FALSE, FNE_CHECK_START);
if (redir_endp != NULL && redir_lval->ll_name != NULL)
set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)".");
clear_lval(redir_lval);
}
/* free the collected output */
vim_free(redir_ga.ga_data);
redir_ga.ga_data = NULL;
vim_free(redir_lval);
redir_lval = NULL;
}
vim_free(redir_varname);
redir_varname = NULL;
}
# 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 */
{
typval_T tv;
int retval = FALSE;
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, !skip) == FAIL)
*error = TRUE;
else
{
*error = FALSE;
if (!skip)
{
retval = (get_tv_number_chk(&tv, error) != 0);
clear_tv(&tv);
}
}
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 */
{
typval_T tv;
char_u *retval;
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip)
retval = NULL;
else
{
retval = vim_strsave(get_tv_string(&tv));
clear_tv(&tv);
}
if (skip)
--emsg_skip;
return retval;
}
/*
* Skip over an expression at "*pp".
* Return FAIL for an error, OK otherwise.
*/
int
skip_expr(pp)
char_u **pp;
{
typval_T rettv;
*pp = skipwhite(*pp);
return eval1(pp, &rettv, FALSE);
}
/*
* Top level evaluation function, returning a string.
* When "convert" is TRUE convert a List into a sequence of lines and convert
* a Float to a String.
* Return pointer to allocated memory, or NULL for failure.
*/
char_u *
eval_to_string(arg, nextcmd, convert)
char_u *arg;
char_u **nextcmd;
int convert;
{
typval_T tv;
char_u *retval;
garray_T ga;
#ifdef FEAT_FLOAT
char_u numbuf[NUMBUFLEN];
#endif
if (eval0(arg, &tv, nextcmd, TRUE) == FAIL)
retval = NULL;
else
{
if (convert && tv.v_type == VAR_LIST)
{
ga_init2(&ga, (int)sizeof(char), 80);
if (tv.vval.v_list != NULL)
{
list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, 0);
if (tv.vval.v_list->lv_len > 0)
ga_append(&ga, NL);
}
ga_append(&ga, NUL);
retval = (char_u *)ga.ga_data;
}
#ifdef FEAT_FLOAT
else if (convert && tv.v_type == VAR_FLOAT)
{
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
retval = vim_strsave(numbuf);
}
#endif
else
retval = vim_strsave(get_tv_string(&tv));
clear_tv(&tv);
}
return retval;
}
/*
* Call eval_to_string() without using current local variables and using
* textlock. When "use_sandbox" is TRUE use the sandbox.
*/
char_u *
eval_to_string_safe(arg, nextcmd, use_sandbox)
char_u *arg;
char_u **nextcmd;
int use_sandbox;
{
char_u *retval;
void *save_funccalp;
save_funccalp = save_funccal();
if (use_sandbox)
++sandbox;
++textlock;
retval = eval_to_string(arg, nextcmd, FALSE);
if (use_sandbox)
--sandbox;
--textlock;
restore_funccal(save_funccalp);
return retval;
}
/*
* Top level evaluation function, returning a number.
* Evaluates "expr" silently.
* Returns -1 for an error.
*/
int
eval_to_number(expr)
char_u *expr;
{
typval_T rettv;
int retval;
char_u *p = skipwhite(expr);
++emsg_off;
if (eval1(&p, &rettv, TRUE) == FAIL)
retval = -1;
else
{
retval = get_tv_number_chk(&rettv, NULL);
clear_tv(&rettv);
}
--emsg_off;
return retval;
}
/*
* Prepare v: variable "idx" to be used.
* Save the current typeval in "save_tv".
* When not used yet add the variable to the v: hashtable.
*/
static void
prepare_vimvar(idx, save_tv)
int idx;
typval_T *save_tv;
{
*save_tv = vimvars[idx].vv_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN)
hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
}
/*
* Restore v: variable "idx" to typeval "save_tv".
* When no longer defined, remove the variable from the v: hashtable.
*/
static void
restore_vimvar(idx, save_tv)
int idx;
typval_T *save_tv;
{
hashitem_T *hi;
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN)
{
hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi))
EMSG2(_(e_intern2), "restore_vimvar()");
else
hash_remove(&vimvarht, hi);
}
}
#if defined(FEAT_SPELL) || defined(PROTO)
/*
* Evaluate an expression to a list with suggestions.
* For the "expr:" part of 'spellsuggest'.
* Returns NULL when there is an error.
*/
list_T *
eval_spell_expr(badword, expr)
char_u *badword;
char_u *expr;
{
typval_T save_val;
typval_T rettv;
list_T *list = NULL;
char_u *p = skipwhite(expr);
/* Set "v:val" to the bad word. */
prepare_vimvar(VV_VAL, &save_val);
vimvars[VV_VAL].vv_type = VAR_STRING;
vimvars[VV_VAL].vv_str = badword;
if (p_verbose == 0)
++emsg_off;
if (eval1(&p, &rettv, TRUE) == OK)
{
if (rettv.v_type != VAR_LIST)
clear_tv(&rettv);
else
list = rettv.vval.v_list;
}
if (p_verbose == 0)
--emsg_off;
restore_vimvar(VV_VAL, &save_val);
return list;
}
/*
* "list" is supposed to contain two items: a word and a number. Return the
* word in "pp" and the number as the return value.
* Return -1 if anything isn't right.
* Used to get the good word and score from the eval_spell_expr() result.
*/
int
get_spellword(list, pp)
list_T *list;
char_u **pp;
{
listitem_T *li;
li = list->lv_first;
if (li == NULL)
return -1;
*pp = get_tv_string(&li->li_tv);
li = li->li_next;
if (li == NULL)
return -1;
return get_tv_number(&li->li_tv);
}
#endif
/*
* Top level evaluation function.
* Returns an allocated typval_T with the result.
* Returns NULL when there is an error.
*/
typval_T *
eval_expr(arg, nextcmd)
char_u *arg;
char_u **nextcmd;
{
typval_T *tv;
tv = (typval_T *)alloc(sizeof(typval_T));
if (tv != NULL && eval0(arg, tv, nextcmd, TRUE) == FAIL)
{
vim_free(tv);
tv = NULL;
}
return tv;
}
#if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) \
|| defined(FEAT_COMPL_FUNC) || defined(PROTO)
/*
* Call some vimL function and return the result in "*rettv".
* Uses argv[argc] for the function arguments. Only Number and String
* arguments are currently supported.
* Returns OK or FAIL.
*/
int
call_vim_function(func, argc, argv, safe, str_arg_only, rettv)
char_u *func;
int argc;
char_u **argv;
int safe; /* use the sandbox */
int str_arg_only; /* all arguments are strings */
typval_T *rettv;
{
typval_T *argvars;
long n;
int len;
int i;
int doesrange;
void *save_funccalp = NULL;
int ret;
argvars = (typval_T *)alloc((unsigned)((argc + 1) * sizeof(typval_T)));
if (argvars == NULL)
return FAIL;
for (i = 0; i < argc; i++)
{
/* Pass a NULL or empty argument as an empty string */
if (argv[i] == NULL || *argv[i] == NUL)
{
argvars[i].v_type = VAR_STRING;
argvars[i].vval.v_string = (char_u *)"";
continue;
}
if (str_arg_only)
len = 0;
else
/* 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].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n;
}
else
{
argvars[i].v_type = VAR_STRING;
argvars[i].vval.v_string = argv[i];
}
}
if (safe)
{
save_funccalp = save_funccal();
++sandbox;
}
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, TRUE, NULL);
if (safe)
{
--sandbox;
restore_funccal(save_funccalp);
}
vim_free(argvars);
if (ret == FAIL)
clear_tv(rettv);
return ret;
}
# if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) || defined(PROTO)
/*
* Call vimL function "func" and return the result as a string.
* Returns NULL when calling the function fails.
* Uses argv[argc] for the function arguments.
*/
void *
call_func_retstr(func, argc, argv, safe)
char_u *func;
int argc;
char_u **argv;
int safe; /* use the sandbox */
{
typval_T rettv;
char_u *retval;
/* All arguments are passed as strings, no conversion to number. */
if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
return NULL;
retval = vim_strsave(get_tv_string(&rettv));
clear_tv(&rettv);
return retval;
}
# endif
# if defined(FEAT_COMPL_FUNC) || defined(PROTO)
/*
* Call vimL function "func" and return the result as a number.
* Returns -1 when calling the function fails.
* Uses argv[argc] for the function arguments.
*/
long
call_func_retnr(func, argc, argv, safe)
char_u *func;
int argc;
char_u **argv;
int safe; /* use the sandbox */
{
typval_T rettv;
long retval;
/* All arguments are passed as strings, no conversion to number. */
if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
return -1;
retval = get_tv_number_chk(&rettv, NULL);
clear_tv(&rettv);
return retval;
}
# endif
/*
* Call vimL function "func" and return the result as a List.
* Uses argv[argc] for the function arguments.
* Returns NULL when there is something wrong.
*/
void *
call_func_retlist(func, argc, argv, safe)
char_u *func;
int argc;
char_u **argv;
int safe; /* use the sandbox */
{
typval_T rettv;
/* All arguments are passed as strings, no conversion to number. */
if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
return NULL;
if (rettv.v_type != VAR_LIST)
{
clear_tv(&rettv);
return NULL;
}
return rettv.vval.v_list;
}
#endif
/*
* Save the current function call pointer, and set it to NULL.
* Used when executing autocommands and for ":source".
*/
void *
save_funccal()
{
funccall_T *fc = current_funccal;
current_funccal = NULL;
return (void *)fc;
}
void
restore_funccal(vfc)
void *vfc;
{
funccall_T *fc = (funccall_T *)vfc;
current_funccal = fc;
}
#if defined(FEAT_PROFILE) || defined(PROTO)
/*
* Prepare profiling for entering a child or something else that is not
* counted for the script/function itself.
* Should always be called in pair with prof_child_exit().
*/
void
prof_child_enter(tm)
proftime_T *tm; /* place to store waittime */
{
funccall_T *fc = current_funccal;
if (fc != NULL && fc->func->uf_profiling)
profile_start(&fc->prof_child);
script_prof_save(tm);
}
/*
* Take care of time spent in a child.
* Should always be called after prof_child_enter().
*/
void
prof_child_exit(tm)
proftime_T *tm; /* where waittime was stored */
{
funccall_T *fc = current_funccal;
if (fc != NULL && fc->func->uf_profiling)
{
profile_end(&fc->prof_child);
profile_sub_wait(tm, &fc->prof_child); /* don't count waiting time */
profile_add(&fc->func->uf_tm_children, &fc->prof_child);
profile_add(&fc->func->uf_tml_children, &fc->prof_child);
}
script_prof_restore(tm);
}
#endif
#ifdef FEAT_FOLDING
/*
* 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;
{
typval_T tv;
int retval;
char_u *s;
int use_sandbox = was_set_insecurely((char_u *)"foldexpr",
OPT_LOCAL);
++emsg_off;
if (use_sandbox)
++sandbox;
++textlock;
*cp = NUL;
if (eval0(arg, &tv, NULL, TRUE) == FAIL)
retval = 0;
else
{
/* If the result is a number, just return the number. */
if (tv.v_type == VAR_NUMBER)
retval = tv.vval.v_number;
else if (tv.v_type != VAR_STRING || tv.vval.v_string == NULL)
retval = 0;
else
{
/* If the result is a string, check if there is a non-digit before
* the number. */
s = tv.vval.v_string;
if (!VIM_ISDIGIT(*s) && *s != '-')
*cp = *s++;
retval = atol((char *)s);
}
clear_tv(&tv);
}
--emsg_off;
if (use_sandbox)
--sandbox;
--textlock;
return retval;
}
#endif
/*
* ":let" list all variable values
* ":let var1 var2" list variable values
* ":let var = expr" assignment command.
* ":let var += expr" assignment command.
* ":let var -= expr" assignment command.
* ":let var .= expr" assignment command.
* ":let [var1, var2] = expr" unpack list.
*/
void
ex_let(eap)
exarg_T *eap;
{
char_u *arg = eap->arg;
char_u *expr = NULL;
typval_T rettv;
int i;
int var_count = 0;
int semicolon = 0;
char_u op[2];
char_u *argend;
int first = TRUE;
argend = skip_var_list(arg, &var_count, &semicolon);
if (argend == NULL)
return;
if (argend > arg && argend[-1] == '.') /* for var.='str' */
--argend;
expr = vim_strchr(argend, '=');
if (expr == NULL)
{
/*
* ":let" without "=": list variables
*/
if (*arg == '[')
EMSG(_(e_invarg));
else if (!ends_excmd(*arg))
/* ":let var1 var2" */
arg = list_arg_vars(eap, arg, &first);
else if (!eap->skip)
{
/* ":let" */
list_glob_vars(&first);
list_buf_vars(&first);
list_win_vars(&first);
#ifdef FEAT_WINDOWS
list_tab_vars(&first);
#endif
list_script_vars(&first);
list_func_vars(&first);
list_vim_vars(&first);
}
eap->nextcmd = check_nextcmd(arg);
}
else
{
op[0] = '=';
op[1] = NUL;
if (expr > argend)
{
if (vim_strchr((char_u *)"+-.", expr[-1]) != NULL)
op[0] = expr[-1]; /* +=, -= or .= */
}
expr = skipwhite(expr + 1);
if (eap->skip)
++emsg_skip;
i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
if (eap->skip)
{
if (i != FAIL)
clear_tv(&rettv);
--emsg_skip;
}
else if (i != FAIL)
{
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
op);
clear_tv(&rettv);
}
}
}
/*
* Assign the typevalue "tv" to the variable or variables at "arg_start".
* Handles both "var" with any type and "[var, var; var]" with a list type.
* When "nextchars" is not NULL it points to a string with characters that
* must appear after the variable(s). Use "+", "-" or "." for add, subtract
* or concatenate.
* Returns OK or FAIL;
*/
static int
ex_let_vars(arg_start, tv, copy, semicolon, var_count, nextchars)
char_u *arg_start;
typval_T *tv;
int copy; /* copy values from "tv", don't move */
int semicolon; /* from skip_var_list() */
int var_count; /* from skip_var_list() */
char_u *nextchars;
{
char_u *arg = arg_start;
list_T *l;
int i;
listitem_T *item;
typval_T ltv;
if (*arg != '[')
{
/*
* ":let var = expr" or ":for var in list"
*/
if (ex_let_one(arg, tv, copy, nextchars, nextchars) == NULL)
return FAIL;
return OK;
}
/*
* ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
*/
if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL)
{
EMSG(_(e_listreq));
return FAIL;
}
i = list_len(l);
if (semicolon == 0 && var_count < i)
{
EMSG(_("E687: Less targets than List items"));
return FAIL;
}
if (var_count - semicolon > i)
{
EMSG(_("E688: More targets than List items"));
return FAIL;
}
item = l->lv_first;
while (*arg != ']')
{
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars);
item = item->li_next;
if (arg == NULL)
return FAIL;
arg = skipwhite(arg);
if (*arg == ';')
{
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
l = list_alloc();
if (l == NULL)
return FAIL;
while (item != NULL)
{
list_append_tv(l, &item->li_tv);
item = item->li_next;
}
ltv.v_type = VAR_LIST;
ltv.v_lock = 0;
ltv.vval.v_list = l;
l->lv_refcount = 1;
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE,
(char_u *)"]", nextchars);
clear_tv(&ltv);
if (arg == NULL)
return FAIL;
break;
}
else if (*arg != ',' && *arg != ']')
{
EMSG2(_(e_intern2), "ex_let_vars()");
return FAIL;
}
}
return OK;
}
/*
* Skip over assignable variable "var" or list of variables "[var, var]".
* Used for ":let varvar = expr" and ":for varvar in expr".
* For "[var, var]" increment "*var_count" for each variable.
* for "[var, var; var]" set "semicolon".
* Return NULL for an error.
*/
static char_u *
skip_var_list(arg, var_count, semicolon)
char_u *arg;
int *var_count;
int *semicolon;
{
char_u *p, *s;
if (*arg == '[')
{
/* "[var, var]": find the matching ']'. */
p = arg;
for (;;)
{
p = skipwhite(p + 1); /* skip whites after '[', ';' or ',' */
s = skip_var_one(p);
if (s == p)
{
EMSG2(_(e_invarg2), p);
return NULL;
}
++*var_count;
p = skipwhite(s);
if (*p == ']')
break;
else if (*p == ';')
{
if (*semicolon == 1)
{
EMSG(_("Double ; in list of variables"));
return NULL;
}
*semicolon = 1;
}
else if (*p != ',')
{
EMSG2(_(e_invarg2), p);
return NULL;
}
}
return p + 1;
}
else
return skip_var_one(arg);
}
/*
* Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
* l[idx].
*/
static char_u *
skip_var_one(arg)
char_u *arg;
{
if (*arg == '@' && arg[1] != NUL)
return arg + 2;
return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
}
/*
* List variables for hashtab "ht" with prefix "prefix".
* If "empty" is TRUE also list NULL strings as empty strings.
*/
static void
list_hashtable_vars(ht, prefix, empty, first)
hashtab_T *ht;
char_u *prefix;
int empty;
int *first;
{
hashitem_T *hi;
dictitem_T *di;
int todo;
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
--todo;
di = HI2DI(hi);
if (empty || di->di_tv.v_type != VAR_STRING
|| di->di_tv.vval.v_string != NULL)
list_one_var(di, prefix, first);
}
}
}
/*
* List global variables.
*/
static void
list_glob_vars(first)
int *first;
{
list_hashtable_vars(&globvarht, (char_u *)"", TRUE, first);
}
/*
* List buffer variables.
*/
static void
list_buf_vars(first)
int *first;
{
char_u numbuf[NUMBUFLEN];
list_hashtable_vars(&curbuf->b_vars.dv_hashtab, (char_u *)"b:",
TRUE, first);
sprintf((char *)numbuf, "%ld", (long)curbuf->b_changedtick);
list_one_var_a((char_u *)"b:", (char_u *)"changedtick", VAR_NUMBER,
numbuf, first);
}
/*
* List window variables.
*/
static void
list_win_vars(first)
int *first;
{
list_hashtable_vars(&curwin->w_vars.dv_hashtab,
(char_u *)"w:", TRUE, first);
}
#ifdef FEAT_WINDOWS
/*
* List tab page variables.
*/
static void
list_tab_vars(first)
int *first;
{
list_hashtable_vars(&curtab->tp_vars.dv_hashtab,
(char_u *)"t:", TRUE, first);
}
#endif
/*
* List Vim variables.
*/
static void
list_vim_vars(first)
int *first;
{
list_hashtable_vars(&vimvarht, (char_u *)"v:", FALSE, first);
}
/*
* List script-local variables, if there is a script.
*/
static void
list_script_vars(first)
int *first;
{
if (current_SID > 0 && current_SID <= ga_scripts.ga_len)
list_hashtable_vars(&SCRIPT_VARS(current_SID),
(char_u *)"s:", FALSE, first);
}
/*
* List function variables, if there is a function.
*/
static void
list_func_vars(first)
int *first;
{
if (current_funccal != NULL)
list_hashtable_vars(&current_funccal->l_vars.dv_hashtab,
(char_u *)"l:", FALSE, first);
}
/*
* List variables in "arg".
*/
static char_u *
list_arg_vars(eap, arg, first)
exarg_T *eap;
char_u *arg;
int *first;
{
int error = FALSE;
int len;
char_u *name;
char_u *name_start;
char_u *arg_subsc;
char_u *tofree;
typval_T tv;
while (!ends_excmd(*arg) && !got_int)
{
if (error || eap->skip)
{
arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
if (!vim_iswhite(*arg) && !ends_excmd(*arg))
{
emsg_severe = TRUE;
EMSG(_(e_trailing));
break;
}
}
else
{
/* get_name_len() takes care of expanding curly braces */
name_start = name = arg;
len = get_name_len(&arg, &tofree, TRUE, TRUE);
if (len <= 0)
{
/* This is mainly to keep test 49 working: when expanding
* curly braces fails overrule the exception error message. */
if (len < 0 && !aborting())
{
emsg_severe = TRUE;
EMSG2(_(e_invarg2), arg);
break;
}
error = TRUE;
}
else
{
if (tofree != NULL)
name = tofree;
if (get_var_tv(name, len, &tv, TRUE) == FAIL)
error = TRUE;
else
{
/* handle d.key, l[idx], f(expr) */
arg_subsc = arg;
if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL)
error = TRUE;
else
{
if (arg == arg_subsc && len == 2 && name[1] == ':')
{
switch (*name)
{
case 'g': list_glob_vars(first); break;
case 'b': list_buf_vars(first); break;
case 'w': list_win_vars(first); break;
#ifdef FEAT_WINDOWS
case 't': list_tab_vars(first); break;
#endif
case 'v': list_vim_vars(first); break;
case 's': list_script_vars(first); break;
case 'l': list_func_vars(first); break;
default:
EMSG2(_("E738: Can't list variables for %s"), name);
}
}
else
{
char_u numbuf[NUMBUFLEN];
char_u *tf;
int c;
char_u *s;
s = echo_string(&tv, &tf, numbuf, 0);
c = *arg;
*arg = NUL;
list_one_var_a((char_u *)"",
arg == arg_subsc ? name : name_start,
tv.v_type,
s == NULL ? (char_u *)"" : s,
first);
*arg = c;
vim_free(tf);
}
clear_tv(&tv);
}
}
}
vim_free(tofree);
}
arg = skipwhite(arg);
}
return arg;
}
/*
* Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value.
* Returns a pointer to the char just after the var name.
* Returns NULL if there is an error.
*/
static char_u *
ex_let_one(arg, tv, copy, endchars, op)
char_u *arg; /* points to variable name */
typval_T *tv; /* value to assign to variable */
int copy; /* copy value from "tv" */
char_u *endchars; /* valid chars after variable name or NULL */
char_u *op; /* "+", "-", "." or NULL*/
{
int c1;
char_u *name;
char_u *p;
char_u *arg_end = NULL;
int len;
int opt_flags;
char_u *tofree = NULL;
/*
* ":let $VAR = expr": Set environment variable.
*/
if (*arg == '$')
{
/* Find the end of the name. */
++arg;
name = arg;
len = get_env_len(&arg);
if (len == 0)
EMSG2(_(e_invarg2), name - 1);
else
{
if (op != NULL && (*op == '+' || *op == '-'))
EMSG2(_(e_letwrong), op);
else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL)
EMSG(_(e_letunexp));
else if (!check_secure())
{
c1 = name[len];
name[len] = NUL;
p = get_tv_string_chk(tv);
if (p != NULL && op != NULL && *op == '.')
{
int mustfree = FALSE;
char_u *s = vim_getenv(name, &mustfree);
if (s != NULL)
{
p = tofree = concat_str(s, p);
if (mustfree)
vim_free(s);
}
}
if (p != NULL)
{
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;
arg_end = arg;
}
name[len] = c1;
vim_free(tofree);
}
}
}
/*
* ":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 == '&')
{
/* Find the end of the name. */
p = find_option_end(&arg, &opt_flags);
if (p == NULL || (endchars != NULL
&& vim_strchr(endchars, *skipwhite(p)) == NULL))
EMSG(_(e_letunexp));
else
{
long n;
int opt_type;
long numval;
char_u *stringval = NULL;
char_u *s;
c1 = *p;
*p = NUL;
n = get_tv_number(tv);
s = get_tv_string_chk(tv); /* != NULL if number or string */
if (s != NULL && op != NULL && *op != '=')
{
opt_type = get_option_value(arg, &numval,
&stringval, opt_flags);
if ((opt_type == 1 && *op == '.')
|| (opt_type == 0 && *op != '.'))
EMSG2(_(e_letwrong), op);
else
{
if (opt_type == 1) /* number */
{
if (*op == '+')
n = numval + n;
else
n = numval - n;
}
else if (opt_type == 0 && stringval != NULL) /* string */
{
s = concat_str(stringval, s);
vim_free(stringval);
stringval = s;
}
}
}
if (s != NULL)
{
set_option_value(arg, n, s, opt_flags);
arg_end = p;
}
*p = c1;
vim_free(stringval);
}
}
/*
* ":let @r = expr": Set register contents.
*/
else if (*arg == '@')
{
++arg;
if (op != NULL && (*op == '+' || *op == '-'))
EMSG2(_(e_letwrong), op);
else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
EMSG(_(e_letunexp));
else
{
char_u *ptofree = NULL;
char_u *s;
p = get_tv_string_chk(tv);
if (p != NULL && op != NULL && *op == '.')
{
s = get_reg_contents(*arg == '@' ? '"' : *arg, TRUE, TRUE);
if (s != NULL)
{
p = ptofree = concat_str(s, p);
vim_free(s);
}
}
if (p != NULL)
{
write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
arg_end = arg + 1;
}
vim_free(ptofree);
}
}
/*
* ":let var = expr": Set internal variable.
* ":let {expr} = expr": Idem, name made with curly braces
*/
else if (eval_isnamec1(*arg) || *arg == '{')
{
lval_T lv;
p = get_lval(arg, tv, &lv, FALSE, FALSE, FALSE, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL)
{
if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
EMSG(_(e_letunexp));
else
{
set_var_lval(&lv, p, tv, copy, op);
arg_end = p;
}
}
clear_lval(&lv);
}
else
EMSG2(_(e_invarg2), arg);
return arg_end;
}
/*
* If "arg" is equal to "b:changedtick" give an error and return TRUE.
*/
static int
check_changedtick(arg)
char_u *arg;
{
if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13]))
{
EMSG2(_(e_readonlyvar), arg);
return TRUE;
}
return FALSE;
}
/*
* Get an lval: variable, Dict item or List item that can be assigned a value
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
* "name.key", "name.key[expr]" etc.
* Indexing only works if "name" is an existing List or Dictionary.
* "name" points to the start of the name.
* If "rettv" is not NULL it points to the value to be assigned.
* "unlet" is TRUE for ":unlet": slightly different behavior when something is
* wrong; must end in space or cmd separator.
*
* Returns a pointer to just after the name, including indexes.
* When an evaluation error occurs "lp->ll_name" is NULL;
* Returns NULL for a parsing error. Still need to free items in "lp"!
*/
static char_u *
get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
char_u *name;
typval_T *rettv;
lval_T *lp;
int unlet;
int skip;
int quiet; /* don't give error messages */
int fne_flags; /* flags for find_name_end() */
{
char_u *p;
char_u *expr_start, *expr_end;
int cc;
dictitem_T *v;
typval_T var1;
typval_T var2;
int empty1 = FALSE;
listitem_T *ni;
char_u *key = NULL;
int len;
hashtab_T *ht;
/* Clear everything in "lp". */
vim_memset(lp, 0, sizeof(lval_T));
if (skip)
{
/* When skipping just find the end of the name. */
lp->ll_name = name;
return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags);
}
/* Find the end of the name. */
p = find_name_end(name, &expr_start, &expr_end, fne_flags);
if (expr_start != NULL)
{
/* Don't expand the name when we already know there is an error. */
if (unlet && !vim_iswhite(*p) && !ends_excmd(*p)
&& *p != '[' && *p != '.')
{
EMSG(_(e_trailing));
return NULL;
}
lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p);
if (lp->ll_exp_name == 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() && !quiet)
{
emsg_severe = TRUE;
EMSG2(_(e_invarg2), name);
return NULL;
}
}
lp->ll_name = lp->ll_exp_name;
}
else
lp->ll_name = name;
/* Without [idx] or .key we are done. */
if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
return p;
cc = *p;
*p = NUL;
v = find_var(lp->ll_name, &ht);
if (v == NULL && !quiet)
EMSG2(_(e_undefvar), lp->ll_name);
*p = cc;
if (v == NULL)
return NULL;
/*
* Loop until no more [idx] or .key is following.
*/
lp->ll_tv = &v->di_tv;
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT))
{
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
&& !(lp->ll_tv->v_type == VAR_DICT
&& lp->ll_tv->vval.v_dict != NULL))
{
if (!quiet)
EMSG(_("E689: Can only index a List or Dictionary"));
return NULL;
}
if (lp->ll_range)
{
if (!quiet)
EMSG(_("E708: [:] must come last"));
return NULL;
}
len = -1;
if (*p == '.')
{
key = p + 1;
for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
;
if (len == 0)
{
if (!quiet)
EMSG(_(e_emptykey));
return NULL;
}
p = key + len;
}
else
{
/* Get the index [expr] or the first index [expr: ]. */
p = skipwhite(p + 1);
if (*p == ':')
empty1 = TRUE;
else
{
empty1 = FALSE;
if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
return NULL;
if (get_tv_string_chk(&var1) == NULL)
{
/* not a number or string */
clear_tv(&var1);
return NULL;
}
}
/* Optionally get the second index [ :expr]. */
if (*p == ':')
{
if (lp->ll_tv->v_type == VAR_DICT)
{
if (!quiet)
EMSG(_(e_dictrange));
if (!empty1)
clear_tv(&var1);
return NULL;
}
if (rettv != NULL && (rettv->v_type != VAR_LIST
|| rettv->vval.v_list == NULL))
{
if (!quiet)
EMSG(_("E709: [:] requires a List value"));
if (!empty1)
clear_tv(&var1);
return NULL;
}
p = skipwhite(p + 1);
if (*p == ']')
lp->ll_empty2 = TRUE;
else
{
lp->ll_empty2 = FALSE;
if (eval1(&p, &var2, TRUE) == FAIL) /* recursive! */
{
if (!empty1)
clear_tv(&var1);
return NULL;
}
if (get_tv_string_chk(&var2) == NULL)
{
/* not a number or string */
if (!empty1)
clear_tv(&var1);
clear_tv(&var2);
return NULL;
}
}
lp->ll_range = TRUE;
}
else
lp->ll_range = FALSE;
if (*p != ']')
{
if (!quiet)
EMSG(_(e_missbrac));
if (!empty1)
clear_tv(&var1);
if (lp->ll_range && !lp->ll_empty2)
clear_tv(&var2);
return NULL;
}
/* Skip to past ']'. */
++p;
}
if (lp->ll_tv->v_type == VAR_DICT)
{
if (len == -1)
{
/* "[key]": get key from "var1" */
key = get_tv_string(&var1); /* is number or string */
if (*key == NUL)
{
if (!quiet)
EMSG(_(e_emptykey));
clear_tv(&var1);
return NULL;
}
}
lp->ll_list = NULL;
lp->ll_dict = lp->ll_tv->vval.v_dict;
lp->ll_di = dict_find(lp->ll_dict, key, len);
/* When assigning to a scope dictionary check that a function and
* variable name is valid (only variable name unless it is l: or
* g: dictionary). Disallow overwriting a builtin function. */
if (rettv != NULL && lp->ll_dict->dv_scope != 0)
{
int prevval;
int wrong;
if (len != -1)
{
prevval = key[len];
key[len] = NUL;
}
wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
&& rettv->v_type == VAR_FUNC
&& var_check_func_name(key, lp->ll_di == NULL))
|| !valid_varname(key);
if (len != -1)
key[len] = prevval;
if (wrong)
return NULL;
}
if (lp->ll_di == NULL)
{
/* Can't add "v:" variable. */
if (lp->ll_dict == &vimvardict)
{
EMSG2(_(e_illvar), name);
return NULL;
}
/* Key does not exist in dict: may need to add it. */
if (*p == '[' || *p == '.' || unlet)
{
if (!quiet)
EMSG2(_(e_dictkey), key);
if (len == -1)
clear_tv(&var1);
return NULL;
}
if (len == -1)
lp->ll_newkey = vim_strsave(key);
else
lp->ll_newkey = vim_strnsave(key, len);
if (len == -1)
clear_tv(&var1);
if (lp->ll_newkey == NULL)
p = NULL;
break;
}
/* existing variable, need to check if it can be changed */
else if (var_check_ro(lp->ll_di->di_flags, name))
return NULL;
if (len == -1)
clear_tv(&var1);
lp->ll_tv = &lp->ll_di->di_tv;
}
else
{
/*
* Get the number and item for the only or first index of the List.
*/
if (empty1)
lp->ll_n1 = 0;
else
{
lp->ll_n1 = get_tv_number(&var1); /* is number or string */
clear_tv(&var1);
}
lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
if (lp->ll_li == NULL)
{
if (lp->ll_n1 < 0)
{
lp->ll_n1 = 0;
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
}
}
if (lp->ll_li == NULL)
{
if (lp->ll_range && !lp->ll_empty2)
clear_tv(&var2);
if (!quiet)
EMSGN(_(e_listidx), lp->ll_n1);
return NULL;
}
/*
* May need to find the item or absolute index for the second
* index of a range.
* When no index given: "lp->ll_empty2" is TRUE.
* Otherwise "lp->ll_n2" is set to the second index.
*/
if (lp->ll_range && !lp->ll_empty2)
{
lp->ll_n2 = get_tv_number(&var2); /* is number or string */
clear_tv(&var2);
if (lp->ll_n2 < 0)
{
ni = list_find(lp->ll_list, lp->ll_n2);
if (ni == NULL)
{
if (!quiet)
EMSGN(_(e_listidx), lp->ll_n2);
return NULL;
}
lp->ll_n2 = list_idx_of_item(lp->ll_list, ni);
}
/* Check that lp->ll_n2 isn't before lp->ll_n1. */
if (lp->ll_n1 < 0)
lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li);
if (lp->ll_n2 < lp->ll_n1)
{
if (!quiet)
EMSGN(_(e_listidx), lp->ll_n2);
return NULL;
}
}
lp->ll_tv = &lp->ll_li->li_tv;
}
}
return p;
}
/*
* Clear lval "lp" that was filled by get_lval().
*/
static void
clear_lval(lp)
lval_T *lp;
{
vim_free(lp->ll_exp_name);
vim_free(lp->ll_newkey);
}
/*
* Set a variable that was parsed by get_lval() to "rettv".
* "endp" points to just after the parsed name.
* "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=".
*/
static void
set_var_lval(lp, endp, rettv, copy, op)
lval_T *lp;
char_u *endp;
typval_T *rettv;
int copy;
char_u *op;
{
int cc;
listitem_T *ri;
dictitem_T *di;
if (lp->ll_tv == NULL)
{
if (!check_changedtick(lp->ll_name))
{
cc = *endp;
*endp = NUL;
if (op != NULL && *op != '=')
{
typval_T tv;
/* handle +=, -= and .= */
if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
&tv, TRUE) == OK)
{
if (tv_op(&tv, rettv, op) == OK)
set_var(lp->ll_name, &tv, FALSE);
clear_tv(&tv);
}
}
else
set_var(lp->ll_name, rettv, copy);
*endp = cc;
}
}
else if (tv_check_lock(lp->ll_newkey == NULL
? lp->ll_tv->v_lock
: lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name))
;
else if (lp->ll_range)
{
/*
* Assign the List values to the list items.
*/
for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
{
if (op != NULL && *op != '=')
tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
else
{
clear_tv(&lp->ll_li->li_tv);
copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
}
ri = ri->li_next;
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
break;
if (lp->ll_li->li_next == NULL)
{
/* Need to add an empty item. */
if (list_append_number(lp->ll_list, 0) == FAIL)
{
ri = NULL;
break;
}
}
lp->ll_li = lp->ll_li->li_next;
++lp->ll_n1;
}
if (ri != NULL)
EMSG(_("E710: List value has more items than target"));
else if (lp->ll_empty2
? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
: lp->ll_n1 != lp->ll_n2)
EMSG(_("E711: List value has not enough items"));
}
else
{
/*
* Assign to a List or Dictionary item.
*/
if (lp->ll_newkey != NULL)
{
if (op != NULL && *op != '=')
{
EMSG2(_(e_letwrong), op);
return;
}
/* Need to add an item to the Dictionary. */
di = dictitem_alloc(lp->ll_newkey);
if (di == NULL)
return;
if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL)
{
vim_free(di);
return;
}
lp->ll_tv = &di->di_tv;
}
else if (op != NULL && *op != '=')
{
tv_op(lp->ll_tv, rettv, op);
return;
}
else
clear_tv(lp->ll_tv);
/*
* Assign the value to the variable or list item.
*/
if (copy)
copy_tv(rettv, lp->ll_tv);
else
{
*lp->ll_tv = *rettv;
lp->ll_tv->v_lock = 0;
init_tv(rettv);
}
}
}
/*
* Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2"
* Returns OK or FAIL.
*/
static int
tv_op(tv1, tv2, op)
typval_T *tv1;
typval_T *tv2;
char_u *op;
{
long n;
char_u numbuf[NUMBUFLEN];
char_u *s;
/* Can't do anything with a Funcref or a Dict on the right. */
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT)
{
switch (tv1->v_type)
{
case VAR_DICT:
case VAR_FUNC:
break;
case VAR_LIST:
if (*op != '+' || tv2->v_type != VAR_LIST)
break;
/* List += List */
if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL)
list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
return OK;
case VAR_NUMBER:
case VAR_STRING:
if (tv2->v_type == VAR_LIST)
break;
if (*op == '+' || *op == '-')
{
/* nr += nr or nr -= nr*/
n = get_tv_number(tv1);
#ifdef FEAT_FLOAT
if (tv2->v_type == VAR_FLOAT)
{
float_T f = n;
if (*op == '+')
f += tv2->vval.v_float;
else
f -= tv2->vval.v_float;
clear_tv(tv1);
tv1->v_type = VAR_FLOAT;
tv1->vval.v_float = f;
}
else
#endif
{
if (*op == '+')
n += get_tv_number(tv2);
else
n -= get_tv_number(tv2);
clear_tv(tv1);
tv1->v_type = VAR_NUMBER;
tv1->vval.v_number = n;
}
}
else
{
if (tv2->v_type == VAR_FLOAT)
break;
/* str .= str */
s = get_tv_string(tv1);
s = concat_str(s, get_tv_string_buf(tv2, numbuf));
clear_tv(tv1);
tv1->v_type = VAR_STRING;
tv1->vval.v_string = s;
}
return OK;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
{
float_T f;
if (*op == '.' || (tv2->v_type != VAR_FLOAT
&& tv2->v_type != VAR_NUMBER
&& tv2->v_type != VAR_STRING))
break;
if (tv2->v_type == VAR_FLOAT)
f = tv2->vval.v_float;
else
f = get_tv_number(tv2);
if (*op == '+')
tv1->vval.v_float += f;
else
tv1->vval.v_float -= f;
}
return OK;
#endif
}
}
EMSG2(_(e_letwrong), op);
return FAIL;
}
/*
* Add a watcher to a list.
*/
static void
list_add_watch(l, lw)
list_T *l;
listwatch_T *lw;
{
lw->lw_next = l->lv_watch;
l->lv_watch = lw;
}
/*
* Remove a watcher from a list.
* No warning when it isn't found...
*/
static void
list_rem_watch(l, lwrem)
list_T *l;
listwatch_T *lwrem;
{
listwatch_T *lw, **lwp;
lwp = &l->lv_watch;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
{
if (lw == lwrem)
{
*lwp = lw->lw_next;
break;
}
lwp = &lw->lw_next;
}
}
/*
* Just before removing an item from a list: advance watchers to the next
* item.
*/
static void
list_fix_watch(l, item)
list_T *l;
listitem_T *item;
{
listwatch_T *lw;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
if (lw->lw_item == item)
lw->lw_item = item->li_next;
}
/*
* Evaluate the expression used in a ":for var in expr" command.
* "arg" points to "var".
* Set "*errp" to TRUE for an error, FALSE otherwise;
* Return a pointer that holds the info. Null when there is an error.
*/
void *
eval_for_line(arg, errp, nextcmdp, skip)
char_u *arg;
int *errp;
char_u **nextcmdp;
int skip;
{
forinfo_T *fi;
char_u *expr;
typval_T tv;
list_T *l;
*errp = TRUE; /* default: there is an error */
fi = (forinfo_T *)alloc_clear(sizeof(forinfo_T));
if (fi == NULL)
return NULL;
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL)
return fi;
expr = skipwhite(expr);
if (expr[0] != 'i' || expr[1] != 'n' || !vim_iswhite(expr[2]))
{
EMSG(_("E690: Missing \"in\" after :for"));
return fi;
}
if (skip)
++emsg_skip;
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK)
{
*errp = FALSE;
if (!skip)
{
l = tv.vval.v_list;
if (tv.v_type != VAR_LIST || l == NULL)
{
EMSG(_(e_listreq));
clear_tv(&tv);
}
else
{
/* No need to increment the refcount, it's already set for the
* list being used in "tv". */
fi->fi_list = l;
list_add_watch(l, &fi->fi_lw);
fi->fi_lw.lw_item = l->lv_first;
}
}
}
if (skip)
--emsg_skip;
return fi;
}
/*
* Use the first item in a ":for" list. Advance to the next.
* Assign the values to the variable (list). "arg" points to the first one.
* Return TRUE when a valid item was found, FALSE when at end of list or
* something wrong.
*/
int
next_for_item(fi_void, arg)
void *fi_void;
char_u *arg;
{
forinfo_T *fi = (forinfo_T *)fi_void;
int result;
listitem_T *item;
item = fi->fi_lw.lw_item;
if (item == NULL)
result = FALSE;
else
{
fi->fi_lw.lw_item = item->li_next;
result = (ex_let_vars(arg, &item->li_tv, TRUE,
fi->fi_semicolon, fi->fi_varcount, NULL) == OK);
}
return result;
}
/*
* Free the structure used to store info used by ":for".
*/
void
free_for_info(fi_void)
void *fi_void;
{
forinfo_T *fi = (forinfo_T *)fi_void;
if (fi != NULL && fi->fi_list != NULL)
{
list_rem_watch(fi->fi_list, &fi->fi_lw);
list_unref(fi->fi_list);
}
vim_free(fi);
}
#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;
char_u *p;
if (cmdidx == CMD_let)
{
xp->xp_context = EXPAND_USER_VARS;
if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL)
{
/* ":let var1 var2 ...": find last space. */
for (p = arg + STRLEN(arg); p >= arg; )
{
xp->xp_pattern = p;
mb_ptr_back(arg, p);
if (vim_iswhite(*p))
break;
}
return;
}
}
else
xp->xp_context = 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;
if ((c == 'l' || c == 'g') && xp->xp_pattern[2] == ':')
xp->xp_pattern += 2;
}
}
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 */
{
/* Trick: '' is like stopping and starting a 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
/* Doesn't look like something valid, expand as an expression
* anyway. */
xp->xp_context = EXPAND_EXPRESSION;
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 *name;
char_u *tofree;
int len;
typval_T rettv;
linenr_T lnum;
int doesrange;
int failed = FALSE;
funcdict_T fudi;
if (eap->skip)
{
/* trans_function_name() doesn't work well when skipping, use eval0()
* instead to skip to any following command, e.g. for:
* :if 0 | call dict.foo().bar() | endif */
++emsg_skip;
if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL)
clear_tv(&rettv);
--emsg_skip;
return;
}
tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi);
if (fudi.fd_newkey != NULL)
{
/* Still need to give an error message for missing key. */
EMSG2(_(e_dictkey), fudi.fd_newkey);
vim_free(fudi.fd_newkey);
}
if (tofree == NULL)
return;
/* Increase refcount on dictionary, it could get deleted when evaluating
* the arguments. */
if (fudi.fd_dict != NULL)
++fudi.fd_dict->dv_refcount;
/* If it is the name of a variable of type VAR_FUNC use its contents. */
len = (int)STRLEN(tofree);
name = deref_func_name(tofree, &len);
/* Skip white space to allow ":call func ()". Not good, but required for
* backward compatibility. */
startarg = skipwhite(arg);
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
if (*startarg != '(')
{
EMSG2(_("E107: Missing parentheses: %s"), eap->arg);
goto end;
}
/*
* When skipping, evaluate the function once, to find the end of the
* arguments.
* When the function takes a range, this is discovered after the first
* call, and the loop is broken.
*/
if (eap->skip)
{
++emsg_skip;
lnum = eap->line2; /* do it once, also with an invalid range */
}
else
lnum = eap->line1;
for ( ; lnum <= eap->line2; ++lnum)
{
if (!eap->skip && eap->addr_count > 0)
{
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
#ifdef FEAT_VIRTUALEDIT
curwin->w_cursor.coladd = 0;
#endif
}
arg = startarg;
if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
eap->line1, eap->line2, &doesrange,
!eap->skip, fudi.fd_dict) == FAIL)
{
failed = TRUE;
break;
}
/* Handle a function returning a Funcref, Dictionary or List. */
if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL)
{
failed = TRUE;
break;
}
clear_tv(&rettv);
if (doesrange || eap->skip)
break;
/* Stop when immediately aborting on error, or when an interrupt
* occurred or an exception was thrown but not caught.
* get_func_tv() returned OK, so that the check for trailing
* characters below is executed. */
if (aborting())
break;
}
if (eap->skip)
--emsg_skip;
if (!failed)
{
/* Check for trailing illegal characters and a following command. */
if (!ends_excmd(*arg))
{
emsg_severe = TRUE;
EMSG(_(e_trailing));
}
else
eap->nextcmd = check_nextcmd(arg);
}
end:
dict_unref(fudi.fd_dict);
vim_free(tofree);
}
/*
* ":unlet[!] var1 ... " command.
*/
void
ex_unlet(eap)
exarg_T *eap;
{
ex_unletlock(eap, eap->arg, 0);
}
/*
* ":lockvar" and ":unlockvar" commands
*/
void
ex_lockvar(eap)
exarg_T *eap;
{
char_u *arg = eap->arg;
int deep = 2;
if (eap->forceit)
deep = -1;
else if (vim_isdigit(*arg))
{
deep = getdigits(&arg);
arg = skipwhite(arg);
}
ex_unletlock(eap, arg, deep);
}
/*
* ":unlet", ":lockvar" and ":unlockvar" are quite similar.
*/
static void
ex_unletlock(eap, argstart, deep)
exarg_T *eap;
char_u *argstart;
int deep;
{
char_u *arg = argstart;
char_u *name_end;
int error = FALSE;
lval_T lv;
do
{
/* Parse the name and find the end. */
name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, FALSE,
FNE_CHECK_START);
if (lv.ll_name == NULL)
error = TRUE; /* error but continue parsing */
if (name_end == NULL || (!vim_iswhite(*name_end)
&& !ends_excmd(*name_end)))
{
if (name_end != NULL)
{
emsg_severe = TRUE;
EMSG(_(e_trailing));
}
if (!(eap->skip || error))
clear_lval(&lv);
break;
}
if (!error && !eap->skip)
{
if (eap->cmdidx == CMD_unlet)
{
if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL)
error = TRUE;
}
else
{
if (do_lock_var(&lv, name_end, deep,
eap->cmdidx == CMD_lockvar) == FAIL)
error = TRUE;
}
}
if (!eap->skip)
clear_lval(&lv);
arg = skipwhite(name_end);
} while (!ends_excmd(*arg));
eap->nextcmd = check_nextcmd(arg);
}
static int
do_unlet_var(lp, name_end, forceit)
lval_T *lp;
char_u *name_end;
int forceit;
{
int ret = OK;
int cc;
if (lp->ll_tv == NULL)
{
cc = *name_end;
*name_end = NUL;
/* Normal name or expanded name. */
if (check_changedtick(lp->ll_name))
ret = FAIL;
else if (do_unlet(lp->ll_name, forceit) == FAIL)
ret = FAIL;
*name_end = cc;
}
else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name))
return FAIL;
else if (lp->ll_range)
{
listitem_T *li;
/* Delete a range of List items. */
while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
{
li = lp->ll_li->li_next;
listitem_remove(lp->ll_list, lp->ll_li);
lp->ll_li = li;
++lp->ll_n1;
}
}
else
{
if (lp->ll_list != NULL)
/* unlet a List item. */
listitem_remove(lp->ll_list, lp->ll_li);
else
/* unlet a Dictionary item. */
dictitem_remove(lp->ll_dict, lp->ll_di);
}
return ret;
}
/*
* "unlet" a variable. Return OK if it existed, FAIL if not.
* When "forceit" is TRUE don't complain if the variable doesn't exist.
*/
int
do_unlet(name, forceit)
char_u *name;
int forceit;
{
hashtab_T *ht;
hashitem_T *hi;
char_u *varname;
dictitem_T *di;
ht = find_var_ht(name, &varname);
if (ht != NULL && *varname != NUL)
{
hi = hash_find(ht, varname);
if (!HASHITEM_EMPTY(hi))
{
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name)
|| var_check_ro(di->di_flags, name))
return FAIL;
delete_var(ht, hi);
return OK;
}
}
if (forceit)
return OK;
EMSG2(_("E108: No such variable: \"%s\""), name);
return FAIL;
}
/*
* Lock or unlock variable indicated by "lp".
* "deep" is the levels to go (-1 for unlimited);
* "lock" is TRUE for ":lockvar", FALSE for ":unlockvar".
*/
static int
do_lock_var(lp, name_end, deep, lock)
lval_T *lp;
char_u *name_end;
int deep;
int lock;
{
int ret = OK;
int cc;
dictitem_T *di;
if (deep == 0) /* nothing to do */
return OK;
if (lp->ll_tv == NULL)
{
cc = *name_end;
*name_end = NUL;
/* Normal name or expanded name. */
if (check_changedtick(lp->ll_name))
ret = FAIL;
else
{
di = find_var(lp->ll_name, NULL);
if (di == NULL)
ret = FAIL;
else
{
if (lock)
di->di_flags |= DI_FLAGS_LOCK;
else
di->di_flags &= ~DI_FLAGS_LOCK;
item_lock(&di->di_tv, deep, lock);
}
}
*name_end = cc;
}
else if (lp->ll_range)
{
listitem_T *li = lp->ll_li;
/* (un)lock a range of List items. */
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
{
item_lock(&li->li_tv, deep, lock);
li = li->li_next;
++lp->ll_n1;
}
}
else if (lp->ll_list != NULL)
/* (un)lock a List item. */
item_lock(&lp->ll_li->li_tv, deep, lock);
else
/* un(lock) a Dictionary item. */
item_lock(&lp->ll_di->di_tv, deep, lock);
return ret;
}
/*
* Lock or unlock an item. "deep" is nr of levels to go.
*/
static void
item_lock(tv, deep, lock)
typval_T *tv;
int deep;
int lock;
{
static int recurse = 0;
list_T *l;
listitem_T *li;
dict_T *d;
hashitem_T *hi;
int todo;
if (recurse >= DICT_MAXNEST)
{
EMSG(_("E743: variable nested too deep for (un)lock"));
return;
}
if (deep == 0)
return;
++recurse;
/* lock/unlock the item itself */
if (lock)
tv->v_lock |= VAR_LOCKED;
else
tv->v_lock &= ~VAR_LOCKED;
switch (tv->v_type)
{
case VAR_LIST:
if ((l = tv->vval.v_list) != NULL)
{
if (lock)
l->lv_lock |= VAR_LOCKED;
else
l->lv_lock &= ~VAR_LOCKED;
if (deep < 0 || deep > 1)
/* recursive: lock/unlock the items the List contains */
for (li = l->lv_first; li != NULL; li = li->li_next)
item_lock(&li->li_tv, deep - 1, lock);
}
break;
case VAR_DICT:
if ((d = tv->vval.v_dict) != NULL)
{
if (lock)
d->dv_lock |= VAR_LOCKED;
else
d->dv_lock &= ~VAR_LOCKED;
if (deep < 0 || deep > 1)
{
/* recursive: lock/unlock the items the List contains */
todo = (int)d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
--todo;
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
}
}
}
}
}
--recurse;
}
/*
* Return TRUE if typeval "tv" is locked: Either that value is locked itself
* or it refers to a List or Dictionary that is locked.
*/
static int
tv_islocked(tv)
typval_T *tv;
{
return (tv->v_lock & VAR_LOCKED)
|| (tv->v_type == VAR_LIST
&& tv->vval.v_list != NULL
&& (tv->vval.v_list->lv_lock & VAR_LOCKED))
|| (tv->v_type == VAR_DICT
&& tv->vval.v_dict != NULL
&& (tv->vval.v_dict->dv_lock & VAR_LOCKED));
}
#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO)
/*
* Delete all "menutrans_" variables.
*/
void
del_menutrans_vars()
{
hashitem_T *hi;
int todo;
hash_lock(&globvarht);
todo = (int)globvarht.ht_used;
for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
--todo;
if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0)
delete_var(&globvarht, hi);
}
}
hash_unlock(&globvarht);
}
#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.
*/
char_u *
get_user_var_name(xp, idx)
expand_T *xp;
int idx;
{
static long_u gdone;
static long_u bdone;
static long_u wdone;
#ifdef FEAT_WINDOWS
static long_u tdone;
#endif
static int vidx;
static hashitem_T *hi;
hashtab_T *ht;
if (idx == 0)
{
gdone = bdone = wdone = vidx = 0;
#ifdef FEAT_WINDOWS
tdone = 0;
#endif
}
/* Global variables */
if (gdone < globvarht.ht_used)
{
if (gdone++ == 0)
hi = globvarht.ht_array;
else
++hi;
while (HASHITEM_EMPTY(hi))
++hi;
if (STRNCMP("g:", xp->xp_pattern, 2) == 0)
return cat_prefix_varname('g', hi->hi_key);
return hi->hi_key;
}
/* b: variables */
ht = &curbuf->b_vars.dv_hashtab;
if (bdone < ht->ht_used)
{
if (bdone++ == 0)
hi = ht->ht_array;
else
++hi;
while (HASHITEM_EMPTY(hi))
++hi;
return cat_prefix_varname('b', hi->hi_key);
}
if (bdone == ht->ht_used)
{
++bdone;
return (char_u *)"b:changedtick";
}
/* w: variables */
ht = &curwin->w_vars.dv_hashtab;
if (wdone < ht->ht_used)
{
if (wdone++ == 0)
hi = ht->ht_array;
else
++hi;
while (HASHITEM_EMPTY(hi))
++hi;
return cat_prefix_varname('w', hi->hi_key);
}
#ifdef FEAT_WINDOWS
/* t: variables */
ht = &curtab->tp_vars.dv_hashtab;
if (tdone < ht->ht_used)
{
if (tdone++ == 0)
hi = ht->ht_array;
else
++hi;
while (HASHITEM_EMPTY(hi))
++hi;
return cat_prefix_varname('t', hi->hi_key);
}
#endif
/* v: variables */
if (vidx < VV_LEN)
return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_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 rettv 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.
* Put the result in "rettv" when returning OK and "evaluate" is TRUE.
* Note: "rettv.v_lock" is not set.
* Return OK or FAIL.
*/
static int
eval0(arg, rettv, nextcmd, evaluate)
char_u *arg;
typval_T *rettv;
char_u **nextcmd;
int evaluate;
{
int ret;
char_u *p;
p = skipwhite(arg);
ret = eval1(&p, rettv, evaluate);
if (ret == FAIL || !ends_excmd(*p))
{
if (ret != FAIL)
clear_tv(rettv);
/*
* 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:
* expr2 ? expr1 : expr1
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Note: "rettv.v_lock" is not set.
*
* Return OK or FAIL.
*/
static int
eval1(arg, rettv, evaluate)
char_u **arg;
typval_T *rettv;
int evaluate;
{
int result;
typval_T var2;
/*
* Get the first variable.
*/
if (eval2(arg, rettv, evaluate) == FAIL)
return FAIL;
if ((*arg)[0] == '?')
{
result = FALSE;
if (evaluate)
{
int error = FALSE;
if (get_tv_number_chk(rettv, &error) != 0)
result = TRUE;
clear_tv(rettv);
if (error)
return FAIL;
}
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, rettv, evaluate && result) == FAIL) /* recursive! */
return FAIL;
/*
* Check for the ":".
*/
if ((*arg)[0] != ':')
{
EMSG(_("E109: Missing ':' after '?'"));
if (evaluate && result)
clear_tv(rettv);
return FAIL;
}
/*
* Get the third variable.
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2, evaluate && !result) == FAIL) /* recursive! */
{
if (evaluate && result)
clear_tv(rettv);
return FAIL;
}
if (evaluate && !result)
*rettv = 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, rettv, evaluate)
char_u **arg;
typval_T *rettv;
int evaluate;
{
typval_T var2;
long result;
int first;
int error = FALSE;
/*
* Get the first variable.
*/
if (eval3(arg, rettv, evaluate) == FAIL)
return FAIL;
/*
* Repeat until there is no following "||".
*/
first = TRUE;
result = FALSE;
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
if (evaluate && first)
{
if (get_tv_number_chk(rettv, &error) != 0)
result = TRUE;
clear_tv(rettv);
if (error)
return FAIL;
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_tv_number_chk(&var2, &error) != 0)
result = TRUE;
clear_tv(&var2);
if (error)
return FAIL;
}
if (evaluate)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_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, rettv, evaluate)
char_u **arg;
typval_T *rettv;
int evaluate;
{
typval_T var2;
long result;
int first;
int error = FALSE;
/*
* Get the first variable.
*/
if (eval4(arg, rettv, evaluate) == FAIL)
return FAIL;
/*
* Repeat until there is no following "&&".
*/
first = TRUE;
result = TRUE;
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
if (evaluate && first)
{
if (get_tv_number_chk(rettv, &error) == 0)
result = FALSE;
clear_tv(rettv);
if (error)
return FAIL;
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_tv_number_chk(&var2, &error) == 0)
result = FALSE;
clear_tv(&var2);
if (error)
return FAIL;
}
if (evaluate)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = result;
}
}
return OK;
}
/*
* Handle third level expression:
* var1 == var2
* var1 =~ var2
* var1 != var2
* var1 !~ var2
* var1 > var2
* var1 >= var2
* var1 < var2
* var1 <= var2
* var1 is var2
* var1 isnot 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, rettv, evaluate)
char_u **arg;
typval_T *rettv;
int evaluate;
{
typval_T var2;
char_u *p;
int i;
exptype_T type = TYPE_UNKNOWN;
int type_is = FALSE; /* TRUE for "is" and "isnot" */
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, rettv, 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] !=