Patch 8.1.1939

15 views
Skip to first unread message

Bram Moolenaar

unread,
Aug 29, 2019, 4:10:23 PM8/29/19
to vim...@googlegroups.com

Patch 8.1.1939
Problem: Code for handling v: variables in generic eval file.
Solution: Move v: variables to evalvars.c. (Yegappan Lakshmanan,
closes #4872)
Files: src/eval.c, src/evalvars.c, src/proto/eval.pro,
src/proto/evalvars.pro


*** ../vim-8.1.1938/src/eval.c 2019-08-27 22:48:12.737480696 +0200
--- src/eval.c 2019-08-29 22:02:16.891931613 +0200
***************
*** 29,42 ****

#define NAMESPACE_CHAR (char_u *)"abglstvw"

- static dictitem_T globvars_var; /* variable used for g: */
-
- /*
- * 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.
--- 29,34 ----
***************
*** 44,63 ****
*/
static int current_copyID = 0;

- /*
- * 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" */

/* The names of packages that once were loaded are remembered. */
--- 36,41 ----
***************
*** 76,215 ****
blob_T *fi_blob; /* blob being used */
} forinfo_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.
- */
-
- /* 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}}
-
- static struct vimvar
- {
- char *vv_name; /* name of variable, without v: */
- dictitem16_T vv_di; /* value and name for key (max 16 chars!) */
- 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_winid", VAR_NUMBER), VV_RO},
- {VV_NAME("beval_lnum", VAR_NUMBER), VV_RO},
- {VV_NAME("beval_col", VAR_NUMBER), VV_RO},
- {VV_NAME("beval_text", VAR_STRING), VV_RO},
- {VV_NAME("scrollstart", VAR_STRING), 0},
- {VV_NAME("swapname", VAR_STRING), VV_RO},
- {VV_NAME("swapchoice", VAR_STRING), 0},
- {VV_NAME("swapcommand", VAR_STRING), VV_RO},
- {VV_NAME("char", VAR_STRING), 0},
- {VV_NAME("mouse_win", VAR_NUMBER), 0},
- {VV_NAME("mouse_winid", 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("hlsearch", VAR_NUMBER), 0},
- {VV_NAME("oldfiles", VAR_LIST), 0},
- {VV_NAME("windowid", VAR_NUMBER), VV_RO},
- {VV_NAME("progpath", VAR_STRING), VV_RO},
- {VV_NAME("completed_item", VAR_DICT), VV_RO},
- {VV_NAME("option_new", VAR_STRING), VV_RO},
- {VV_NAME("option_old", VAR_STRING), VV_RO},
- {VV_NAME("option_oldlocal", VAR_STRING), VV_RO},
- {VV_NAME("option_oldglobal", VAR_STRING), VV_RO},
- {VV_NAME("option_command", VAR_STRING), VV_RO},
- {VV_NAME("option_type", VAR_STRING), VV_RO},
- {VV_NAME("errors", VAR_LIST), 0},
- {VV_NAME("false", VAR_SPECIAL), VV_RO},
- {VV_NAME("true", VAR_SPECIAL), VV_RO},
- {VV_NAME("null", VAR_SPECIAL), VV_RO},
- {VV_NAME("none", VAR_SPECIAL), VV_RO},
- {VV_NAME("vim_did_enter", VAR_NUMBER), VV_RO},
- {VV_NAME("testing", VAR_NUMBER), 0},
- {VV_NAME("t_number", VAR_NUMBER), VV_RO},
- {VV_NAME("t_string", VAR_NUMBER), VV_RO},
- {VV_NAME("t_func", VAR_NUMBER), VV_RO},
- {VV_NAME("t_list", VAR_NUMBER), VV_RO},
- {VV_NAME("t_dict", VAR_NUMBER), VV_RO},
- {VV_NAME("t_float", VAR_NUMBER), VV_RO},
- {VV_NAME("t_bool", VAR_NUMBER), VV_RO},
- {VV_NAME("t_none", VAR_NUMBER), VV_RO},
- {VV_NAME("t_job", VAR_NUMBER), VV_RO},
- {VV_NAME("t_channel", VAR_NUMBER), VV_RO},
- {VV_NAME("t_blob", VAR_NUMBER), VV_RO},
- {VV_NAME("termrfgresp", VAR_STRING), VV_RO},
- {VV_NAME("termrbgresp", VAR_STRING), VV_RO},
- {VV_NAME("termu7resp", VAR_STRING), VV_RO},
- {VV_NAME("termstyleresp", VAR_STRING), VV_RO},
- {VV_NAME("termblinkresp", VAR_STRING), VV_RO},
- {VV_NAME("event", VAR_DICT), VV_RO},
- {VV_NAME("versionlong", VAR_NUMBER), VV_RO},
- {VV_NAME("echospace", VAR_NUMBER), VV_RO},
- };
-
- /* shorthand */
- #define vv_type vv_di.di_tv.v_type
- #define vv_nr vv_di.di_tv.vval.v_number
- #define vv_float vv_di.di_tv.vval.v_float
- #define vv_str vv_di.di_tv.vval.v_string
- #define vv_list vv_di.di_tv.vval.v_list
- #define vv_dict vv_di.di_tv.vval.v_dict
- #define vv_blob vv_di.di_tv.vval.v_blob
- #define vv_tv vv_di.di_tv
-
- static dictitem_T vimvars_var; /* variable used for v: */
- #define vimvarht vimvardict.dv_hashtab
-
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
static int eval2(char_u **arg, typval_T *rettv, int evaluate);
static int eval3(char_u **arg, typval_T *rettv, int evaluate);
--- 54,59 ----
***************
*** 224,236 ****
static int free_unref_items(int copyID);
static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end);
- static void check_vars(char_u *name, int len);
- static typval_T *alloc_string_tv(char_u *string);
static int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);

- /* for VIM_VERSION_ defines */
- #include "version.h"
-
/*
* Return "n1" divided by "n2", taking care of dividing by zero.
*/
--- 68,75 ----
***************
*** 264,270 ****
return (n2 == 0) ? 0 : (n1 % n2);
}

-
#if defined(EBCDIC) || defined(PROTO)
/*
* Compare struct fst by function name.
--- 103,108 ----
***************
*** 292,366 ****
}
#endif

-
/*
* Initialize the global and v: variables.
*/
void
eval_init(void)
{
! int i;
! struct vimvar *p;
!
! init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
! init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
! vimvardict.dv_lock = VAR_FIXED;
! hash_init(&compat_hashtab);
func_init();

- for (i = 0; i < VV_LEN; ++i)
- {
- p = &vimvars[i];
- if (STRLEN(p->vv_name) > DICTITEM16_KEY_LEN)
- {
- iemsg("INTERNAL: name too long, increase size of dictitem16_T");
- getout(1);
- }
- 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);
- }
- vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- vimvars[VV_VERSIONLONG].vv_nr = VIM_VERSION_100 * 10000 + highest_patch();
-
- set_vim_var_nr(VV_SEARCHFORWARD, 1L);
- set_vim_var_nr(VV_HLSEARCH, 1L);
- set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
- set_vim_var_list(VV_ERRORS, list_alloc());
- set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));
-
- set_vim_var_nr(VV_FALSE, VVAL_FALSE);
- set_vim_var_nr(VV_TRUE, VVAL_TRUE);
- set_vim_var_nr(VV_NONE, VVAL_NONE);
- set_vim_var_nr(VV_NULL, VVAL_NULL);
-
- set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
- set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
- set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
- set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST);
- set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
- set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
- set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
- set_vim_var_nr(VV_TYPE_NONE, VAR_TYPE_NONE);
- set_vim_var_nr(VV_TYPE_JOB, VAR_TYPE_JOB);
- set_vim_var_nr(VV_TYPE_CHANNEL, VAR_TYPE_CHANNEL);
- set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
-
- set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
-
- set_reg_var(0); /* default for v:register is not 0 but '"' */
-
#ifdef EBCDIC
/*
* Sort the function table, to enable binary search.
--- 130,144 ----
}
#endif

/*
* Initialize the global and v: variables.
*/
void
eval_init(void)
{
! evalvars_init();
func_init();

#ifdef EBCDIC
/*
* Sort the function table, to enable binary search.
***************
*** 373,414 ****
void
eval_clear(void)
{
! 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_CLEAR(p->vv_str);
! else if (p->vv_di.di_tv.v_type == VAR_LIST)
! {
! list_unref(p->vv_list);
! p->vv_list = NULL;
! }
! }
! hash_clear(&vimvarht);
! hash_init(&vimvarht); /* garbage_collect() will access it */
! hash_clear(&compat_hashtab);

free_scriptnames();
free_locales();

- /* global variables */
- vars_clear(&globvarht);
-
/* autoloaded script names */
ga_clear_strings(&ga_loaded);

- /* Script-local variables. First clear all the variables and in a second
- * loop free the scriptvar_T, because a variable in one script might hold
- * a reference to the whole scope of another script. */
- for (i = 1; i <= ga_scripts.ga_len; ++i)
- vars_clear(&SCRIPT_VARS(i));
- for (i = 1; i <= ga_scripts.ga_len; ++i)
- vim_free(SCRIPT_SV(i));
- ga_clear(&ga_scripts);
-
// unreferenced lists and dicts
(void)garbage_collect(FALSE);

--- 151,164 ----
void
eval_clear(void)
{
! evalvars_clear();

free_scriptnames();
free_locales();

/* autoloaded script names */
ga_clear_strings(&ga_loaded);

// unreferenced lists and dicts
(void)garbage_collect(FALSE);

***************
*** 417,445 ****
}
#endif

-
- /*
- * Set an internal variable to a string value. Creates the variable if it does
- * not already exist.
- */
- void
- set_internal_string_var(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;
#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval)
static garray_T redir_ga; /* only valid when redir_lval is not NULL */
--- 167,172 ----
***************
*** 944,1014 ****
return retval;
}

- /*
- * List Vim variables.
- */
- void
- list_vim_vars(int *first)
- {
- list_hashtable_vars(&vimvarht, "v:", FALSE, first);
- }
-
- /*
- * List script-local variables, if there is a script.
- */
- void
- list_script_vars(int *first)
- {
- if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len)
- list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid),
- "s:", FALSE, first);
- }
-
- int
- is_vimvarht(hashtab_T *ht)
- {
- return ht == &vimvarht;
- }
-
- int
- is_compatht(hashtab_T *ht)
- {
- return ht == &compat_hashtab;
- }
-
- /*
- * 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.
- */
- void
- prepare_vimvar(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.
- */
- void
- restore_vimvar(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))
- internal_error("restore_vimvar()");
- else
- hash_remove(&vimvarht, hi);
- }
- }
-
#if defined(FEAT_SPELL) || defined(PROTO)
/*
* Evaluate an expression to a list with suggestions.
--- 671,676 ----
***************
*** 1025,1032 ****

/* 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;

--- 687,693 ----

/* Set "v:val" to the bad word. */
prepare_vimvar(VV_VAL, &save_val);
! set_vim_var_string(VV_VAL, badword, -1);
if (p_verbose == 0)
++emsg_off;

