diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 453 |
1 files changed, 240 insertions, 213 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e60cc4e218..56a6632fad 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + /* * eval.c: Expression evaluation. */ @@ -463,6 +466,7 @@ typedef struct { int refcount; long timeout; bool stopped; + bool paused; Callback callback; } timer_T; @@ -2379,9 +2383,9 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, &tv, &di, true, false) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, (const char *)lp->ll_name, - STRLEN(lp->ll_name)) + TV_CSTRING) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name)))) + TV_CSTRING))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { set_var(lp->ll_name, lp->ll_name_len, &tv, false); } @@ -2394,7 +2398,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, } else if (tv_check_lock(lp->ll_newkey == NULL ? lp->ll_tv->v_lock : lp->ll_tv->vval.v_dict->dv_lock, - (const char *)lp->ll_name, STRLEN(lp->ll_name))) { + (const char *)lp->ll_name, TV_CSTRING)) { } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; @@ -2403,7 +2407,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, for (listitem_T *ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) { + TV_CSTRING)) { return; } ri = ri->li_next; @@ -2742,12 +2746,13 @@ void ex_call(exarg_T *eap) * call, and the loop is broken. */ if (eap->skip) { - ++emsg_skip; - lnum = eap->line2; /* do it once, also with an invalid range */ - } else + emsg_skip++; + lnum = eap->line2; // Do it once, also with an invalid range. + } else { lnum = eap->line1; - for (; lnum <= eap->line2; ++lnum) { - if (!eap->skip && eap->addr_count > 0) { + } + for (; lnum <= eap->line2; lnum++) { + if (!eap->skip && eap->addr_count > 0) { // -V560 curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -2768,7 +2773,7 @@ void ex_call(exarg_T *eap) } tv_clear(&rettv); - if (doesrange || eap->skip) { + if (doesrange || eap->skip) { // -V560 break; } @@ -2993,14 +2998,13 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit) } if (hi != NULL && !HASHITEM_EMPTY(hi)) { dictitem_T *const di = TV_DICT_HI2DI(hi); - if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) - || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) - || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { + if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING) + || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING) + || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { return FAIL; } - if (d == NULL - || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { + if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { return FAIL; } @@ -4764,7 +4768,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) // Special key, e.g.: "\<C-W>" case '<': - extra = trans_special((const char_u **) &p, STRLEN(p), name, true); + extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true); if (extra != 0) { name += extra; break; @@ -6006,7 +6010,7 @@ char_u *get_expr_name(expand_T *xp, int idx) /// @param[in] name Name of the function. /// /// Returns pointer to the function definition or NULL if not found. -static VimLFuncDef *find_internal_func(const char *const name) +static const VimLFuncDef *find_internal_func(const char *const name) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { size_t len = strlen(name); @@ -6375,7 +6379,7 @@ call_func( } } else { // Find the function name in the table, call its implementation. - VimLFuncDef *const fdef = find_internal_func((const char *)fname); + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); if (fdef != NULL) { if (argcount < fdef->min_argc) { error = ERROR_TOOFEW; @@ -6509,7 +6513,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; Object result = fn(VIML_INTERNAL_CALL, args, &err); - if (err.set) { + if (ERROR_SET(&err)) { nvim_err_writeln(cstr_as_string(err.msg)); goto end; } @@ -6521,6 +6525,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) end: api_free_array(args); api_free_object(result); + api_clear_error(&err); } /* @@ -6554,10 +6559,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { - const char *const arg_errmsg = _("add() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -7306,18 +7309,14 @@ static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (has_mbyte) { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) { - utf8 = tv_get_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + if (!tv_check_num(&argvars[1])) { + return; } - - rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( - (const char_u *)tv_get_string(&argvars[0])); - } else { - rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]); } + + rettv->vval.v_number = utf_ptr2char( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -8153,7 +8152,6 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const arg_errmsg = N_("extend() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { long before; @@ -8162,13 +8160,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; if (l1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); } else if (l2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)) { + } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -8196,13 +8194,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *const d1 = argvars[0].vval.v_dict; dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); } else if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) { + } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -8350,20 +8348,19 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int todo; char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); const char *const arg_errmsg = (map - ? _("map() argument") - : _("filter() argument")); - const size_t arg_errmsg_len = strlen(arg_errmsg); + ? N_("map() argument") + : N_("filter() argument")); int save_did_emsg; int idx = 0; if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len))) { + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len))) { + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else { @@ -8396,8 +8393,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) di = TV_DICT_HI2DI(hi); if (map - && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len) - || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) { + && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } @@ -8408,8 +8405,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) break; } if (!map && rem) { - if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) - || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { + if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { break; } tv_dict_item_remove(d, di); @@ -8422,7 +8419,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) for (li = l->lv_first; li != NULL; li = nli) { if (map - && tv_check_lock(li->li_tv.v_lock, arg_errmsg, arg_errmsg_len)) { + && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) { break; } nli = li->li_next; @@ -8667,7 +8664,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *r; int len; char *txt; - long count; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -8695,8 +8691,8 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) s = skipwhite(s + 1); } } - count = (long)(foldend - foldstart + 1); - txt = _("+-%s%3ld lines: "); + unsigned long count = (unsigned long)(foldend - foldstart + 1); + txt = ngettext("+-%s%3ld line: ", "+-%s%3ld lines: ", count); r = xmalloc(STRLEN(txt) + STRLEN(dashes) // for %s + 20 // for %3ld @@ -8716,7 +8712,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *text; - char_u buf[51]; + char_u buf[FOLD_TEXT_LEN]; foldinfo_T foldinfo; int fold_count; @@ -8982,13 +8978,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); const char *const n = (const char *)partial_name(pt); - if (n == NULL) { - rettv->vval.v_string = NULL; - } else { - rettv->vval.v_string = (char_u *)xstrdup(n); - if (rettv->v_type == VAR_FUNC) { - func_ref(rettv->vval.v_string); - } + assert(n != NULL); + rettv->vval.v_string = (char_u *)xstrdup(n); + if (rettv->v_type == VAR_FUNC) { + func_ref(rettv->vval.v_string); } } else if (strcmp(what, "dict") == 0) { rettv->v_type = VAR_DICT; @@ -11161,13 +11154,12 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { list_T *l; bool error = false; - const char *const arg_errmsg = _("insert() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + && !tv_check_lock(l->lv_lock, N_("insert() argument"), + TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -12560,7 +12552,7 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int prot = 0755; + int prot = 0755; // -V536 rettv->vval.v_number = FAIL; if (check_restricted() || check_secure()) @@ -12598,59 +12590,18 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "mode()" function - */ +/// "mode()" function static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[3]; + char *mode = get_mode(); - buf[1] = NUL; - buf[2] = NUL; - - if (VIsual_active) { - if (VIsual_select) - buf[0] = VIsual_mode + 's' - 'v'; - else - buf[0] = VIsual_mode; - } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE - || State == CONFIRM) { - buf[0] = 'r'; - if (State == ASKMORE) - buf[1] = 'm'; - else if (State == CONFIRM) - buf[1] = '?'; - } else if (State == EXTERNCMD) - buf[0] = '!'; - else if (State & INSERT) { - if (State & VREPLACE_FLAG) { - buf[0] = 'R'; - buf[1] = 'v'; - } else if (State & REPLACE_FLAG) - buf[0] = 'R'; - else - buf[0] = 'i'; - } else if (State & CMDLINE) { - buf[0] = 'c'; - if (exmode_active) - buf[1] = 'v'; - } else if (exmode_active) { - buf[0] = 'c'; - buf[1] = 'e'; - } else if (State & TERM_FOCUS) { - buf[0] = 't'; - } else { - buf[0] = 'n'; - if (finish_op) - buf[1] = 'o'; + // Clear out the minor mode when the argument is not a non-zero number or + // non-empty string. + if (!non_zero_arg(&argvars[0])) { + mode[1] = NUL; } - /* Clear out the minor mode when the argument is not a non-zero number or - * non-empty string. */ - if (!non_zero_arg(&argvars[0])) - buf[1] = NUL; - - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)mode; rettv->v_type = VAR_STRING; } @@ -12785,25 +12736,32 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - - if (has_mbyte) { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) { - utf8 = tv_get_number_chk(&argvars[1], NULL); - } - if (utf8) { - buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; - } else { - buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + if (argvars[1].v_type != VAR_UNKNOWN) { + if (!tv_check_num(&argvars[1])) { + return; } - } else { - buf[0] = (char_u)tv_get_number(&argvars[0]); - buf[1] = NUL; } + + bool error = false; + const varnumber_T num = tv_get_number_chk(&argvars[0], &error); + if (error) { + return; + } + if (num < 0) { + emsgf(_("E5070: Character number must not be less than zero")); + return; + } + if (num > INT_MAX) { + emsgf(_("E5071: Character number must not be greater than INT_MAX (%i)"), + INT_MAX); + return; + } + + char buf[MB_MAXBYTES]; + const int len = utf_char2bytes((int)num, (char_u *)buf); + rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = xmemdupz(buf, (size_t)len); } /* @@ -13220,21 +13178,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) long end; dict_T *d; dictitem_T *di; - const char *const arg_errmsg = _("remove() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); + const char *const arg_errmsg = N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { + && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) - && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { + } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { *rettv = di->di_tv; di->di_tv = TV_INITIAL_VALUE; tv_dict_item_remove(d, di); @@ -13247,7 +13204,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -13518,14 +13475,12 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *const arg_errmsg = _("reverse() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); - list_T *l; if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + && !tv_check_lock(l->lv_lock, N_("reverse() argument"), + TV_TRANSLATE)) { listitem_T *li = l->lv_last; l->lv_first = l->lv_last = NULL; l->lv_len = 0; @@ -13803,7 +13758,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) restore_funccal(save_funccalp); } - if (err.set) { + if (ERROR_SET(&err)) { nvim_err_writeln(cstr_as_string(err.msg)); goto end; } @@ -13814,6 +13769,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) end: api_free_object(result); + api_clear_error(&err); } // "rpcstart()" function (DEPRECATED) @@ -14321,7 +14277,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (argvars[0].v_type == VAR_UNKNOWN || argvars[0].v_type != VAR_STRING) { + if (argvars[0].v_type != VAR_STRING) { EMSG(_(e_invarg)); return; } @@ -14345,7 +14301,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *const buf = get_buf_tv(&argvars[0], false); typval_T *varp = &argvars[2]; - if (buf != NULL && varname != NULL && varp != NULL) { + if (buf != NULL && varname != NULL) { if (*varname == '&') { long numval; bool error = false; @@ -14889,7 +14845,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const varname = tv_get_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; - if (varname != NULL && varp != NULL && tp != NULL) { + if (varname != NULL && tp != NULL) { tabpage_T *const save_curtab = curtab; goto_tabpage_tp(tp, false, false); @@ -15217,16 +15173,14 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) sortinfo = &info; const char *const arg_errmsg = (sort - ? _("sort() argument") - : _("uniq() argument")); - const size_t arg_errmsg_len = strlen(arg_errmsg); + ? N_("sort() argument") + : N_("uniq() argument")); if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { l = argvars[0].vval.v_list; - if (l == NULL - || tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { goto theend; } rettv->vval.v_list = l; @@ -16545,11 +16499,13 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) curbuf->b_p_swf = false; (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} - Error err; + Error err = ERROR_INIT; dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), INTEGER_OBJ(rettv->vval.v_number), false, false, &err); + api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); + api_clear_error(&err); Terminal *term = terminal_open(topts); data->term = term; @@ -16666,6 +16622,77 @@ static bool set_ref_in_callback(Callback *callback, int copyID, return false; } +static void add_timer_info(typval_T *rettv, timer_T *timer) +{ + list_T *list = rettv->vval.v_list; + dict_T *dict = tv_dict_alloc(); + + tv_list_append_dict(list, dict); + tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id); + tv_dict_add_nr(dict, S_LEN("time"), timer->timeout); + tv_dict_add_nr(dict, S_LEN("paused"), timer->paused); + + tv_dict_add_nr(dict, S_LEN("repeat"), + (timer->repeat_count < 0 ? -1 : timer->repeat_count)); + + dictitem_T *di = tv_dict_item_alloc("callback"); + if (tv_dict_add(dict, di) == FAIL) { + xfree(di); + return; + } + + if (timer->callback.type == kCallbackPartial) { + di->di_tv.v_type = VAR_PARTIAL; + di->di_tv.vval.v_partial = timer->callback.data.partial; + timer->callback.data.partial->pt_refcount++; + } else if (timer->callback.type == kCallbackFuncref) { + di->di_tv.v_type = VAR_FUNC; + di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref); + } + di->di_tv.v_lock = 0; +} + +static void add_timer_info_all(typval_T *rettv) +{ + timer_T *timer; + map_foreach_value(timers, timer, { + if (!timer->stopped) { + add_timer_info(rettv, timer); + } + }) +} + +/// "timer_info([timer])" function +static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv); + if (argvars[0].v_type != VAR_UNKNOWN) { + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_number_exp)); + return; + } + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); + if (timer != NULL && !timer->stopped) { + add_timer_info(rettv, timer); + } + } else { + add_timer_info_all(rettv); + } +} + +/// "timer_pause(timer, paused)" function +static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_number_exp)); + return; + } + int paused = (bool)tv_get_number(&argvars[1]); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); + if (timer != NULL) { + timer->paused = paused; + } +} /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -16700,6 +16727,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer = xmalloc(sizeof *timer); timer->refcount = 1; timer->stopped = false; + timer->paused = false; timer->repeat_count = repeat; timer->timeout = timeout; timer->timer_id = last_timer_id++; @@ -16709,8 +16737,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->tw.events = multiqueue_new_child(main_loop.events); // if main loop is blocked, don't queue up multiple events timer->tw.blockable = true; - time_watcher_start(&timer->tw, timer_due_cb, timeout, - timeout * (repeat != 1)); + time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout); pmap_put(uint64_t)(timers, timer->timer_id, timer); rettv->vval.v_number = timer->timer_id; @@ -16734,13 +16761,19 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer_stop(timer); } +static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) +{ + timer_stop_all(); +} + // invoked on the main loop static void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; - if (timer->stopped) { + if (timer->stopped || timer->paused) { return; } + timer->refcount++; // if repeat was negative repeat forever if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { @@ -16793,7 +16826,7 @@ static void timer_decref(timer_T *timer) } } -void timer_teardown(void) +static void timer_stop_all(void) { timer_T *timer; map_foreach_value(timers, timer, { @@ -16801,6 +16834,11 @@ void timer_teardown(void) }) } +void timer_teardown(void) +{ + timer_stop_all(); +} + /* * "tolower(string)" function */ @@ -18891,24 +18929,43 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, /// /// @param[in] flags di_flags attribute value. /// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. /// /// @return True if variable is read-only: either always or in sandbox when /// sandbox is enabled, false otherwise. -bool var_check_ro(const int flags, const char *const name, - const size_t name_len) +bool var_check_ro(const int flags, const char *name, + size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { + const char *error_message = NULL; if (flags & DI_FLAGS_RO) { - emsgf(_(e_readonlyvar), (int)name_len, name); - return true; + error_message = N_(e_readonlyvar); + } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { + error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); } - if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - emsgf(_("E794: Cannot set variable in the sandbox: \"%.*s\""), - (int)name_len, name); - return true; + + if (error_message == NULL) { + return false; } - return false; + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + + emsgf(_(error_message), (int)name_len, name); + + return true; } /// Check whether variable is fixed (DI_FLAGS_FIX) @@ -18917,14 +18974,29 @@ bool var_check_ro(const int flags, const char *const name, /// /// @param[in] flags di_flags attribute value. /// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. /// /// @return True if variable is fixed, false otherwise. -static bool var_check_fixed(const int flags, const char *const name, - const size_t name_len) +static bool var_check_fixed(const int flags, const char *name, + size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (flags & DI_FLAGS_FIX) { + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } emsgf(_("E795: Cannot delete variable %.*s"), (int)name_len, name); return true; } @@ -19395,8 +19467,9 @@ void ex_function(exarg_T *eap) * interrupt, or an exception. */ if (!aborting()) { - if (!eap->skip && fudi.fd_newkey != NULL) + if (fudi.fd_newkey != NULL) { EMSG2(_(e_dictkey), fudi.fd_newkey); + } xfree(fudi.fd_newkey); return; } else @@ -19731,12 +19804,12 @@ void ex_function(exarg_T *eap) } if (fudi.fd_di == NULL) { if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, - STRLEN(eap->arg))) { + TV_CSTRING)) { // Can't add a function to a locked dictionary goto erret; } } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, - STRLEN(eap->arg))) { + TV_CSTRING)) { // Can't change an existing function if it is locked goto erret; } @@ -20980,8 +21053,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, char *s = tofree; emsg_off--; if (s != NULL) { + char buf[MSG_BUF_LEN]; if (vim_strsize((char_u *)s) > MSG_BUF_CLEN) { - char buf[MSG_BUF_LEN]; trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN, sizeof(buf)); s = buf; @@ -21721,52 +21794,6 @@ void last_set_msg(scid_T scriptID) } } -/* - * List v:oldfiles in a nice way. - */ -void ex_oldfiles(exarg_T *eap) -{ - list_T *l = get_vim_var_list(VV_OLDFILES); - listitem_T *li; - long nr = 0; - - if (l == NULL) - msg((char_u *)_("No old files")); - else { - msg_start(); - msg_scroll = TRUE; - for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { - msg_outnum(++nr); - MSG_PUTS(": "); - msg_outtrans((char_u *)tv_get_string(&li->li_tv)); - msg_clr_eos(); - msg_putchar('\n'); - ui_flush(); /* output one line at a time */ - os_breakcheck(); - } - /* Assume "got_int" was set to truncate the listing. */ - got_int = FALSE; - - // File selection prompt on ":browse oldfiles" - if (cmdmod.browse) { - quit_more = false; - nr = prompt_for_number(false); - msg_starthere(); - if (nr > 0 && nr <= l->lv_len) { - const char *const p = tv_list_find_str(l, nr - 1); - if (p == NULL) { - return; - } - char *const s = (char *)expand_env_save((char_u *)p); - eap->arg = (char_u *)s; - eap->cmdidx = CMD_edit; - do_exedit(eap, NULL); - xfree(s); - } - } - } -} - // reset v:option_new, v:option_old and v:option_type void reset_v_option_vars(void) { |