To: vim_dev@googlegroups.com Subject: Patch 8.1.1939 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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, ®name, 1); - } - - /* - * Get or set v:exception. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:exception! Does not - * take care of memory allocations. - */ - char_u * - v_exception(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, ®name, 1); + } + + /* + * Get or set v:exception. If "oldval" == NULL, return the current value. + * Otherwise, restore the value to "oldval" and return NULL. + * Must always be called in pairs to save and restore v:exception! Does not + * take care of memory allocations. + */ + char_u * + v_exception(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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///