summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/eval.c532
-rw-r--r--src/evalvars.c371
-rw-r--r--src/ex_getln.c108
-rw-r--r--src/globals.h3
-rw-r--r--src/if_py_both.h4
-rw-r--r--src/proto/eval.pro12
-rw-r--r--src/proto/evalvars.pro14
-rw-r--r--src/proto/ex_getln.pro1
-rw-r--r--src/proto/scriptfile.pro3
-rw-r--r--src/scriptfile.c83
-rw-r--r--src/session.c5
-rw-r--r--src/version.c2
-rw-r--r--src/viminfo.c5
13 files changed, 604 insertions, 539 deletions
diff --git a/src/eval.c b/src/eval.c
index f32092c821..e75624345a 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -38,9 +38,6 @@ static int current_copyID = 0;
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.
*/
@@ -156,8 +153,8 @@ eval_clear(void)
free_scriptnames();
free_locales();
- /* autoloaded script names */
- ga_clear_strings(&ga_loaded);
+ // autoloaded script names
+ free_autoload_scriptnames();
// unreferenced lists and dicts
(void)garbage_collect(FALSE);
@@ -167,240 +164,6 @@ eval_clear(void)
}
#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.
@@ -671,65 +434,6 @@ eval_to_number(char_u *expr)
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.
@@ -1153,7 +857,7 @@ get_lval(
if (lp->ll_di == NULL)
{
// Can't add "v:" or "a:" variable.
- if (lp->ll_dict == &vimvardict
+ if (lp->ll_dict == get_vimvar_dict()
|| &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
{
semsg(_(e_illvar), name);
@@ -1921,31 +1625,6 @@ set_context_for_expression(
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'.
@@ -4215,7 +3894,7 @@ garbage_collect(int testing)
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);
+ abort = abort || garbage_collect_globvars(copyID);
/* function-local variables */
abort = abort || set_ref_in_call_stack(copyID);
@@ -4630,20 +4309,6 @@ set_ref_in_item(
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.
@@ -6204,112 +5869,6 @@ item_copy(
}
/*
- * 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.
@@ -6425,6 +5984,15 @@ ex_echohl(exarg_T *eap)
}
/*
+ * 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,78 +6119,6 @@ find_option_end(char_u **arg, int *opt_flags)
}
/*
- * 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.
*/
@@ -7739,6 +7235,8 @@ filter_map(typval_T *argvars, typval_T *rettv, int map)
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;
diff --git a/src/evalvars.c b/src/evalvars.c
index d7a565af20..e6fb3d8988 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -18,6 +18,8 @@
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,6 +156,7 @@ static struct vimvar
#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,6 +188,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep);
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,6 +300,12 @@ evalvars_clear(void)
#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,6 +345,148 @@ 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 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,6 +1721,31 @@ item_lock(typval_T *tv, int deep, int lock)
--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,6 +1865,47 @@ get_user_var_name(expand_T *xp, int idx)
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,7 +2539,7 @@ vars_clear_ext(hashtab_T *ht, int free_val)
* Delete a variable from hashtab "ht" at item "hi".
* Clear the variable value and free the dictitem.
*/
- void
+ static void
delete_var(hashtab_T *ht, hashitem_T *hi)
{
dictitem_T *di = HI2DI(hi);
@@ -2853,6 +3071,157 @@ var_exists(char_u *var)
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
*/
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 5d1c8efb7f..857c9c74fa 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -4317,3 +4317,111 @@ script_get(exarg_T *eap, char_u *cmd)
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