***************
*** 1040,1045 ****
--- 701,707 ----

if (p_verbose == 0)
--emsg_off;
+ clear_tv(get_vim_var_tv(VV_VAL));
restore_vimvar(VV_VAL, &save_val);

return list;
***************
*** 1085,1091 ****
return tv;
}

-
/*
* Call some Vim script function and return the result in "*rettv".
* Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc]
--- 747,752 ----
***************
*** 1186,1192 ****
return rettv.vval.v_list;
}

-
#ifdef FEAT_FOLDING
/*
* Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
--- 847,852 ----
***************
*** 2287,2411 ****
#endif

/*
- * 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 *varnamebuf = NULL;
- static int varnamebuflen = 0;
-
- /*
- * Function to concatenate a prefix and a variable name.
- */
- static char_u *
- cat_prefix_varname(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(expand_T *xp, int idx)
- {
- static long_u gdone;
- static long_u bdone;
- static long_u wdone;
- static long_u tdone;
- static int vidx;
- static hashitem_T *hi;
- hashtab_T *ht;
-
- if (idx == 0)
- {
- gdone = bdone = wdone = vidx = 0;
- tdone = 0;
- }
-
- /* 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);
- }
-
- /* 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);
- }
-
- /* 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);
- }
-
- /* v: variables */
- if (vidx < VV_LEN)
- return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
-
- VIM_CLEAR(varnamebuf);
- varnamebuflen = 0;
- return NULL;
- }
-
- /*
* Return TRUE if "pat" matches "text".
* Does not use 'cpo' and always uses 'magic'.
*/
--- 1947,1952 ----
***************
*** 4619,4625 ****
int abort = FALSE;
buf_T *buf;
win_T *wp;
- int i;
int did_free = FALSE;
tabpage_T *tp;

--- 4160,4165 ----
***************
*** 4646,4653 ****
abort = abort || set_ref_in_previous_funccal(copyID);

/* script-local variables */
! for (i = 1; i <= ga_scripts.ga_len; ++i)
! abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);

/* buffer-local variables */
FOR_ALL_BUFFERS(buf)
--- 4186,4192 ----
abort = abort || set_ref_in_previous_funccal(copyID);

/* script-local variables */
! abort = abort || garbage_collect_scriptvars(copyID);

/* buffer-local variables */
FOR_ALL_BUFFERS(buf)
***************
*** 4688,4694 ****
abort = abort || set_ref_in_func_args(copyID);

/* v: vars */
! abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL);

// callbacks in buffers
abort = abort || set_ref_in_buffers(copyID);
--- 4227,4233 ----
abort = abort || set_ref_in_func_args(copyID);

/* v: vars */
! abort = abort || garbage_collect_vimvars(copyID);

// callbacks in buffers
abort = abort || set_ref_in_buffers(copyID);
***************
*** 5475,5482 ****
return OK;
}

