Patch 8.1.1957 Problem: More code can be moved to evalvars.c. Solution: Move code to where it fits better. (Yegappan Lakshmanan, closes #4883) Files: src/eval.c, src/evalvars.c, src/ex_getln.c, src/globals.h, src/if_py_both.h, src/proto/eval.pro, src/proto/evalvars.pro, src/proto/ex_getln.pro, src/proto/scriptfile.pro, src/scriptfile.c, src/session.c, src/viminfo.c
*** ../vim-8.1.1956/src/eval.c 2019-08-30 15:46:27.188906163 +0200 --- src/eval.c 2019-09-01 15:46:52.568427240 +0200 *************** *** 38,46 **** static int echo_attr = 0; /* attributes used for ":echo" */ - /* The names of packages that once were loaded are remembered. */ - static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; - /* * Info used by a ":for" loop. */ --- 38,43 ---- *************** *** 156,163 **** free_scriptnames(); free_locales(); ! /* autoloaded script names */ ! ga_clear_strings(&ga_loaded); // unreferenced lists and dicts (void)garbage_collect(FALSE); --- 153,160 ---- free_scriptnames(); free_locales(); ! // autoloaded script names ! free_autoload_scriptnames(); // unreferenced lists and dicts (void)garbage_collect(FALSE); *************** *** 167,406 **** } #endif - 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 */ - static char_u *redir_endp = NULL; - static char_u *redir_varname = NULL; - - /* - * Start recording command output to a variable - * When "append" is TRUE append to an existing variable. - * Returns OK if successfully completed the setup. FAIL otherwise. - */ - int - var_redir_start(char_u *name, int append) - { - int save_emsg; - int err; - typval_T tv; - - /* Catch a bad name early. */ - if (!eval_isnamec1(*name)) - { - emsg(_(e_invarg)); - return FAIL; - } - - /* Make a copy of the name, it is used in redir_lval until redir ends. */ - redir_varname = vim_strsave(name); - if (redir_varname == NULL) - return FAIL; - - redir_lval = ALLOC_CLEAR_ONE(lval_T); - if (redir_lval == NULL) - { - var_redir_stop(); - return FAIL; - } - - /* The output is stored in growarray "redir_ga" until redirection ends. */ - ga_init2(&redir_ga, (int)sizeof(char), 500); - - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) - { - clear_lval(redir_lval); - if (redir_endp != NULL && *redir_endp != NUL) - /* Trailing characters are present after the variable name */ - emsg(_(e_trailing)); - else - emsg(_(e_invarg)); - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - /* check if we can write to the variable: set it to or append an empty - * string */ - save_emsg = did_emsg; - did_emsg = FALSE; - tv.v_type = VAR_STRING; - tv.vval.v_string = (char_u *)""; - if (append) - set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"."); - else - set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"="); - clear_lval(redir_lval); - err = did_emsg; - did_emsg |= save_emsg; - if (err) - { - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - return OK; - } - - /* - * Append "value[value_len]" to the variable set by var_redir_start(). - * The actual appending is postponed until redirection ends, because the value - * appended may in fact be the string we write to, changing it may cause freed - * memory to be used: - * :redir => foo - * :let foo - * :redir END - */ - void - var_redir_str(char_u *value, int value_len) - { - int len; - - if (redir_lval == NULL) - return; - - if (value_len == -1) - len = (int)STRLEN(value); /* Append the entire string */ - else - len = value_len; /* Append only "value_len" characters */ - - if (ga_grow(&redir_ga, len) == OK) - { - mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); - redir_ga.ga_len += len; - } - else - var_redir_stop(); - } - - /* - * Stop redirecting command output to a variable. - * Frees the allocated memory. - */ - void - var_redir_stop(void) - { - typval_T tv; - - if (EVALCMD_BUSY) - { - redir_lval = NULL; - return; - } - - if (redir_lval != NULL) - { - /* If there was no error: assign the text to the variable. */ - if (redir_endp != NULL) - { - ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ - tv.v_type = VAR_STRING; - tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE, - (char_u *)"."); - clear_lval(redir_lval); - } - - /* free the collected output */ - VIM_CLEAR(redir_ga.ga_data); - - VIM_CLEAR(redir_lval); - } - VIM_CLEAR(redir_varname); - } - - int - eval_charconvert( - char_u *enc_from, - char_u *enc_to, - char_u *fname_from, - char_u *fname_to) - { - int err = FALSE; - - set_vim_var_string(VV_CC_FROM, enc_from, -1); - set_vim_var_string(VV_CC_TO, enc_to, -1); - set_vim_var_string(VV_FNAME_IN, fname_from, -1); - set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - if (eval_to_bool(p_ccv, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_CC_FROM, NULL, -1); - set_vim_var_string(VV_CC_TO, NULL, -1); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); - - if (err) - return FAIL; - return OK; - } - - # if defined(FEAT_POSTSCRIPT) || defined(PROTO) - int - eval_printexpr(char_u *fname, char_u *args) - { - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, fname, -1); - set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_CMDARG, NULL, -1); - - if (err) - { - mch_remove(fname); - return FAIL; - } - return OK; - } - # endif - - # if defined(FEAT_DIFF) || defined(PROTO) - void - eval_diff( - char_u *origfile, - char_u *newfile, - char_u *outfile) - { - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_NEW, newfile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_dex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_NEW, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); - } - - void - eval_patch( - char_u *origfile, - char_u *difffile, - char_u *outfile) - { - int err; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_DIFF, difffile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_pex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_DIFF, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); - } - # endif - /* * Top level evaluation function, returning a boolean. * Sets "error" to TRUE if there was an error. --- 164,169 ---- *************** *** 671,735 **** return retval; } - #if defined(FEAT_SPELL) || defined(PROTO) - /* - * Evaluate an expression to a list with suggestions. - * For the "expr:" part of 'spellsuggest'. - * Returns NULL when there is an error. - */ - list_T * - eval_spell_expr(char_u *badword, char_u *expr) - { - typval_T save_val; - typval_T rettv; - list_T *list = NULL; - char_u *p = skipwhite(expr); - - /* Set "v:val" to the bad word. */ - prepare_vimvar(VV_VAL, &save_val); - set_vim_var_string(VV_VAL, badword, -1); - if (p_verbose == 0) - ++emsg_off; - - if (eval1(&p, &rettv, TRUE) == OK) - { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else - list = rettv.vval.v_list; - } - - if (p_verbose == 0) - --emsg_off; - clear_tv(get_vim_var_tv(VV_VAL)); - restore_vimvar(VV_VAL, &save_val); - - return list; - } - - /* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ - int - get_spellword(list_T *list, char_u **pp) - { - listitem_T *li; - - li = list->lv_first; - if (li == NULL) - return -1; - *pp = tv_get_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) - return -1; - return (int)tv_get_number(&li->li_tv); - } - #endif - /* * Top level evaluation function. * Returns an allocated typval_T with the result. --- 434,439 ---- *************** *** 1153,1159 **** if (lp->ll_di == NULL) { // Can't add "v:" or "a:" variable. ! if (lp->ll_dict == &vimvardict || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { semsg(_(e_illvar), name); --- 857,863 ---- if (lp->ll_di == NULL) { // Can't add "v:" or "a:" variable. ! if (lp->ll_dict == get_vimvar_dict() || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { semsg(_(e_illvar), name); *************** *** 1921,1951 **** xp->xp_pattern = arg; } - #if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) - /* - * Delete all "menutrans_" variables. - */ - void - del_menutrans_vars(void) - { - hashitem_T *hi; - int todo; - - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); - } - } - hash_unlock(&globvarht); - } - #endif - /* * Return TRUE if "pat" matches "text". * Does not use 'cpo' and always uses 'magic'. --- 1625,1630 ---- *************** *** 4215,4221 **** abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, NULL, NULL); /* global variables */ ! abort = abort || set_ref_in_ht(&globvarht, copyID, NULL); /* function-local variables */ abort = abort || set_ref_in_call_stack(copyID); --- 3894,3900 ---- abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, NULL, NULL); /* global variables */ ! abort = abort || garbage_collect_globvars(copyID); /* function-local variables */ abort = abort || set_ref_in_call_stack(copyID); *************** *** 4630,4649 **** return abort; } - static char * - get_var_special_name(int nr) - { - switch (nr) - { - case VVAL_FALSE: return "v:false"; - case VVAL_TRUE: return "v:true"; - case VVAL_NONE: return "v:none"; - case VVAL_NULL: return "v:null"; - } - internal_error("get_var_special_name()"); - return "42"; - } - /* * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. --- 4309,4314 ---- *************** *** 6204,6315 **** } /* - * This function is used by f_input() and f_inputdialog() functions. The third - * argument to f_input() specifies the type of completion to use at the - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ - void - get_user_input( - typval_T *argvars, - typval_T *rettv, - int inputdialog, - int secret) - { - char_u *prompt = tv_get_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; - int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - #ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. When running tests - * with --not-a-term we assume feedkeys() will be used. */ - if (no_console_input() && !is_not_a_term()) - return; - #endif - - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) - { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) - p = prompt; - else - { - ++p; - c = *p; - *p = NUL; - msg_start(); - msg_clr_eos(); - msg_puts_attr((char *)prompt, echo_attr); - msg_didout = FALSE; - msg_starthere(); - *p = c; - } - cmdline_row = msg_row; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - defstr = tv_get_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) - stuffReadbuffSpec(defstr); - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) - { - char_u *xp_name; - int xp_namelen; - long argt; - - /* input() with a third argument: completion */ - rettv->vval.v_string = NULL; - - xp_name = tv_get_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) - return; - - xp_namelen = (int)STRLEN(xp_name); - - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) - return; - } - } - - if (defstr != NULL) - { - int save_ex_normal_busy = ex_normal_busy; - - ex_normal_busy = 0; - rettv->vval.v_string = - getcmdline_prompt(secret ? NUL : '@', p, echo_attr, - xp_type, xp_arg); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(tv_get_string_buf( - &argvars[2], buf)); - - vim_free(xp_arg); - - /* since the user typed this, no need to wait for return */ - need_wait_return = FALSE; - msg_didout = FALSE; - } - cmd_silent = cmd_silent_save; - } - - /* * ":echo expr1 ..." print each argument separated with a space, add a * newline at the end. * ":echon expr1 ..." print each argument plain. --- 5869,5874 ---- *************** *** 6425,6430 **** --- 5984,5998 ---- } /* + * Returns the :echo attribute + */ + int + get_echo_attr(void) + { + return echo_attr; + } + + /* * ":execute expr1 ..." execute the result of an expression. * ":echomsg expr1 ..." Print a message * ":echoerr expr1 ..." Print an error *************** *** 6551,6628 **** } /* - * Return the autoload script name for a function or variable name. - * Returns NULL when out of memory. - * Caller must make sure that "name" contains AUTOLOAD_CHAR. - */ - char_u * - autoload_name(char_u *name) - { - char_u *p, *q = NULL; - char_u *scriptname; - - // Get the script file name: replace '#' with '/', append ".vim". - scriptname = alloc(STRLEN(name) + 14); - if (scriptname == NULL) - return NULL; - STRCPY(scriptname, "autoload/"); - STRCAT(scriptname, name); - for (p = scriptname + 9; (p = vim_strchr(p, AUTOLOAD_CHAR)) != NULL; - q = p, ++p) - *p = '/'; - STRCPY(q, ".vim"); - return scriptname; - } - - /* - * If "name" has a package name try autoloading the script for it. - * Return TRUE if a package was loaded. - */ - int - script_autoload( - char_u *name, - int reload) /* load script again when already loaded */ - { - char_u *p; - char_u *scriptname, *tofree; - int ret = FALSE; - int i; - - /* If there is no '#' after name[0] there is no package name. */ - p = vim_strchr(name, AUTOLOAD_CHAR); - if (p == NULL || p == name) - return FALSE; - - tofree = scriptname = autoload_name(name); - if (scriptname == NULL) - return FALSE; - - /* Find the name in the list of previously loaded package names. Skip - * "autoload/", it's always the same. */ - for (i = 0; i < ga_loaded.ga_len; ++i) - if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) - break; - if (!reload && i < ga_loaded.ga_len) - ret = FALSE; /* was loaded already */ - else - { - /* Remember the name if it wasn't loaded already. */ - if (i == ga_loaded.ga_len && ga_grow(&ga_loaded, 1) == OK) - { - ((char_u **)ga_loaded.ga_data)[ga_loaded.ga_len++] = scriptname; - tofree = NULL; - } - - /* Try loading the package from $VIMRUNTIME/autoload/<name>.vim */ - if (source_runtime(scriptname, 0) == OK) - ret = TRUE; - } - - vim_free(tofree); - return ret; - } - - /* * Display script name where an item was last set. * Should only be invoked when 'verbose' is non-zero. */ --- 6119,6124 ---- *************** *** 7739,7744 **** --- 7235,7242 ---- prepare_vimvar(VV_KEY, &save_key); if (argvars[0].v_type == VAR_DICT) { + set_vim_var_type(VV_KEY, VAR_STRING); + ht = &d->dv_hashtab; hash_lock(ht); todo = (int)ht->ht_used; *** ../vim-8.1.1956/src/evalvars.c 2019-08-30 15:46:27.188906163 +0200 --- src/evalvars.c 2019-09-01 15:46:52.568427240 +0200 *************** *** 18,23 **** --- 18,25 ---- static char *e_letunexp = N_("E18: Unexpected characters in :let"); static dictitem_T globvars_var; // variable used for g: + static dict_T globvardict; // Dictionary with g: variables + #define globvarht globvardict.dv_hashtab /* * Old Vim variables such as "v:version" are also available without the "v:". *************** *** 154,159 **** --- 156,162 ---- #define vv_tv vv_di.di_tv static dictitem_T vimvars_var; // variable used for v: + static dict_T vimvardict; // Dictionary with v: variables #define vimvarht vimvardict.dv_hashtab // for VIM_VERSION_ defines *************** *** 185,190 **** --- 188,194 ---- static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit); static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); static void item_lock(typval_T *tv, int deep, int lock); + static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); *************** *** 296,301 **** --- 300,311 ---- #endif int + garbage_collect_globvars(int copyID) + { + return set_ref_in_ht(&globvarht, copyID, NULL); + } + + int garbage_collect_vimvars(int copyID) { return set_ref_in_ht(&vimvarht, copyID, NULL); *************** *** 335,340 **** --- 345,492 ---- } } + int + eval_charconvert( + char_u *enc_from, + char_u *enc_to, + char_u *fname_from, + char_u *fname_to) + { + int err = FALSE; + + set_vim_var_string(VV_CC_FROM, enc_from, -1); + set_vim_var_string(VV_CC_TO, enc_to, -1); + set_vim_var_string(VV_FNAME_IN, fname_from, -1); + set_vim_var_string(VV_FNAME_OUT, fname_to, -1); + if (eval_to_bool(p_ccv, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_CC_FROM, NULL, -1); + set_vim_var_string(VV_CC_TO, NULL, -1); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + + if (err) + return FAIL; + return OK; + } + + # if defined(FEAT_POSTSCRIPT) || defined(PROTO) + int + eval_printexpr(char_u *fname, char_u *args) + { + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, fname, -1); + set_vim_var_string(VV_CMDARG, args, -1); + if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_CMDARG, NULL, -1); + + if (err) + { + mch_remove(fname); + return FAIL; + } + return OK; + } + # endif + + # if defined(FEAT_DIFF) || defined(PROTO) + void + eval_diff( + char_u *origfile, + char_u *newfile, + char_u *outfile) + { + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_NEW, newfile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_dex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_NEW, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + } + + void + eval_patch( + char_u *origfile, + char_u *difffile, + char_u *outfile) + { + int err; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_DIFF, difffile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_pex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_DIFF, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + } + # endif + + #if defined(FEAT_SPELL) || defined(PROTO) + /* + * Evaluate an expression to a list with suggestions. + * For the "expr:" part of 'spellsuggest'. + * Returns NULL when there is an error. + */ + list_T * + eval_spell_expr(char_u *badword, char_u *expr) + { + typval_T save_val; + typval_T rettv; + list_T *list = NULL; + char_u *p = skipwhite(expr); + + // Set "v:val" to the bad word. + prepare_vimvar(VV_VAL, &save_val); + set_vim_var_string(VV_VAL, badword, -1); + if (p_verbose == 0) + ++emsg_off; + + if (eval1(&p, &rettv, TRUE) == OK) + { + if (rettv.v_type != VAR_LIST) + clear_tv(&rettv); + else + list = rettv.vval.v_list; + } + + if (p_verbose == 0) + --emsg_off; + clear_tv(get_vim_var_tv(VV_VAL)); + restore_vimvar(VV_VAL, &save_val); + + return list; + } + + /* + * "list" is supposed to contain two items: a word and a number. Return the + * word in "pp" and the number as the return value. + * Return -1 if anything isn't right. + * Used to get the good word and score from the eval_spell_expr() result. + */ + int + get_spellword(list_T *list, char_u **pp) + { + listitem_T *li; + + li = list->lv_first; + if (li == NULL) + return -1; + *pp = tv_get_string(&li->li_tv); + + li = li->li_next; + if (li == NULL) + return -1; + return (int)tv_get_number(&li->li_tv); + } + #endif + /* * Prepare v: variable "idx" to be used. * Save the current typeval in "save_tv". *************** *** 1569,1574 **** --- 1721,1751 ---- --recurse; } + #if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) + /* + * Delete all "menutrans_" variables. + */ + void + del_menutrans_vars(void) + { + hashitem_T *hi; + int todo; + + hash_lock(&globvarht); + todo = (int)globvarht.ht_used; + for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) + delete_var(&globvarht, hi); + } + } + hash_unlock(&globvarht); + } + #endif + /* * Local string buffer for the next two functions to store a variable name * with its prefix. Allocated in cat_prefix_varname(), freed later in *************** *** 1688,1693 **** --- 1865,1911 ---- return NULL; } + char * + get_var_special_name(int nr) + { + switch (nr) + { + case VVAL_FALSE: return "v:false"; + case VVAL_TRUE: return "v:true"; + case VVAL_NONE: return "v:none"; + case VVAL_NULL: return "v:null"; + } + internal_error("get_var_special_name()"); + return "42"; + } + + /* + * Returns the global variable dictionary + */ + dict_T * + get_globvar_dict(void) + { + return &globvardict; + } + + /* + * Returns the global variable hash table + */ + hashtab_T * + get_globvar_ht(void) + { + return &globvarht; + } + + /* + * Returns the v: variable dictionary + */ + dict_T * + get_vimvar_dict(void) + { + return &vimvardict; + } + /* * Set type of v: variable to "type". */ *************** *** 2321,2327 **** * Delete a variable from hashtab "ht" at item "hi". * Clear the variable value and free the dictitem. */ ! void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = HI2DI(hi); --- 2539,2545 ---- * Delete a variable from hashtab "ht" at item "hi". * Clear the variable value and free the dictitem. */ ! static void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = HI2DI(hi); *************** *** 2853,2858 **** --- 3071,3227 ---- return n; } + 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 + static char_u *redir_endp = NULL; + static char_u *redir_varname = NULL; + + /* + * Start recording command output to a variable + * When "append" is TRUE append to an existing variable. + * Returns OK if successfully completed the setup. FAIL otherwise. + */ + int + var_redir_start(char_u *name, int append) + { + int save_emsg; + int err; + typval_T tv; + + // Catch a bad name early. + if (!eval_isnamec1(*name)) + { + emsg(_(e_invarg)); + return FAIL; + } + + // Make a copy of the name, it is used in redir_lval until redir ends. + redir_varname = vim_strsave(name); + if (redir_varname == NULL) + return FAIL; + + redir_lval = ALLOC_CLEAR_ONE(lval_T); + if (redir_lval == NULL) + { + var_redir_stop(); + return FAIL; + } + + // The output is stored in growarray "redir_ga" until redirection ends. + ga_init2(&redir_ga, (int)sizeof(char), 500); + + // Parse the variable name (can be a dict or list entry). + redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, + FNE_CHECK_START); + if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) + { + clear_lval(redir_lval); + if (redir_endp != NULL && *redir_endp != NUL) + // Trailing characters are present after the variable name + emsg(_(e_trailing)); + else + emsg(_(e_invarg)); + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + // check if we can write to the variable: set it to or append an empty + // string + save_emsg = did_emsg; + did_emsg = FALSE; + tv.v_type = VAR_STRING; + tv.vval.v_string = (char_u *)""; + if (append) + set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"."); + else + set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"="); + clear_lval(redir_lval); + err = did_emsg; + did_emsg |= save_emsg; + if (err) + { + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + return OK; + } + + /* + * Append "value[value_len]" to the variable set by var_redir_start(). + * The actual appending is postponed until redirection ends, because the value + * appended may in fact be the string we write to, changing it may cause freed + * memory to be used: + * :redir => foo + * :let foo + * :redir END + */ + void + var_redir_str(char_u *value, int value_len) + { + int len; + + if (redir_lval == NULL) + return; + + if (value_len == -1) + len = (int)STRLEN(value); // Append the entire string + else + len = value_len; // Append only "value_len" characters + + if (ga_grow(&redir_ga, len) == OK) + { + mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); + redir_ga.ga_len += len; + } + else + var_redir_stop(); + } + + /* + * Stop redirecting command output to a variable. + * Frees the allocated memory. + */ + void + var_redir_stop(void) + { + typval_T tv; + + if (EVALCMD_BUSY) + { + redir_lval = NULL; + return; + } + + if (redir_lval != NULL) + { + // If there was no error: assign the text to the variable. + if (redir_endp != NULL) + { + ga_append(&redir_ga, NUL); // Append the trailing NUL. + tv.v_type = VAR_STRING; + tv.vval.v_string = redir_ga.ga_data; + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = get_lval(redir_varname, NULL, redir_lval, + FALSE, FALSE, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) + set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE, + (char_u *)"."); + clear_lval(redir_lval); + } + + // free the collected output + VIM_CLEAR(redir_ga.ga_data); + + VIM_CLEAR(redir_lval); + } + VIM_CLEAR(redir_varname); + } + /* * "gettabvar()" function */ *** ../vim-8.1.1956/src/ex_getln.c 2019-08-21 14:36:29.387376100 +0200 --- src/ex_getln.c 2019-09-01 15:46:52.568427240 +0200 *************** *** 4317,4319 **** --- 4317,4427 ---- return (char_u *)ga.ga_data; } + + #if defined(FEAT_EVAL) || defined(PROTO) + /* + * This function is used by f_input() and f_inputdialog() functions. The third + * argument to f_input() specifies the type of completion to use at the + * prompt. The third argument to f_inputdialog() specifies the value to return + * when the user cancels the prompt. + */ + void + get_user_input( + typval_T *argvars, + typval_T *rettv, + int inputdialog, + int secret) + { + char_u *prompt = tv_get_string_chk(&argvars[0]); + char_u *p = NULL; + int c; + char_u buf[NUMBUFLEN]; + int cmd_silent_save = cmd_silent; + char_u *defstr = (char_u *)""; + int xp_type = EXPAND_NOTHING; + char_u *xp_arg = NULL; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + #ifdef NO_CONSOLE_INPUT + // While starting up, there is no place to enter text. When running tests + // with --not-a-term we assume feedkeys() will be used. + if (no_console_input() && !is_not_a_term()) + return; + #endif + + cmd_silent = FALSE; // Want to see the prompt. + if (prompt != NULL) + { + // Only the part of the message after the last NL is considered as + // prompt for the command line + p = vim_strrchr(prompt, '\n'); + if (p == NULL) + p = prompt; + else + { + ++p; + c = *p; + *p = NUL; + msg_start(); + msg_clr_eos(); + msg_puts_attr((char *)prompt, get_echo_attr()); + msg_didout = FALSE; + msg_starthere(); + *p = c; + } + cmdline_row = msg_row; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + defstr = tv_get_string_buf_chk(&argvars[1], buf); + if (defstr != NULL) + stuffReadbuffSpec(defstr); + + if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) + { + char_u *xp_name; + int xp_namelen; + long argt; + + // input() with a third argument: completion + rettv->vval.v_string = NULL; + + xp_name = tv_get_string_buf_chk(&argvars[2], buf); + if (xp_name == NULL) + return; + + xp_namelen = (int)STRLEN(xp_name); + + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) + return; + } + } + + if (defstr != NULL) + { + int save_ex_normal_busy = ex_normal_busy; + + ex_normal_busy = 0; + rettv->vval.v_string = + getcmdline_prompt(secret ? NUL : '@', p, get_echo_attr(), + xp_type, xp_arg); + ex_normal_busy = save_ex_normal_busy; + } + if (inputdialog && rettv->vval.v_string == NULL + && argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + rettv->vval.v_string = vim_strsave(tv_get_string_buf( + &argvars[2], buf)); + + vim_free(xp_arg); + + // since the user typed this, no need to wait for return + need_wait_return = FALSE; + msg_didout = FALSE; + } + cmd_silent = cmd_silent_save; + } + #endif *** ../vim-8.1.1956/src/globals.h 2019-08-27 22:48:12.741480663 +0200 --- src/globals.h 2019-09-01 15:46:52.568427240 +0200 *************** *** 191,199 **** EXTERN int emsg_severe INIT(= FALSE); // use message of next of several // emsg() calls for throw EXTERN int did_endif INIT(= FALSE); // just had ":endif" - EXTERN dict_T vimvardict; // Dictionary with v: variables - EXTERN dict_T globvardict; // Dictionary with g: variables - #define globvarht globvardict.dv_hashtab #endif EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown --- 191,196 ---- *** ../vim-8.1.1956/src/if_py_both.h 2019-06-23 01:46:11.840059790 +0200 --- src/if_py_both.h 2019-09-01 15:46:52.568427240 +0200 *************** *** 6847,6854 **** return -1; ADD_OBJECT(m, "error", VimError); ! ADD_CHECKED_OBJECT(m, "vars", NEW_DICTIONARY(&globvardict)); ! ADD_CHECKED_OBJECT(m, "vvars", NEW_DICTIONARY(&vimvardict)); ADD_CHECKED_OBJECT(m, "options", OptionsNew(SREQ_GLOBAL, NULL, dummy_check, NULL)); --- 6847,6854 ---- return -1; ADD_OBJECT(m, "error", VimError); ! ADD_CHECKED_OBJECT(m, "vars", NEW_DICTIONARY(get_globvar_dict())); ! ADD_CHECKED_OBJECT(m, "vvars", NEW_DICTIONARY(get_vimvar_dict())); ADD_CHECKED_OBJECT(m, "options", OptionsNew(SREQ_GLOBAL, NULL, dummy_check, NULL)); *** ../vim-8.1.1956/src/proto/eval.pro 2019-08-30 15:46:27.188906163 +0200 --- src/proto/eval.pro 2019-09-01 15:46:52.568427240 +0200 *************** *** 3,15 **** varnumber_T num_modulus(varnumber_T n1, varnumber_T n2); void eval_init(void); void eval_clear(void); - int var_redir_start(char_u *name, int append); - void var_redir_str(char_u *value, int value_len); - void var_redir_stop(void); - int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to); - int eval_printexpr(char_u *fname, char_u *args); - void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); - void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip); int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); int eval_expr_to_bool(typval_T *expr, int *error); --- 3,8 ---- *************** *** 33,39 **** int next_for_item(void *fi_void, char_u *arg); 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); 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); --- 26,31 ---- *************** *** 76,88 **** char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf); 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); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); void ex_execute(exarg_T *eap); char_u *find_option_end(char_u **arg, int *opt_flags); - char_u *autoload_name(char_u *name); - int script_autoload(char_u *name, int reload); void last_set_msg(sctx_T script_ctx); int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic); char_u *typval_tostring(typval_T *arg); --- 68,78 ---- char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); + int get_echo_attr(void); void ex_execute(exarg_T *eap); char_u *find_option_end(char_u **arg, int *opt_flags); void last_set_msg(sctx_T script_ctx); int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic); char_u *typval_tostring(typval_T *arg); *** ../vim-8.1.1956/src/proto/evalvars.pro 2019-08-30 15:46:27.188906163 +0200 --- src/proto/evalvars.pro 2019-09-01 15:46:52.568427240 +0200 *************** *** 1,9 **** --- 1,14 ---- /* evalvars.c */ void evalvars_init(void); void evalvars_clear(void); + int garbage_collect_globvars(int copyID); int garbage_collect_vimvars(int copyID); int garbage_collect_scriptvars(int copyID); void set_internal_string_var(char_u *name, char_u *value); + int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to); + int eval_printexpr(char_u *fname, char_u *args); + void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); + void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); void prepare_vimvar(int idx, typval_T *save_tv); void restore_vimvar(int idx, typval_T *save_tv); void ex_let(exarg_T *eap); *************** *** 14,20 **** --- 19,30 ---- void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); int do_unlet(char_u *name, int forceit); + void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); + char *get_var_special_name(int nr); + dict_T *get_globvar_dict(void); + hashtab_T *get_globvar_ht(void); + dict_T *get_vimvar_dict(void); void set_vim_var_type(int idx, vartype_T type); void set_vim_var_nr(int idx, varnumber_T val); typval_T *get_vim_var_tv(int idx); *************** *** 44,50 **** 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); void set_var(char_u *name, typval_T *tv, int copy); void set_var_const(char_u *name, typval_T *tv, int copy, int is_const); int var_check_ro(int flags, char_u *name, int use_gettext); --- 54,59 ---- *************** *** 55,60 **** --- 64,72 ---- void reset_v_option_vars(void); void assert_error(garray_T *gap); int var_exists(char_u *var); + int var_redir_start(char_u *name, int append); + void var_redir_str(char_u *value, int value_len); + void var_redir_stop(void); void f_gettabvar(typval_T *argvars, typval_T *rettv); void f_gettabwinvar(typval_T *argvars, typval_T *rettv); void f_getwinvar(typval_T *argvars, typval_T *rettv); *** ../vim-8.1.1956/src/proto/ex_getln.pro 2019-08-18 21:43:52.138401489 +0200 --- src/proto/ex_getln.pro 2019-09-01 15:46:52.568427240 +0200 *************** *** 35,38 **** --- 35,39 ---- int get_cmdline_firstc(void); int get_list_range(char_u **str, int *num1, int *num2); char_u *script_get(exarg_T *eap, char_u *cmd); + void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); /* vim: set ft=c : */ *** ../vim-8.1.1956/src/proto/scriptfile.pro 2019-08-25 15:40:39.662739502 +0200 --- src/proto/scriptfile.pro 2019-09-01 15:46:52.568427240 +0200 *************** *** 20,25 **** --- 20,26 ---- void scriptnames_slash_adjust(void); char_u *get_scriptname(scid_T id); void free_scriptnames(void); + void free_autoload_scriptnames(void); linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie); char_u *getsourceline(int c, void *cookie, int indent, int do_concat); void ex_scriptencoding(exarg_T *eap); *************** *** 27,30 **** --- 28,33 ---- void ex_finish(exarg_T *eap); void do_finish(exarg_T *eap, int reanimate); int source_finished(char_u *(*fgetline)(int, void *, int, int), void *cookie); + char_u *autoload_name(char_u *name); + int script_autoload(char_u *name, int reload); /* vim: set ft=c : */ *** ../vim-8.1.1956/src/scriptfile.c 2019-08-25 15:40:39.662739502 +0200 --- src/scriptfile.c 2019-09-01 15:46:52.572427200 +0200 *************** *** 13,18 **** --- 13,23 ---- #include "vim.h" + #if defined(FEAT_EVAL) || defined(PROTO) + // The names of packages that once were loaded are remembered. + static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; + #endif + /* * ":runtime [what] {name}" */ *************** *** 1334,1339 **** --- 1339,1350 ---- vim_free(SCRIPT_ITEM(i).sn_name); ga_clear(&script_items); } + + void + free_autoload_scriptnames(void) + { + ga_clear_strings(&ga_loaded); + } # endif #endif *************** *** 1690,1693 **** --- 1701,1776 ---- && ((struct source_cookie *)getline_cookie( fgetline, cookie))->finished); } + + /* + * Return the autoload script name for a function or variable name. + * Returns NULL when out of memory. + * Caller must make sure that "name" contains AUTOLOAD_CHAR. + */ + char_u * + autoload_name(char_u *name) + { + char_u *p, *q = NULL; + char_u *scriptname; + + // Get the script file name: replace '#' with '/', append ".vim". + scriptname = alloc(STRLEN(name) + 14); + if (scriptname == NULL) + return NULL; + STRCPY(scriptname, "autoload/"); + STRCAT(scriptname, name); + for (p = scriptname + 9; (p = vim_strchr(p, AUTOLOAD_CHAR)) != NULL; + q = p, ++p) + *p = '/'; + STRCPY(q, ".vim"); + return scriptname; + } + + /* + * If "name" has a package name try autoloading the script for it. + * Return TRUE if a package was loaded. + */ + int + script_autoload( + char_u *name, + int reload) // load script again when already loaded + { + char_u *p; + char_u *scriptname, *tofree; + int ret = FALSE; + int i; + + // If there is no '#' after name[0] there is no package name. + p = vim_strchr(name, AUTOLOAD_CHAR); + if (p == NULL || p == name) + return FALSE; + + tofree = scriptname = autoload_name(name); + if (scriptname == NULL) + return FALSE; + + // Find the name in the list of previously loaded package names. Skip + // "autoload/", it's always the same. + for (i = 0; i < ga_loaded.ga_len; ++i) + if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) + break; + if (!reload && i < ga_loaded.ga_len) + ret = FALSE; // was loaded already + else + { + // Remember the name if it wasn't loaded already. + if (i == ga_loaded.ga_len && ga_grow(&ga_loaded, 1) == OK) + { + ((char_u **)ga_loaded.ga_data)[ga_loaded.ga_len++] = scriptname; + tofree = NULL; + } + + // Try loading the package from $VIMRUNTIME/autoload/<name>.vim + if (source_runtime(scriptname, 0) == OK) + ret = TRUE; + } + + vim_free(tofree); + return ret; + } #endif *** ../vim-8.1.1956/src/session.c 2019-07-28 15:21:50.809275875 +0200 --- src/session.c 2019-09-01 15:46:52.572427200 +0200 *************** *** 500,512 **** static int store_session_globals(FILE *fd) { hashitem_T *hi; dictitem_T *this_var; int todo; char_u *p, *t; ! todo = (int)globvarht.ht_used; ! for (hi = globvarht.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { --- 500,513 ---- static int store_session_globals(FILE *fd) { + hashtab_T *gvht = get_globvar_ht(); hashitem_T *hi; dictitem_T *this_var; int todo; char_u *p, *t; ! todo = (int)gvht->ht_used; ! for (hi = gvht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { *** ../vim-8.1.1956/src/viminfo.c 2019-08-06 21:59:37.763982481 +0200 --- src/viminfo.c 2019-09-01 15:46:52.572427200 +0200 *************** *** 1277,1282 **** --- 1277,1283 ---- static void write_viminfo_varlist(FILE *fp) { + hashtab_T *gvht = get_globvar_ht(); hashitem_T *hi; dictitem_T *this_var; int todo; *************** *** 1290,1297 **** fputs(_("\n# global variables:\n"), fp); ! todo = (int)globvarht.ht_used; ! for (hi = globvarht.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { --- 1291,1298 ---- fputs(_("\n# global variables:\n"), fp); ! todo = (int)gvht->ht_used; ! for (hi = gvht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { *** ../vim-8.1.1956/src/version.c 2019-09-01 15:26:20.043537901 +0200 --- src/version.c 2019-09-01 16:00:36.061152573 +0200 *************** *** 763,764 **** --- 763,766 ---- { /* Add new patch number below this line */ + /**/ + 1957, /**/ -- hundred-and-one symptoms of being an internet addict: 160. You get in the elevator and double-click the button for the floor you want. /// Bram Moolenaar -- b...@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 /// -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/vim_dev/201909011403.x81E3GEf032375%40masaka.moolenaar.net.