Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

22752 lines (20839 sloc) 512.807 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.
*/
#if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
# include "vimio.h" /* for mch_open(), must be before vim.h */
#endif
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
#ifdef AMIGA
# include <time.h> /* for strftime() */
#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 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), VV_RO},
{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},
};
/* 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));
#if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
static int call_vim_function __ARGS((char_u *func, int argc, char_u **argv, int safe, typval_T *rettv));
#endif
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 listitem_T *listitem_alloc __ARGS((void));
static void listitem_free __ARGS((listitem_T *item));
static void listitem_remove __ARGS((list_T *l, listitem_T *item));
static long list_len __ARGS((list_T *l));
static int list_equal __ARGS((list_T *l1, list_T *l2, int ic));
static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic));
static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic));
static listitem_T *list_find __ARGS((list_T *l, long n));
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 void list_append __ARGS((list_T *l, listitem_T *item));
static int list_append_tv __ARGS((list_T *l, typval_T *tv));
static int list_append_number __ARGS((list_T *l, varnumber_T n));
static int list_insert_tv __ARGS((list_T *l, typval_T *tv, listitem_T *item));
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 void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2));
static char_u *list2string __ARGS((typval_T *tv, int copyID));
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 void set_ref_in_ht __ARGS((hashtab_T *ht, int copyID));
static void set_ref_in_list __ARGS((list_T *l, int copyID));
static void set_ref_in_item __ARGS((typval_T *tv, int copyID));
static void dict_unref __ARGS((dict_T *d));
static void dict_free __ARGS((dict_T *d, int recurse));
static dictitem_T *dictitem_alloc __ARGS((char_u *key));
static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
static void dictitem_free __ARGS((dictitem_T *item));
static dict_T *dict_copy __ARGS((dict_T *orig, int deep, int copyID));
static int dict_add __ARGS((dict_T *d, dictitem_T *item));
static long dict_len __ARGS((dict_T *d));
static dictitem_T *dict_find __ARGS((dict_T *d, char_u *key, int len));
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 *name, 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));
#endif
static void f_add __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_atan __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));
#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));
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));
#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_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_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_log10 __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));
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_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));
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_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));
#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));
#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_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_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));
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_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 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 tv_check_lock __ARGS((int lock, char_u *name));
static void copy_tv __ARGS((typval_T *from, typval_T *to));
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 func_unref __ARGS((char_u *name));
static void func_ref __ARGS((char_u *name));
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));
/* 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);
init_var_dict(&vimvardict, &vimvars_var);
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);
}
#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);
/* script-local variables */
for (i = 1; i <= ga_scripts.ga_len; ++i)
vars_clear(&SCRIPT_VARS(i));
ga_clear(&ga_scripts);
free_scriptnames();
/* global variables */
vars_clear(&globvarht);
/* autoloaded script names */
ga_clear_strings(&ga_loaded);
/* 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;
/* Make sure a valid variable name is specified */
if (!eval_isnamec1(*name))
{
EMSG(_(e_invarg));
return FAIL;
}
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)
{
if (redir_endp != NULL && *redir_endp != NUL)
/* Trailing characters are present after the variable name */
EMSG(_(e_trailing));
else
EMSG(_(e_invarg));
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 *)"=");
err = did_emsg;
did_emsg |= save_emsg;
if (err)
{
var_redir_stop();
return FAIL;
}
if (redir_lval->ll_newkey != NULL)
{
/* Dictionary item was created, don't do it again. */
vim_free(redir_lval->ll_newkey);
redir_lval->ll_newkey = NULL;
}
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.
*/
void
var_redir_stop()
{
typval_T tv;
if (redir_lval != NULL)
{
/* Append the trailing NUL. */
ga_append(&redir_ga, NUL);
/* Assign the text to the variable. */
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)".");
vim_free(tv.vval.v_string);
clear_lval(redir_lval);
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);
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.
*/
static int
call_vim_function(func, argc, argv, safe, rettv)
char_u *func;
int argc;
char_u **argv;
int safe; /* use the sandbox */
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;
}
/* 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;
if (call_vim_function(func, argc, argv, safe, &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;
if (call_vim_function(func, argc, argv, safe, &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;
if (call_vim_function(func, argc, argv, safe, &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
{
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);
if (lp->ll_di == 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;
}
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);
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)
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)
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;
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;
}
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;
{