-
-
/*
* Translate a String variable into a position.
* Returns NULL when there is an error.
--- 5014,5019 ----
***************
*** 5957,6272 ****
}

/*
- * Set number v: variable to "val".
- */
- void
- set_vim_var_nr(int idx, varnumber_T val)
- {
- vimvars[idx].vv_nr = val;
- }
-
- /*
- * Get typval_T v: variable value.
- */
- typval_T *
- get_vim_var_tv(int idx)
- {
- return &vimvars[idx].vv_tv;
- }
-
- /*
- * Get number v: variable value.
- */
- varnumber_T
- get_vim_var_nr(int idx)
- {
- return vimvars[idx].vv_nr;
- }
-
- /*
- * Get string v: variable value. Uses a static buffer, can only be used once.
- * If the String variable has never been set, return an empty string.
- * Never returns NULL;
- */
- char_u *
- get_vim_var_str(int idx)
- {
- return tv_get_string(&vimvars[idx].vv_tv);
- }
-
- /*
- * Get List v: variable value. Caller must take care of reference count when
- * needed.
- */
- list_T *
- get_vim_var_list(int idx)
- {
- return vimvars[idx].vv_list;
- }
-
- /*
- * Get Dict v: variable value. Caller must take care of reference count when
- * needed.
- */
- dict_T *
- get_vim_var_dict(int idx)
- {
- return vimvars[idx].vv_dict;
- }
-
- /*
- * Set v:char to character "c".
- */
- void
- set_vim_var_char(int c)
- {
- char_u buf[MB_MAXBYTES + 1];
-
- if (has_mbyte)
- buf[(*mb_char2bytes)(c, buf)] = NUL;
- else
- {
- buf[0] = c;
- buf[1] = NUL;
- }
- set_vim_var_string(VV_CHAR, buf, -1);
- }
-
- /*
- * Set v:count to "count" and v:count1 to "count1".
- * When "set_prevcount" is TRUE first set v:prevcount from v:count.
- */
- void
- set_vcount(
- long count,
- long count1,
- int set_prevcount)
- {
- if (set_prevcount)
- vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr;
- vimvars[VV_COUNT].vv_nr = count;
- vimvars[VV_COUNT1].vv_nr = count1;
- }
-
- /*
- * Save variables that might be changed as a side effect. Used when executing
- * a timer callback.
- */
- void
- save_vimvars(vimvars_save_T *vvsave)
- {
- vvsave->vv_prevcount = vimvars[VV_PREVCOUNT].vv_nr;
- vvsave->vv_count = vimvars[VV_COUNT].vv_nr;
- vvsave->vv_count1 = vimvars[VV_COUNT1].vv_nr;
- }
-
- /*
- * Restore variables saved by save_vimvars().
- */
- void
- restore_vimvars(vimvars_save_T *vvsave)
- {
- vimvars[VV_PREVCOUNT].vv_nr = vvsave->vv_prevcount;
- vimvars[VV_COUNT].vv_nr = vvsave->vv_count;
- vimvars[VV_COUNT1].vv_nr = vvsave->vv_count1;
- }
-
- /*
- * Set string v: variable to a copy of "val".
- */
- void
- set_vim_var_string(
- int idx,
- char_u *val,
- int len) /* length of "val" to use or -1 (whole string) */
- {
- clear_tv(&vimvars[idx].vv_di.di_tv);
- vimvars[idx].vv_type = VAR_STRING;
- if (val == NULL)
- vimvars[idx].vv_str = NULL;
- else if (len == -1)
- vimvars[idx].vv_str = vim_strsave(val);
- else
- vimvars[idx].vv_str = vim_strnsave(val, len);
- }
-
- /*
- * Set List v: variable to "val".
- */
- void
- set_vim_var_list(int idx, list_T *val)
- {
- clear_tv(&vimvars[idx].vv_di.di_tv);
- vimvars[idx].vv_type = VAR_LIST;
- vimvars[idx].vv_list = val;
- if (val != NULL)
- ++val->lv_refcount;
- }
-
- /*
- * Set Dictionary v: variable to "val".
- */
- void
- set_vim_var_dict(int idx, dict_T *val)
- {
- clear_tv(&vimvars[idx].vv_di.di_tv);
- vimvars[idx].vv_type = VAR_DICT;
- vimvars[idx].vv_dict = val;
- if (val != NULL)
- {
- ++val->dv_refcount;
- dict_set_items_ro(val);
- }
- }
-
- /*
- * Set v:register if needed.
- */
- void
- set_reg_var(int c)
- {
- char_u regname;
-
- if (c == 0 || c == ' ')
- regname = '"';
- else
- regname = c;
- /* Avoid free/alloc when the value is already right. */
- if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c)
- set_vim_var_string(VV_REG, &regname, 1);
- }
-
- /*
- * Get or set v:exception. If "oldval" == NULL, return the current value.
- * Otherwise, restore the value to "oldval" and return NULL.
- * Must always be called in pairs to save and restore v:exception! Does not
- * take care of memory allocations.
- */
- char_u *
- v_exception(char_u *oldval)
- {
- if (oldval == NULL)
- return vimvars[VV_EXCEPTION].vv_str;
-
- vimvars[VV_EXCEPTION].vv_str = oldval;
- return NULL;
- }
-
- /*
- * Get or set v:throwpoint. If "oldval" == NULL, return the current value.
- * Otherwise, restore the value to "oldval" and return NULL.
- * Must always be called in pairs to save and restore v:throwpoint! Does not
- * take care of memory allocations.
- */
- char_u *
- v_throwpoint(char_u *oldval)
- {
- if (oldval == NULL)
- return vimvars[VV_THROWPOINT].vv_str;
-
- vimvars[VV_THROWPOINT].vv_str = oldval;
- return NULL;
- }
-
- /*
- * Set v:cmdarg.
- * If "eap" != NULL, use "eap" to generate the value and return the old value.
- * If "oldarg" != NULL, restore the value to "oldarg" and return NULL.
- * Must always be called in pairs!
- */
- char_u *
- set_cmdarg(exarg_T *eap, char_u *oldarg)
- {
- char_u *oldval;
- char_u *newval;
- unsigned len;
-
- oldval = vimvars[VV_CMDARG].vv_str;
- if (eap == NULL)
- {
- vim_free(oldval);
- vimvars[VV_CMDARG].vv_str = oldarg;
- return NULL;
- }
-
- if (eap->force_bin == FORCE_BIN)
- len = 6;
- else if (eap->force_bin == FORCE_NOBIN)
- len = 8;
- else
- len = 0;
-
- if (eap->read_edit)
- len += 7;
-
- if (eap->force_ff != 0)
- len += 10; /* " ++ff=unix" */
- if (eap->force_enc != 0)
- len += (unsigned)STRLEN(eap->cmd + eap->force_enc) + 7;
- if (eap->bad_char != 0)
- len += 7 + 4; /* " ++bad=" + "keep" or "drop" */
-
- newval = alloc(len + 1);
- if (newval == NULL)
- return NULL;
-
- if (eap->force_bin == FORCE_BIN)
- sprintf((char *)newval, " ++bin");
- else if (eap->force_bin == FORCE_NOBIN)
- sprintf((char *)newval, " ++nobin");
- else
- *newval = NUL;
-
- if (eap->read_edit)
- STRCAT(newval, " ++edit");
-
- if (eap->force_ff != 0)
- sprintf((char *)newval + STRLEN(newval), " ++ff=%s",
- eap->force_ff == 'u' ? "unix"
- : eap->force_ff == 'd' ? "dos"
- : "mac");
- if (eap->force_enc != 0)
- sprintf((char *)newval + STRLEN(newval), " ++enc=%s",
- eap->cmd + eap->force_enc);
- if (eap->bad_char == BAD_KEEP)
- STRCPY(newval + STRLEN(newval), " ++bad=keep");
- else if (eap->bad_char == BAD_DROP)
- STRCPY(newval + STRLEN(newval), " ++bad=drop");
- else if (eap->bad_char != 0)
- sprintf((char *)newval + STRLEN(newval), " ++bad=%c", eap->bad_char);
- vimvars[VV_CMDARG].vv_str = newval;
- return oldval;
- }
-
- /*
- * Check if variable "name[len]" is a local variable or an argument.
- * If so, "*eval_lavars_used" is set to TRUE.
- */
- static void
- check_vars(char_u *name, int len)
- {
- int cc;
- char_u *varname;
- hashtab_T *ht;
-
- if (eval_lavars_used == NULL)
- return;
-
- /* truncate the name, so that we can use strcmp() */
- cc = name[len];
- name[len] = NUL;
-
- ht = find_var_ht(name, &varname);
- if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht())
- {
- if (find_var(name, NULL, TRUE) != NULL)
- *eval_lavars_used = TRUE;
- }
-
- name[len] = cc;
- }
-
- /*
* Handle:
* - expr[expr], expr[expr:expr] subscript
* - ".name" lookup
--- 5494,5499 ----
***************
*** 6380,6386 ****
* The string "s" must have been allocated, it is consumed.
* Return NULL for out of memory, the variable otherwise.
*/
! static typval_T *
alloc_string_tv(char_u *s)
{
typval_T *rettv;
--- 5607,5613 ----
* The string "s" must have been allocated, it is consumed.
* Return NULL for out of memory, the variable otherwise.
*/
! typval_T *
alloc_string_tv(char_u *s)
{
typval_T *rettv;
***************
*** 6777,6985 ****
}

/*
- * Find variable "name" in the list of variables.
- * Return a pointer to it if found, NULL if not found.
- * Careful: "a:0" variables don't have a name.
- * When "htp" is not NULL we are writing to the variable, set "htp" to the
- * hashtab_T used.
- */
- dictitem_T *
- find_var(char_u *name, hashtab_T **htp, int no_autoload)
- {
- char_u *varname;
- hashtab_T *ht;
- dictitem_T *ret = NULL;
-
- ht = find_var_ht(name, &varname);
- if (htp != NULL)
- *htp = ht;
- if (ht == NULL)
- return NULL;
- ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
- if (ret != NULL)
- return ret;
-
- /* Search in parent scope for lambda */
- return find_var_in_scoped_ht(name, no_autoload || htp != NULL);
- }
-
- /*
- * Find variable "varname" in hashtab "ht" with name "htname".
- * Returns NULL if not found.
- */
- dictitem_T *
- find_var_in_ht(
- hashtab_T *ht,
- int htname,
- char_u *varname,
- int no_autoload)
- {
- hashitem_T *hi;
-
- if (*varname == NUL)
- {
- /* Must be something like "s:", otherwise "ht" would be NULL. */
- switch (htname)
- {
- case 's': return &SCRIPT_SV(current_sctx.sc_sid)->sv_var;
- case 'g': return &globvars_var;
- case 'v': return &vimvars_var;
- case 'b': return &curbuf->b_bufvar;
- case 'w': return &curwin->w_winvar;
- case 't': return &curtab->tp_winvar;
- case 'l': return get_funccal_local_var();
- case 'a': return get_funccal_args_var();
- }
- return NULL;
- }
-
- hi = hash_find(ht, varname);
- if (HASHITEM_EMPTY(hi))
- {
- /* For global variables we may try auto-loading the script. If it
- * worked find the variable again. Don't auto-load a script if it was
- * loaded already, otherwise it would be loaded every time when
- * checking if a function name is a Funcref variable. */
- if (ht == &globvarht && !no_autoload)
- {
- /* Note: script_autoload() may make "hi" invalid. It must either
- * be obtained again or not used. */
- if (!script_autoload(varname, FALSE) || aborting())
- return NULL;
- hi = hash_find(ht, varname);
- }
- if (HASHITEM_EMPTY(hi))
- return NULL;
- }
- return HI2DI(hi);
- }
-
- /*
- * Find the hashtab used for a variable name.
- * Return NULL if the name is not valid.
- * Set "varname" to the start of name without ':'.
- */
- hashtab_T *
- find_var_ht(char_u *name, char_u **varname)
- {
- hashitem_T *hi;
- hashtab_T *ht;
-
- if (name[0] == NUL)
- return NULL;
- if (name[1] != ':')
- {
- /* The name must not start with a colon or #. */
- if (name[0] == ':' || name[0] == AUTOLOAD_CHAR)
- return NULL;
- *varname = name;
-
- // "version" is "v:version" in all scopes if scriptversion < 3.
- // Same for a few other variables marked with VV_COMPAT.
- if (current_sctx.sc_version < 3)
- {
- hi = hash_find(&compat_hashtab, name);
- if (!HASHITEM_EMPTY(hi))
- return &compat_hashtab;
- }
-
- ht = get_funccal_local_ht();
- if (ht == NULL)
- return &globvarht; /* global variable */
- return ht; /* local variable */
- }
- *varname = name + 2;
- if (*name == 'g') /* global variable */
- return &globvarht;
- // There must be no ':' or '#' in the rest of the name, unless g: is used
- if (vim_strchr(name + 2, ':') != NULL
- || vim_strchr(name + 2, AUTOLOAD_CHAR) != NULL)
- return NULL;
- if (*name == 'b') /* buffer variable */
- return &curbuf->b_vars->dv_hashtab;
- if (*name == 'w') /* window variable */
- return &curwin->w_vars->dv_hashtab;
- if (*name == 't') /* tab page variable */
- return &curtab->tp_vars->dv_hashtab;
- if (*name == 'v') /* v: variable */
- return &vimvarht;
- if (*name == 'a') /* a: function argument */
- return get_funccal_args_ht();
- if (*name == 'l') /* l: local function variable */
- return get_funccal_local_ht();
- if (*name == 's' /* script variable */
- && current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len)
- return &SCRIPT_VARS(current_sctx.sc_sid);
- return NULL;
- }
-
- /*
- * Allocate a new hashtab for a sourced script. It will be used while
- * sourcing this script and when executing functions defined in the script.
- */
- void
- new_script_vars(scid_T id)
- {
- int i;
- hashtab_T *ht;
- scriptvar_T *sv;
-
- if (ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)) == OK)
- {
- /* Re-allocating ga_data means that an ht_array pointing to
- * ht_smallarray becomes invalid. We can recognize this: ht_mask is
- * at its init value. Also reset "v_dict", it's always the same. */
- for (i = 1; i <= ga_scripts.ga_len; ++i)
- {
- ht = &SCRIPT_VARS(i);
- if (ht->ht_mask == HT_INIT_SIZE - 1)
- ht->ht_array = ht->ht_smallarray;
- sv = SCRIPT_SV(i);
- sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
- }
-
- while (ga_scripts.ga_len < id)
- {
- sv = SCRIPT_SV(ga_scripts.ga_len + 1) =
- ALLOC_CLEAR_ONE(scriptvar_T);
- init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
- ++ga_scripts.ga_len;
- }
- }
- }
-
- /*
- * Initialize dictionary "dict" as a scope and set variable "dict_var" to
- * point to it.
- */
- void
- init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
- {
- hash_init(&dict->dv_hashtab);
- dict->dv_lock = 0;
- dict->dv_scope = scope;
- dict->dv_refcount = DO_NOT_FREE_CNT;
- dict->dv_copyID = 0;
- dict_var->di_tv.vval.v_dict = dict;
- dict_var->di_tv.v_type = VAR_DICT;
- dict_var->di_tv.v_lock = VAR_FIXED;
- dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- dict_var->di_key[0] = NUL;
- }
-
- /*
- * Unreference a dictionary initialized by init_var_dict().
- */
- void
- unref_var_dict(dict_T *dict)
- {
- /* Now the dict needs to be freed if no one else is using it, go back to
- * normal reference counting. */
- dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
- dict_unref(dict);
- }
-
- /*
* Return TRUE if typeval "tv" and its value are set to be locked (immutable).
* Also give an error message, using "name" or _("name") when use_gettext is
* TRUE.
--- 6004,6009 ----
***************
*** 7730,7763 ****
}

/*
- * reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal,
- * v:option_type, and v:option_command.
- */
- void
- reset_v_option_vars(void)
- {
- set_vim_var_string(VV_OPTION_NEW, NULL, -1);
- set_vim_var_string(VV_OPTION_OLD, NULL, -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1);
- set_vim_var_string(VV_OPTION_TYPE, NULL, -1);
- set_vim_var_string(VV_OPTION_COMMAND, NULL, -1);
- }
-
- /*
- * Add an assert error to v:errors.
- */
- void
- assert_error(garray_T *gap)
- {
- struct vimvar *vp = &vimvars[VV_ERRORS];
-
- if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL)
- /* Make sure v:errors is a list. */
- set_vim_var_list(VV_ERRORS, list_alloc());
- list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len);
- }
- /*
* Compare "typ1" and "typ2". Put the result in "typ1".
*/
int
--- 6754,6759 ----
***************
*** 8000,8006 ****

