diff options
Diffstat (limited to 'src/evalvars.c')
-rw-r--r-- | src/evalvars.c | 371 |
1 files changed, 370 insertions, 1 deletions
diff --git a/src/evalvars.c b/src/evalvars.c index d7a565af2..e6fb3d898 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 */ |