diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 125 |
1 files changed, 76 insertions, 49 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fd2c3a6dd8..1cef91785c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -83,7 +83,7 @@ #include "nvim/os/time.h" #include "nvim/os/channel.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/private/defs.h" +#include "nvim/api/vim.h" #include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/os/dl.h" #include "nvim/os/provider.h" @@ -193,6 +193,9 @@ static int current_copyID = 0; #define COPYID_INC 2 #define COPYID_MASK (~0x1) +/// Abort conversion to string after a recursion error. +static bool did_echo_string_emsg = false; + /* * Array to hold the hashtab with variables local to each sourced script. * Each item holds a variable (nameless) that points to the dict_T. @@ -5322,6 +5325,9 @@ list_join_inner ( } line_breakcheck(); + if (did_echo_string_emsg) { // recursion error, bail out + break; + } } /* Allocate result buffer with its total size, avoid re-allocation and @@ -5945,8 +5951,10 @@ static char_u *dict2string(typval_T *tv, int copyID) if (s != NULL) ga_concat(&ga, s); free(tofree); - if (s == NULL) + if (s == NULL || did_echo_string_emsg) { break; + } + line_breakcheck(); } } if (todo > 0) { @@ -6077,9 +6085,15 @@ static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int co char_u *r = NULL; if (recurse >= DICT_MAXNEST) { - EMSG(_("E724: variable nested too deep for displaying")); + if (!did_echo_string_emsg) { + // Only give this message once for a recursive call to avoid + // flooding the user with errors. And stop iterating over lists + // and dicts. + did_echo_string_emsg = true; + EMSG(_("E724: variable nested too deep for displaying")); + } *tofree = NULL; - return NULL; + return (char_u *)"{E724}"; } ++recurse; @@ -6134,7 +6148,9 @@ static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int co *tofree = NULL; } - --recurse; + if (--recurse == 0) { + did_echo_string_emsg = false; + } return r; } @@ -6392,7 +6408,7 @@ static struct fst { {"getwinposy", 0, 0, f_getwinposy}, {"getwinvar", 2, 3, f_getwinvar}, {"glob", 1, 3, f_glob}, - {"globpath", 2, 3, f_globpath}, + {"globpath", 2, 4, f_globpath}, {"has", 1, 1, f_has}, {"has_key", 2, 2, f_has_key}, {"haslocaldir", 0, 0, f_haslocaldir}, @@ -8283,11 +8299,8 @@ static void f_extend(typval_T *argvars, typval_T *rettv) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv) { - int remap = TRUE; - char_u *keys, *flags; + char_u *keys, *flags = NULL; char_u nbuf[NUMBUFLEN]; - int typed = FALSE; - char_u *keys_esc; /* This is not allowed in the sandbox. If the commands would still be * executed in the sandbox it would be OK, but it probably happens later, @@ -8299,23 +8312,10 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv) if (*keys != NUL) { if (argvars[1].v_type != VAR_UNKNOWN) { flags = get_tv_string_buf(&argvars[1], nbuf); - for (; *flags != NUL; ++flags) { - switch (*flags) { - case 'n': remap = FALSE; break; - case 'm': remap = TRUE; break; - case 't': typed = TRUE; break; - } - } } - /* Need to escape K_SPECIAL and CSI before putting the string in the - * typeahead buffer. */ - keys_esc = vim_strsave_escape_csi(keys); - ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), - typebuf.tb_len, !typed, FALSE); - free(keys_esc); - if (vgetc_busy) - typebuf_was_filled = TRUE; + vim_feedkeys(cstr_as_string((char *)keys), + cstr_as_string((char *)flags)); } } @@ -9608,27 +9608,50 @@ static void f_glob(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = NULL; } -/* - * "globpath()" function - */ +/// "globpath()" function static void f_globpath(typval_T *argvars, typval_T *rettv) { - int flags = 0; - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); - int error = FALSE; + int flags = 0; // Flags for globpath. + int error = false; - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - if (argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error)) - flags |= WILD_KEEP_ALL; + // Return a string, or a list if the optional third argument is non-zero. rettv->v_type = VAR_STRING; - if (file == NULL || error) + + if (argvars[2].v_type != VAR_UNKNOWN) { + // When the optional second argument is non-zero, don't remove matches + // for 'wildignore' and don't put matches for 'suffixes' at the end. + if (get_tv_number_chk(&argvars[2], &error)) { + flags |= WILD_KEEP_ALL; + } + + if (argvars[3].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[3], &error)) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + } + + char_u buf1[NUMBUFLEN]; + char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + if (file != NULL && !error) { + garray_T ga; + ga_init(&ga, (int)sizeof(char_u *), 10); + globpath(get_tv_string(&argvars[0]), file, &ga, flags); + + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); + } else { + rettv_list_alloc(rettv); + for (int i = 0; i < ga.ga_len; i++) { + list_append_string(rettv->vval.v_list, + ((char_u **)(ga.ga_data))[i], -1); + } + } + + ga_clear_strings(&ga); + } else { rettv->vval.v_string = NULL; - else - rettv->vval.v_string = globpath(get_tv_string(&argvars[0]), file, - flags); + } } /* @@ -9745,9 +9768,6 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif "tag_binary", "tag_old_static", -#ifdef FEAT_TAG_ANYWHITE - "tag_any_white", -#endif #ifdef TERMINFO "terminfo", #endif @@ -18032,7 +18052,10 @@ call_user_func ( if (argvars[i].v_type == VAR_NUMBER) msg_outnum((long)argvars[i].vval.v_number); else { + // Do not want errors such as E724 here. + ++emsg_off; s = tv2string(&argvars[i], &tofree, numbuf2, 0); + --emsg_off; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -18114,10 +18137,12 @@ call_user_func ( char_u *tofree; char_u *s; - /* The value may be very long. Skip the middle part, so that we - * have some idea how it starts and ends. smsg() would always - * truncate it at the end. */ + // The value may be very long. Skip the middle part, so that we + // have some idea how it starts and ends. smsg() would always + // truncate it at the end. Don't want errors such as E724 here. + ++emsg_off; s = tv2string(fc->rettv, &tofree, numbuf2, 0); + --emsg_off; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -19019,8 +19044,10 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) if (regmatch.startp[0] == regmatch.endp[0]) { if (zero_width == regmatch.startp[0]) { /* avoid getting stuck on a match with an empty string */ - *((char_u *)ga.ga_data + ga.ga_len) = *tail++; - ++ga.ga_len; + int i = MB_PTR2LEN(tail); + memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + ga.ga_len += i; + tail += i; continue; } zero_width = regmatch.startp[0]; |