#endif /* FEAT_EVAL */

-
#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO)

#ifdef MSWIN
--- 6996,7001 ----
***************
*** 8754,8762 ****
typval_T argv[3];
int retval = FAIL;

! copy_tv(tv, &vimvars[VV_VAL].vv_tv);
! argv[0] = vimvars[VV_KEY].vv_tv;
! argv[1] = vimvars[VV_VAL].vv_tv;
if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
goto theend;
if (map)
--- 7749,7757 ----
typval_T argv[3];
int retval = FAIL;

! copy_tv(tv, get_vim_var_tv(VV_VAL));
! argv[0] = *get_vim_var_tv(VV_KEY);
! argv[1] = *get_vim_var_tv(VV_VAL);
if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
goto theend;
if (map)
***************
*** 8780,8790 ****
}
retval = OK;
theend:
! clear_tv(&vimvars[VV_VAL].vv_tv);
return retval;
}

-
/*
* Implementation of map() and filter().
*/
--- 7775,7784 ----
}
retval = OK;
theend:
! clear_tv(get_vim_var_tv(VV_VAL));
return retval;
}

/*
* Implementation of map() and filter().
*/
***************
*** 8848,8855 ****
prepare_vimvar(VV_KEY, &save_key);
if (argvars[0].v_type == VAR_DICT)
{
- vimvars[VV_KEY].vv_type = VAR_STRING;
-
ht = &d->dv_hashtab;
hash_lock(ht);
todo = (int)ht->ht_used;
--- 7842,7847 ----
***************
*** 8866,8874 ****
|| var_check_ro(di->di_flags,
arg_errmsg, TRUE)))
break;
! vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
r = filter_map_one(&di->di_tv, expr, map, &rem);
! clear_tv(&vimvars[VV_KEY].vv_tv);
if (r == FAIL || did_emsg)
break;
if (!map && rem)
--- 7858,7866 ----
|| var_check_ro(di->di_flags,
arg_errmsg, TRUE)))
break;
! set_vim_var_string(VV_KEY, di->di_key, -1);
r = filter_map_one(&di->di_tv, expr, map, &rem);
! clear_tv(get_vim_var_tv(VV_KEY));
if (r == FAIL || did_emsg)
break;
if (!map && rem)
***************
*** 8887,8898 ****
int i;
typval_T tv;

- vimvars[VV_KEY].vv_type = VAR_NUMBER;
for (i = 0; i < b->bv_ga.ga_len; i++)
{
tv.v_type = VAR_NUMBER;
tv.vval.v_number = blob_get(b, i);
! vimvars[VV_KEY].vv_nr = idx;
if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg)
break;
if (tv.v_type != VAR_NUMBER)
--- 7879,7889 ----
int i;
typval_T tv;

for (i = 0; i < b->bv_ga.ga_len; i++)
{
tv.v_type = VAR_NUMBER;
tv.vval.v_number = blob_get(b, i);
! set_vim_var_nr(VV_KEY, idx);
if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg)
break;
if (tv.v_type != VAR_NUMBER)
***************
*** 8916,8929 ****
else
{
// argvars[0].v_type == VAR_LIST
- vimvars[VV_KEY].vv_type = VAR_NUMBER;
-
for (li = l->lv_first; li != NULL; li = nli)
{
if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
break;
nli = li->li_next;
! vimvars[VV_KEY].vv_nr = idx;
if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
|| did_emsg)
break;
--- 7907,7918 ----
else
{
// argvars[0].v_type == VAR_LIST
for (li = l->lv_first; li != NULL; li = nli)
{
if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
break;
nli = li->li_next;
! set_vim_var_nr(VV_KEY, idx);
if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
|| did_emsg)
break;
*** ../vim-8.1.1938/src/evalvars.c 2019-08-27 22:48:12.741480663 +0200
--- src/evalvars.c 2019-08-29 22:09:20.628579500 +0200
***************
*** 17,22 ****
--- 17,180 ----

static char *e_letunexp = N_("E18: Unexpected characters in :let");

+ static dictitem_T globvars_var; // variable used for g:
+
+ /*
+ * 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;
+
+ /*
+ * 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.
+ */
+
+ // 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}}
+
+ static struct vimvar
+ {
+ char *vv_name; // name of variable, without v:
+ dictitem16_T vv_di; // value and name for key (max 16 chars!)
+ 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_winid", VAR_NUMBER), VV_RO},
+ {VV_NAME("beval_lnum", VAR_NUMBER), VV_RO},
+ {VV_NAME("beval_col", VAR_NUMBER), VV_RO},
+ {VV_NAME("beval_text", VAR_STRING), VV_RO},
+ {VV_NAME("scrollstart", VAR_STRING), 0},
+ {VV_NAME("swapname", VAR_STRING), VV_RO},
+ {VV_NAME("swapchoice", VAR_STRING), 0},
+ {VV_NAME("swapcommand", VAR_STRING), VV_RO},
+ {VV_NAME("char", VAR_STRING), 0},
+ {VV_NAME("mouse_win", VAR_NUMBER), 0},
+ {VV_NAME("mouse_winid", 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("hlsearch", VAR_NUMBER), 0},
+ {VV_NAME("oldfiles", VAR_LIST), 0},
+ {VV_NAME("windowid", VAR_NUMBER), VV_RO},
+ {VV_NAME("progpath", VAR_STRING), VV_RO},
+ {VV_NAME("completed_item", VAR_DICT), VV_RO},
+ {VV_NAME("option_new", VAR_STRING), VV_RO},
+ {VV_NAME("option_old", VAR_STRING), VV_RO},
+ {VV_NAME("option_oldlocal", VAR_STRING), VV_RO},
+ {VV_NAME("option_oldglobal", VAR_STRING), VV_RO},
+ {VV_NAME("option_command", VAR_STRING), VV_RO},
+ {VV_NAME("option_type", VAR_STRING), VV_RO},
+ {VV_NAME("errors", VAR_LIST), 0},
+ {VV_NAME("false", VAR_SPECIAL), VV_RO},
+ {VV_NAME("true", VAR_SPECIAL), VV_RO},
+ {VV_NAME("null", VAR_SPECIAL), VV_RO},
+ {VV_NAME("none", VAR_SPECIAL), VV_RO},
+ {VV_NAME("vim_did_enter", VAR_NUMBER), VV_RO},
+ {VV_NAME("testing", VAR_NUMBER), 0},
+ {VV_NAME("t_number", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_string", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_func", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_list", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_dict", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_float", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_bool", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_none", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_job", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_channel", VAR_NUMBER), VV_RO},
+ {VV_NAME("t_blob", VAR_NUMBER), VV_RO},
+ {VV_NAME("termrfgresp", VAR_STRING), VV_RO},
+ {VV_NAME("termrbgresp", VAR_STRING), VV_RO},
+ {VV_NAME("termu7resp", VAR_STRING), VV_RO},
+ {VV_NAME("termstyleresp", VAR_STRING), VV_RO},
+ {VV_NAME("termblinkresp", VAR_STRING), VV_RO},
+ {VV_NAME("event", VAR_DICT), VV_RO},
+ {VV_NAME("versionlong", VAR_NUMBER), VV_RO},
+ {VV_NAME("echospace", VAR_NUMBER), VV_RO},
+ };
+
+ // shorthand
+ #define vv_type vv_di.di_tv.v_type
+ #define vv_nr vv_di.di_tv.vval.v_number
+ #define vv_float vv_di.di_tv.vval.v_float
+ #define vv_str vv_di.di_tv.vval.v_string
+ #define vv_list vv_di.di_tv.vval.v_list
+ #define vv_dict vv_di.di_tv.vval.v_dict
+ #define vv_blob vv_di.di_tv.vval.v_blob
+ #define vv_tv vv_di.di_tv
+
+ static dictitem_T vimvars_var; // variable used for v:
+ #define vimvarht vimvardict.dv_hashtab
+
+ // for VIM_VERSION_ defines
+ #include "version.h"
+
+ /*
+ * 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 void ex_let_const(exarg_T *eap, int is_const);
static char_u *skip_var_one(char_u *arg);
static void list_glob_vars(int *first);
***************
*** 33,38 ****
--- 191,396 ----
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);

/*
+ * Initialize global and vim special variables
+ */
+ void
+ evalvars_init(void)
+ {
+ int i;
+ struct vimvar *p;
+
+ init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
+ init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
+ vimvardict.dv_lock = VAR_FIXED;
+ hash_init(&compat_hashtab);
+
+ for (i = 0; i < VV_LEN; ++i)
+ {
+ p = &vimvars[i];
+ if (STRLEN(p->vv_name) > DICTITEM16_KEY_LEN)
+ {
+ iemsg("INTERNAL: name too long, increase size of dictitem16_T");
+ getout(1);
+ }
+ 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);
+ }
+ vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+ vimvars[VV_VERSIONLONG].vv_nr = VIM_VERSION_100 * 10000 + highest_patch();
+
+ set_vim_var_nr(VV_SEARCHFORWARD, 1L);
+ set_vim_var_nr(VV_HLSEARCH, 1L);
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
+ set_vim_var_list(VV_ERRORS, list_alloc());
+ set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));
+
+ set_vim_var_nr(VV_FALSE, VVAL_FALSE);
+ set_vim_var_nr(VV_TRUE, VVAL_TRUE);
+ set_vim_var_nr(VV_NONE, VVAL_NONE);
+ set_vim_var_nr(VV_NULL, VVAL_NULL);
+
+ set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
+ set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
+ set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
+ set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST);
+ set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
+ set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
+ set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
+ set_vim_var_nr(VV_TYPE_NONE, VAR_TYPE_NONE);
+ set_vim_var_nr(VV_TYPE_JOB, VAR_TYPE_JOB);
+ set_vim_var_nr(VV_TYPE_CHANNEL, VAR_TYPE_CHANNEL);
+ set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
+
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
+
+ set_reg_var(0); // default for v:register is not 0 but '"'
+ }
+
+ #if defined(EXITFREE) || defined(PROTO)
+ /*
+ * Free all vim variables information on exit
+ */
+ void
+ evalvars_clear(void)
+ {
+ 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_CLEAR(p->vv_str);
+ 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);
+
+ // global variables
+ vars_clear(&globvarht);
+
+ // Script-local variables. First clear all the variables and in a second
+ // loop free the scriptvar_T, because a variable in one script might hold
+ // a reference to the whole scope of another script.
+ for (i = 1; i <= ga_scripts.ga_len; ++i)
+ vars_clear(&SCRIPT_VARS(i));
+ for (i = 1; i <= ga_scripts.ga_len; ++i)
+ vim_free(SCRIPT_SV(i));
+ ga_clear(&ga_scripts);
+ }
+ #endif
+
+ int
+ garbage_collect_vimvars(int copyID)
+ {
+ return set_ref_in_ht(&vimvarht, copyID, NULL);
+ }
+
+ int
+ garbage_collect_scriptvars(int copyID)
+ {
+ int i;
+ int abort = FALSE;
+
+ for (i = 1; i <= ga_scripts.ga_len; ++i)
+ abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);
+
+ return abort;
+ }
+
+ /*
+ * Set an internal variable to a string value. Creates the variable if it does
+ * not already exist.
+ */
+ void
+ set_internal_string_var(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);
+ }
+ }
+ }
+
+ /*
+ * 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.
+ */
+ void
+ prepare_vimvar(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.
+ */
+ void
+ restore_vimvar(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))
+ internal_error("restore_vimvar()");
+ else
+ hash_remove(&vimvarht, hi);
+ }
+ }
+
+ /*
+ * List Vim variables.
+ */
+ static void
+ list_vim_vars(int *first)
+ {
+ list_hashtable_vars(&vimvarht, "v:", FALSE, first);
+ }
+
+ /*
+ * List script-local variables, if there is a script.
+ */
+ static void
+ list_script_vars(int *first)
+ {
+ if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len)
+ list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid),
+ "s:", FALSE, first);
+ }
+
+ /*
* Get a list of lines from a HERE document. The here document is a list of
* lines surrounded by a marker.
* cmd << {marker}
***************
*** 1024,1030 ****
{
if (ht == &globvarht)
d = &globvardict;
! else if (is_compatht(ht))
d = &vimvardict;
else
{
--- 1382,1388 ----
{
if (ht == &globvarht)
d = &globvardict;
! else if (ht == &compat_hashtab)
d = &vimvardict;
else
{
***************
*** 1214,1219 ****
--- 1572,1980 ----
}

/*
+ * 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 *varnamebuf = NULL;
+ static int varnamebuflen = 0;
+
+ /*
+ * Function to concatenate a prefix and a variable name.
+ */
+ static char_u *
+ cat_prefix_varname(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(expand_T *xp, int idx)
+ {
+ static long_u gdone;
+ static long_u bdone;
+ static long_u wdone;
+ static long_u tdone;
+ static int vidx;
+ static hashitem_T *hi;
+ hashtab_T *ht;
+
+ if (idx == 0)
+ {
+ gdone = bdone = wdone = vidx = 0;
+ tdone = 0;
+ }
+
+ // 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);
+ }
+
+ // 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);
+ }
+
+ // 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);
+ }
+
+ // v: variables
+ if (vidx < VV_LEN)
+ return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
+
+ VIM_CLEAR(varnamebuf);
+ varnamebuflen = 0;
+ return NULL;
+ }
+
+ /*
+ * Set number v: variable to "val".
+ */
+ void
+ set_vim_var_nr(int idx, varnumber_T val)
+ {
+ vimvars[idx].vv_type = VAR_NUMBER;
+ vimvars[idx].vv_nr = val;
+ }
+
+ /*
+ * Get typval_T v: variable value.
+ */
+ typval_T *
+ get_vim_var_tv(int idx)
+ {
+ return &vimvars[idx].vv_tv;
+ }
+
+ /*
+ * Get number v: variable value.
+ */
+ varnumber_T
+ get_vim_var_nr(int idx)
+ {
+ return vimvars[idx].vv_nr;
+ }
+
+ /*
+ * Get string v: variable value. Uses a static buffer, can only be used once.
+ * If the String variable has never been set, return an empty string.
+ * Never returns NULL;
+ */
+ char_u *
+ get_vim_var_str(int idx)
+ {
+ return tv_get_string(&vimvars[idx].vv_tv);
+ }
+
+ /*
+ * Get List v: variable value. Caller must take care of reference count when
+ * needed.
+ */
+ list_T *
+ get_vim_var_list(int idx)
+ {
+ return vimvars[idx].vv_list;
+ }
+
+ /*
+ * Get Dict v: variable value. Caller must take care of reference count when
+ * needed.
+ */
+ dict_T *
+ get_vim_var_dict(int idx)
+ {
+ return vimvars[idx].vv_dict;
+ }
+
+ /*
+ * Set v:char to character "c".
+ */
+ void
+ set_vim_var_char(int c)
+ {
+ char_u buf[MB_MAXBYTES + 1];
+
+ if (has_mbyte)
+ buf[(*mb_char2bytes)(c, buf)] = NUL;
+ else
+ {
+ buf[0] = c;
+ buf[1] = NUL;
+ }
+ set_vim_var_string(VV_CHAR, buf, -1);
+ }
+
+ /*
+ * Set v:count to "count" and v:count1 to "count1".
+ * When "set_prevcount" is TRUE first set v:prevcount from v:count.
+ */
+ void
+ set_vcount(
+ long count,
+ long count1,
+ int set_prevcount)
+ {
+ if (set_prevcount)
+ vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr;
+ vimvars[VV_COUNT].vv_nr = count;
+ vimvars[VV_COUNT1].vv_nr = count1;
+ }
+
+ /*
+ * Save variables that might be changed as a side effect. Used when executing
+ * a timer callback.
+ */
+ void
+ save_vimvars(vimvars_save_T *vvsave)
+ {
+ vvsave->vv_prevcount = vimvars[VV_PREVCOUNT].vv_nr;
+ vvsave->vv_count = vimvars[VV_COUNT].vv_nr;
+ vvsave->vv_count1 = vimvars[VV_COUNT1].vv_nr;
+ }
+
+ /*
+ * Restore variables saved by save_vimvars().
+ */
+ void
+ restore_vimvars(vimvars_save_T *vvsave)
+ {
+ vimvars[VV_PREVCOUNT].vv_nr = vvsave->vv_prevcount;
+ vimvars[VV_COUNT].vv_nr = vvsave->vv_count;
+ vimvars[VV_COUNT1].vv_nr = vvsave->vv_count1;
+ }
+
+ /*
+ * Set string v: variable to a copy of "val". If 'copy' is FALSE, then set the
+ * value.
+ */
+ void
+ set_vim_var_string(
+ int idx,
+ char_u *val,
+ int len) // length of "val" to use or -1 (whole string)
+ {
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_STRING;
+ if (val == NULL)
+ vimvars[idx].vv_str = NULL;
+ else if (len == -1)
+ vimvars[idx].vv_str = vim_strsave(val);
+ else
+ vimvars[idx].vv_str = vim_strnsave(val, len);
+ }
+
+ /*
+ * Set List v: variable to "val".
+ */
+ void
+ set_vim_var_list(int idx, list_T *val)
+ {
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_LIST;
+ vimvars[idx].vv_list = val;
+ if (val != NULL)
+ ++val->lv_refcount;
+ }
+
+ /*
+ * Set Dictionary v: variable to "val".
+ */
+ void
+ set_vim_var_dict(int idx, dict_T *val)
+ {
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_DICT;
+ vimvars[idx].vv_dict = val;
+ if (val != NULL)
+ {
+ ++val->dv_refcount;
+ dict_set_items_ro(val);
+ }
+ }
+
+ /*
+ * Set v:register if needed.
+ */
+ void
+ set_reg_var(int c)
+ {
+ char_u regname;
+
+ if (c == 0 || c == ' ')
+ regname = '"';
+ else
+ regname = c;
+ // Avoid free/alloc when the value is already right.
+ if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c)
+ set_vim_var_string(VV_REG, &regname, 1);
+ }
+
+ /*
+ * Get or set v:exception. If "oldval" == NULL, return the current value.
+ * Otherwise, restore the value to "oldval" and return NULL.
+ * Must always be called in pairs to save and restore v:exception! Does not
+ * take care of memory allocations.
+ */
+ char_u *
+ v_exception(char_u *oldval)
+ {
+ if (oldval == NULL)
+ return vimvars[VV_EXCEPTION].vv_str;
+
+ vimvars[VV_EXCEPTION].vv_str = oldval;
+ return NULL;
+ }
+
+ /*
+ * Get or set v:throwpoint. If "oldval" == NULL, return the current value.
+ * Otherwise, restore the value to "oldval" and return NULL.
+ * Must always be called in pairs to save and restore v:throwpoint! Does not
+ * take care of memory allocations.
+ */
+ char_u *
+ v_throwpoint(char_u *oldval)
+ {
+ if (oldval == NULL)
+ return vimvars[VV_THROWPOINT].vv_str;
+
+ vimvars[VV_THROWPOINT].vv_str = oldval;
+ return NULL;
+ }
+
+ /*
+ * Set v:cmdarg.
+ * If "eap" != NULL, use "eap" to generate the value and return the old value.
+ * If "oldarg" != NULL, restore the value to "oldarg" and return NULL.
+ * Must always be called in pairs!
+ */
+ char_u *
+ set_cmdarg(exarg_T *eap, char_u *oldarg)
+ {
+ char_u *oldval;
+ char_u *newval;
+ unsigned len;
+
+ oldval = vimvars[VV_CMDARG].vv_str;
+ if (eap == NULL)
+ {
+ vim_free(oldval);
+ vimvars[VV_CMDARG].vv_str = oldarg;
+ return NULL;
+ }
+
+ if (eap->force_bin == FORCE_BIN)
+ len = 6;
+ else if (eap->force_bin == FORCE_NOBIN)
+ len = 8;
+ else
+ len = 0;
+
+ if (eap->read_edit)
+ len += 7;
+
+ if (eap->force_ff != 0)
+ len += 10; // " ++ff=unix"
+ if (eap->force_enc != 0)
+ len += (unsigned)STRLEN(eap->cmd + eap->force_enc) + 7;
+ if (eap->bad_char != 0)
+ len += 7 + 4; // " ++bad=" + "keep" or "drop"
+
+ newval = alloc(len + 1);
+ if (newval == NULL)
+ return NULL;
+
+ if (eap->force_bin == FORCE_BIN)
+ sprintf((char *)newval, " ++bin");
+ else if (eap->force_bin == FORCE_NOBIN)
+ sprintf((char *)newval, " ++nobin");
+ else
+ *newval = NUL;
+
+ if (eap->read_edit)
+ STRCAT(newval, " ++edit");
+
+ if (eap->force_ff != 0)
+ sprintf((char *)newval + STRLEN(newval), " ++ff=%s",
+ eap->force_ff == 'u' ? "unix"
+ : eap->force_ff == 'd' ? "dos"
+ : "mac");
+ if (eap->force_enc != 0)
+ sprintf((char *)newval + STRLEN(newval), " ++enc=%s",
+ eap->cmd + eap->force_enc);
+ if (eap->bad_char == BAD_KEEP)
+ STRCPY(newval + STRLEN(newval), " ++bad=keep");
+ else if (eap->bad_char == BAD_DROP)
+ STRCPY(newval + STRLEN(newval), " ++bad=drop");
+ else if (eap->bad_char != 0)
+ sprintf((char *)newval + STRLEN(newval), " ++bad=%c", eap->bad_char);
+ vimvars[VV_CMDARG].vv_str = newval;
+ return oldval;
+ }
+
+ /*
* Get the value of internal variable "name".
* Return OK or FAIL. If OK is returned "rettv" must be cleared.
*/
***************
*** 1259,1264 ****
--- 2020,2191 ----
}

/*
+ * Check if variable "name[len]" is a local variable or an argument.
+ * If so, "*eval_lavars_used" is set to TRUE.
+ */
+ void
+ check_vars(char_u *name, int len)
+ {
+ int cc;
+ char_u *varname;
+ hashtab_T *ht;
+
+ if (eval_lavars_used == NULL)
+ return;
+
+ // truncate the name, so that we can use strcmp()
+ cc = name[len];
+ name[len] = NUL;
+
+ ht = find_var_ht(name, &varname);
+ if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht())
+ {
+ if (find_var(name, NULL, TRUE) != NULL)
+ *eval_lavars_used = TRUE;
+ }
+
+ name[len] = cc;
+ }
+
+ /*
+ * Find variable "name" in the list of variables.
+ * Return a pointer to it if found, NULL if not found.
+ * Careful: "a:0" variables don't have a name.
+ * When "htp" is not NULL we are writing to the variable, set "htp" to the
+ * hashtab_T used.
+ */
+ dictitem_T *
+ find_var(char_u *name, hashtab_T **htp, int no_autoload)
+ {
+ char_u *varname;
+ hashtab_T *ht;
+ dictitem_T *ret = NULL;
+
+ ht = find_var_ht(name, &varname);
+ if (htp != NULL)
+ *htp = ht;
+ if (ht == NULL)
+ return NULL;
+ ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
+ if (ret != NULL)
+ return ret;
+
+ /* Search in parent scope for lambda */
+ return find_var_in_scoped_ht(name, no_autoload || htp != NULL);
+ }
+
+ /*
+ * Find variable "varname" in hashtab "ht" with name "htname".
+ * Returns NULL if not found.
+ */
+ dictitem_T *
+ find_var_in_ht(
+ hashtab_T *ht,
+ int htname,
+ char_u *varname,
+ int no_autoload)
+ {
+ hashitem_T *hi;
+
+ if (*varname == NUL)
+ {
+ // Must be something like "s:", otherwise "ht" would be NULL.
+ switch (htname)
+ {
+ case 's': return &SCRIPT_SV(current_sctx.sc_sid)->sv_var;
+ case 'g': return &globvars_var;
+ case 'v': return &vimvars_var;
+ case 'b': return &curbuf->b_bufvar;
+ case 'w': return &curwin->w_winvar;
+ case 't': return &curtab->tp_winvar;
+ case 'l': return get_funccal_local_var();
+ case 'a': return get_funccal_args_var();
+ }
+ return NULL;
+ }
+
+ hi = hash_find(ht, varname);
+ if (HASHITEM_EMPTY(hi))
+ {
+ // For global variables we may try auto-loading the script. If it
+ // worked find the variable again. Don't auto-load a script if it was
+ // loaded already, otherwise it would be loaded every time when
+ // checking if a function name is a Funcref variable.
+ if (ht == &globvarht && !no_autoload)
+ {
+ // Note: script_autoload() may make "hi" invalid. It must either
+ // be obtained again or not used.
+ if (!script_autoload(varname, FALSE) || aborting())
+ return NULL;
+ hi = hash_find(ht, varname);
+ }
+ if (HASHITEM_EMPTY(hi))
+ return NULL;
+ }
+ return HI2DI(hi);
+ }
+
+ /*
+ * Find the hashtab used for a variable name.
+ * Return NULL if the name is not valid.
+ * Set "varname" to the start of name without ':'.
+ */
+ hashtab_T *
+ find_var_ht(char_u *name, char_u **varname)
+ {
+ hashitem_T *hi;
+ hashtab_T *ht;
+
+ if (name[0] == NUL)
+ return NULL;
+ if (name[1] != ':')
+ {
+ // The name must not start with a colon or #.
+ if (name[0] == ':' || name[0] == AUTOLOAD_CHAR)
+ return NULL;
+ *varname = name;
+
+ // "version" is "v:version" in all scopes if scriptversion < 3.
+ // Same for a few other variables marked with VV_COMPAT.
+ if (current_sctx.sc_version < 3)
+ {
+ hi = hash_find(&compat_hashtab, name);
+ if (!HASHITEM_EMPTY(hi))
+ return &compat_hashtab;
+ }
+
+ ht = get_funccal_local_ht();
+ if (ht == NULL)
+ return &globvarht; // global variable
+ return ht; // local variable
+ }
+ *varname = name + 2;
+ if (*name == 'g') // global variable
+ return &globvarht;
+ // There must be no ':' or '#' in the rest of the name, unless g: is used
+ if (vim_strchr(name + 2, ':') != NULL
+ || vim_strchr(name + 2, AUTOLOAD_CHAR) != NULL)
+ return NULL;
+ if (*name == 'b') // buffer variable
+ return &curbuf->b_vars->dv_hashtab;
+ if (*name == 'w') // window variable
+ return &curwin->w_vars->dv_hashtab;
+ if (*name == 't') // tab page variable
+ return &curtab->tp_vars->dv_hashtab;
+ if (*name == 'v') // v: variable
+ return &vimvarht;
+ if (*name == 'a') // a: function argument
+ return get_funccal_args_ht();
+ if (*name == 'l') // l: local function variable
+ return get_funccal_local_ht();
+ if (*name == 's' // script variable
+ && current_sctx.sc_sid > 0
+ && current_sctx.sc_sid <= ga_scripts.ga_len)
+ return &SCRIPT_VARS(current_sctx.sc_sid);
+ return NULL;
+ }
+
+ /*
* Get the string value of a (global/local) variable.
* Note: see tv_get_string() for how long the pointer remains valid.
* Returns NULL when it doesn't exist.
***************
*** 1275,1280 ****
--- 2202,2273 ----
}

/*
+ * Allocate a new hashtab for a sourced script. It will be used while
+ * sourcing this script and when executing functions defined in the script.
+ */
+ void
+ new_script_vars(scid_T id)
+ {
+ int i;
+ hashtab_T *ht;
+ scriptvar_T *sv;
+
+ if (ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)) == OK)
+ {
+ /* Re-allocating ga_data means that an ht_array pointing to
+ * ht_smallarray becomes invalid. We can recognize this: ht_mask is
+ * at its init value. Also reset "v_dict", it's always the same. */
+ for (i = 1; i <= ga_scripts.ga_len; ++i)
+ {
+ ht = &SCRIPT_VARS(i);
+ if (ht->ht_mask == HT_INIT_SIZE - 1)
+ ht->ht_array = ht->ht_smallarray;
+ sv = SCRIPT_SV(i);
+ sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
+ }
+
+ while (ga_scripts.ga_len < id)
+ {
+ sv = SCRIPT_SV(ga_scripts.ga_len + 1) =
+ ALLOC_CLEAR_ONE(scriptvar_T);
+ init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
+ ++ga_scripts.ga_len;
+ }
+ }
+ }
+
+ /*
+ * Initialize dictionary "dict" as a scope and set variable "dict_var" to
+ * point to it.
+ */
+ void
+ init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
+ {
+ hash_init(&dict->dv_hashtab);
+ dict->dv_lock = 0;
+ dict->dv_scope = scope;
+ dict->dv_refcount = DO_NOT_FREE_CNT;
+ dict->dv_copyID = 0;
+ dict_var->di_tv.vval.v_dict = dict;
+ dict_var->di_tv.v_type = VAR_DICT;
+ dict_var->di_tv.v_lock = VAR_FIXED;
+ dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ dict_var->di_key[0] = NUL;
+ }
+
+ /*
+ * Unreference a dictionary initialized by init_var_dict().
+ */
+ void
+ unref_var_dict(dict_T *dict)
+ {
+ /* Now the dict needs to be freed if no one else is using it, go back to
+ * normal reference counting. */
+ dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
+ dict_unref(dict);
+ }
+
+ /*
* Clean up a list of internal variables.
* Frees all allocated variables and the value they contain.
* Clears hashtab "ht", does not free it.
***************
*** 1453,1459 ****

// Handle setting internal v: variables separately where needed to
// prevent changing the type.
! if (is_vimvarht(ht))
{
if (v->di_tv.v_type == VAR_STRING)
{
--- 2446,2452 ----

// Handle setting internal v: variables separately where needed to
// prevent changing the type.
! if (ht == &vimvarht)
{
if (v->di_tv.v_type == VAR_STRING)
{
***************
*** 1501,1507 ****
else // add a new variable
{
// Can't add "v:" or "a:" variable.
! if (is_vimvarht(ht) || ht == get_funccal_args_ht())
{
semsg(_(e_illvar), name);
return;
--- 2494,2500 ----
else // add a new variable
{
// Can't add "v:" or "a:" variable.
! if (ht == &vimvarht || ht == get_funccal_args_ht())
{
semsg(_(e_illvar), name);
return;
***************
*** 1792,1797 ****
--- 2785,2819 ----
}
}

+ /*
+ * reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal,
+ * v:option_type, and v:option_command.
+ */
+ void
+ reset_v_option_vars(void)
+ {
+ set_vim_var_string(VV_OPTION_NEW, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLD, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1);
+ set_vim_var_string(VV_OPTION_TYPE, NULL, -1);
+ set_vim_var_string(VV_OPTION_COMMAND, NULL, -1);
+ }
+
+ /*
+ * Add an assert error to v:errors.
+ */
+ void
+ assert_error(garray_T *gap)
+ {
+ struct vimvar *vp = &vimvars[VV_ERRORS];
+
+ if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL)
+ /* Make sure v:errors is a list. */
+ set_vim_var_list(VV_ERRORS, list_alloc());
+ list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len);
+ }
+
int
var_exists(char_u *var)
{
*** ../vim-8.1.1938/src/proto/eval.pro 2019-08-27 22:48:12.741480663 +0200
--- src/proto/eval.pro 2019-08-29 22:02:16.891931613 +0200
***************
*** 3,9 ****
varnumber_T num_modulus(varnumber_T n1, varnumber_T n2);
void eval_init(void);
void eval_clear(void);
- void set_internal_string_var(char_u *name, char_u *value);
int var_redir_start(char_u *name, int append);
void var_redir_str(char_u *value, int value_len);
void var_redir_stop(void);
--- 3,8 ----
***************
*** 19,30 ****
char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert);
char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox);
varnumber_T eval_to_number(char_u *expr);
- void list_vim_vars(int *first);
- void list_script_vars(int *first);
- int is_vimvarht(hashtab_T *ht);
- int is_compatht(hashtab_T *ht);
- void prepare_vimvar(int idx, typval_T *save_tv);
- void restore_vimvar(int idx, typval_T *save_tv);
list_T *eval_spell_expr(char_u *badword, char_u *expr);
int get_spellword(list_T *list, char_u **pp);
typval_T *eval_expr(char_u *arg, char_u **nextcmd);
--- 18,23 ----
***************
*** 41,47 ****
void free_for_info(void *fi_void);
void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
void del_menutrans_vars(void);
- char_u *get_user_var_name(expand_T *xp, int idx);
int pattern_match(char_u *pat, char_u *text, int ic);
int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate);
int eval1(char_u **arg, typval_T *rettv, int evaluate);
--- 34,39 ----
***************
*** 69,93 ****
char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags);
int eval_isnamec(int c);
int eval_isnamec1(int c);
- void set_vim_var_nr(int idx, varnumber_T val);
- typval_T *get_vim_var_tv(int idx);
- varnumber_T get_vim_var_nr(int idx);
- char_u *get_vim_var_str(int idx);
- list_T *get_vim_var_list(int idx);
- dict_T *get_vim_var_dict(int idx);
- void set_vim_var_char(int c);
- void set_vcount(long count, long count1, int set_prevcount);
- void save_vimvars(vimvars_save_T *vvsave);
- void restore_vimvars(vimvars_save_T *vvsave);
- void set_vim_var_string(int idx, char_u *val, int len);
- void set_vim_var_list(int idx, list_T *val);
- void set_vim_var_dict(int idx, dict_T *val);
- void set_reg_var(int c);
- char_u *v_exception(char_u *oldval);
- char_u *v_throwpoint(char_u *oldval);
- char_u *set_cmdarg(exarg_T *eap, char_u *oldarg);
int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose, char_u *start_leader, char_u **end_leaderp);
typval_T *alloc_tv(void);
void free_tv(typval_T *varp);
void clear_tv(typval_T *varp);
void init_tv(typval_T *varp);
--- 61,69 ----
char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags);
int eval_isnamec(int c);
int eval_isnamec1(int c);
int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose, char_u *start_leader, char_u **end_leaderp);
typval_T *alloc_tv(void);
+ typval_T *alloc_string_tv(char_u *s);
void free_tv(typval_T *varp);
void clear_tv(typval_T *varp);
void init_tv(typval_T *varp);
***************
*** 98,109 ****
char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
char_u *tv_get_string_chk(typval_T *varp);
char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf);
- dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
- dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
- hashtab_T *find_var_ht(char_u *name, char_u **varname);
- void new_script_vars(scid_T id);
- void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);
- void unref_var_dict(dict_T *dict);
void copy_tv(typval_T *from, typval_T *to);
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret);
--- 74,79 ----
***************
*** 117,124 ****
char_u *autoload_name(char_u *name);
int script_autoload(char_u *name, int reload);
void last_set_msg(sctx_T script_ctx);
- void reset_v_option_vars(void);
- void assert_error(garray_T *gap);
int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic);
char_u *typval_tostring(typval_T *arg);
int modify_fname(char_u *src, int tilde_file, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen);
--- 87,92 ----
*** ../vim-8.1.1938/src/proto/evalvars.pro 2019-08-27 22:48:12.741480663 +0200
--- src/proto/evalvars.pro 2019-08-29 22:02:16.891931613 +0200
***************
*** 1,4 ****
--- 1,11 ----
/* evalvars.c */
+ void evalvars_init(void);
+ void evalvars_clear(void);
+ int garbage_collect_vimvars(int copyID);
+ int garbage_collect_scriptvars(int copyID);
+ void set_internal_string_var(char_u *name, char_u *value);
+ void prepare_vimvar(int idx, typval_T *save_tv);
+ void restore_vimvar(int idx, typval_T *save_tv);
void ex_let(exarg_T *eap);
void ex_const(exarg_T *eap);
int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int is_const, char_u *op);
***************
*** 7,14 ****
--- 14,46 ----
void ex_unlet(exarg_T *eap);
void ex_lockvar(exarg_T *eap);
int do_unlet(char_u *name, int forceit);
+ char_u *get_user_var_name(expand_T *xp, int idx);
+ void set_vim_var_nr(int idx, varnumber_T val);
+ typval_T *get_vim_var_tv(int idx);
+ varnumber_T get_vim_var_nr(int idx);
+ char_u *get_vim_var_str(int idx);
+ list_T *get_vim_var_list(int idx);
+ dict_T *get_vim_var_dict(int idx);
+ void set_vim_var_char(int c);
+ void set_vcount(long count, long count1, int set_prevcount);
+ void save_vimvars(vimvars_save_T *vvsave);
+ void restore_vimvars(vimvars_save_T *vvsave);
+ void set_vim_var_string(int idx, char_u *val, int len);
+ void set_vim_var_list(int idx, list_T *val);
+ void set_vim_var_dict(int idx, dict_T *val);
+ void set_reg_var(int c);
+ char_u *v_exception(char_u *oldval);
+ char_u *v_throwpoint(char_u *oldval);
+ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg);
int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload);
+ void check_vars(char_u *name, int len);
+ dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
+ dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
+ hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
+ void new_script_vars(scid_T id);
+ void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);
+ void unref_var_dict(dict_T *dict);
void vars_clear(hashtab_T *ht);
void vars_clear_ext(hashtab_T *ht, int free_val);
void delete_var(hashtab_T *ht, hashitem_T *hi);
***************
*** 19,24 ****
--- 51,58 ----
int var_check_func_name(char_u *name, int new_var);
int var_check_lock(int lock, char_u *name, int use_gettext);
int valid_varname(char_u *varname);
+ void reset_v_option_vars(void);
+ void assert_error(garray_T *gap);
int var_exists(char_u *var);
void f_gettabvar(typval_T *argvars, typval_T *rettv);
void f_gettabwinvar(typval_T *argvars, typval_T *rettv);
*** ../vim-8.1.1938/src/version.c 2019-08-29 21:32:52.248093098 +0200
--- src/version.c 2019-08-29 22:05:49.498124809 +0200
***************
*** 763,764 ****
--- 763,766 ----
{ /* Add new patch number below this line */
+ /**/
+ 1939,
/**/

--
hundred-and-one symptoms of being an internet addict:
133. You communicate with people on other continents more than you
do with your own neighbors.

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

Yegappan Lakshmanan

unread,
Aug 29, 2019, 4:23:45 PM8/29/19
to vim_dev
Hi Bram,

On Thu, Aug 29, 2019 at 1:10 PM Bram Moolenaar <Br...@moolenaar.net> wrote:
>
>
> Patch 8.1.1939
> Problem: Code for handling v: variables in generic eval file.
> Solution: Move v: variables to evalvars.c. (Yegappan Lakshmanan,
> closes #4872)
> Files: src/eval.c, src/evalvars.c, src/proto/eval.pro,
> src/proto/evalvars.pro
>

It looks like you included the first version of the patch from my PR
instead of the latest one. The tests will start to fail. Can you include
the missing changes or do you want me to create a new PR?

- Yegappan

Bram Moolenaar

unread,
Aug 29, 2019, 4:49:03 PM8/29/19
to vim...@googlegroups.com, Yegappan Lakshmanan
Strange, I just got the diff from github, it's supposed to be the most
recent one. If I look at it now I only get the diff against the current
version, it seems.

I'll fix the tests first.

--
There are three kinds of persons: Those who can count and those who can't.
Reply all
Reply to author
Forward
0 new messages