aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c358
1 files changed, 261 insertions, 97 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index e6880f58e7..9c7af0a87d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -344,11 +344,11 @@ static struct vimvar {
VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
- VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT),
+ VV(VV_ERRMSG, "errmsg", VAR_STRING, 0),
VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
- VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO),
- VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT),
+ VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
+ VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
@@ -496,7 +496,7 @@ typedef enum {
#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
valid character */
-static uint64_t last_timer_id = 0;
+static uint64_t last_timer_id = 1;
static PMap(uint64_t) *timers = NULL;
/// Dummy va_list for passing to vim_snprintf
@@ -1186,7 +1186,7 @@ int call_vim_function(
const char_u *func,
int argc,
const char_u *const *const argv,
- int safe, // use the sandbox
+ bool safe, // use the sandbox
int str_arg_only, // all arguments are strings
typval_T *rettv
)
@@ -1210,8 +1210,12 @@ int call_vim_function(
if (str_arg_only) {
len = 0;
} else {
- // Recognize a number argument, the others must be strings.
+ // Recognize a number argument, the others must be strings. A dash
+ // is a string too.
vim_str2nr(argv[i], NULL, &len, STR2NR_ALL, &n, NULL, 0);
+ if (len == 1 && *argv[i] == '-') {
+ len = 0;
+ }
}
if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER;
@@ -1276,9 +1280,9 @@ varnumber_T call_func_retnr(char_u *func, int argc,
///
/// @return [allocated] NULL when calling function fails, allocated string
/// otherwise.
-char *call_func_retstr(const char *const func, const int argc,
- const char_u *const *const argv,
- const bool safe)
+char *call_func_retstr(const char *const func, int argc,
+ const char_u *const *argv,
+ bool safe)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
@@ -1302,8 +1306,8 @@ char *call_func_retstr(const char *const func, const int argc,
///
/// @return [allocated] NULL when calling function fails or return tv is not a
/// List, allocated List otherwise.
-void *call_func_retlist(char_u *func, int argc, const char_u *const *const argv,
- int safe)
+void *call_func_retlist(char_u *func, int argc, const char_u *const *argv,
+ bool safe)
{
typval_T rettv;
@@ -1908,6 +1912,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
if ((opt_type == 1 && *op == '.')
|| (opt_type == 0 && *op != '.')) {
EMSG2(_(e_letwrong), op);
+ s = NULL; // don't set the value
} else {
if (opt_type == 1) { // number
if (*op == '+') {
@@ -1938,7 +1943,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
emsgf(_(e_letwrong), op);
} else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) {
- emsgf(_(e_letunexp));
+ EMSG(_(e_letunexp));
} else {
char_u *s;
@@ -2072,7 +2077,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
return p;
}
- v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD);
+ // Only pass &ht when we would write to the variable, it prevents autoload
+ // as well.
+ v = find_var(lp->ll_name, lp->ll_name_len,
+ (flags & GLV_READ_ONLY) ? NULL : &ht,
+ flags & GLV_NO_AUTOLOAD);
if (v == NULL && !quiet) {
emsgf(_("E121: Undefined variable: %.*s"),
(int)lp->ll_name_len, lp->ll_name);
@@ -2143,7 +2152,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (rettv != NULL && (rettv->v_type != VAR_LIST
|| rettv->vval.v_list == NULL)) {
if (!quiet) {
- emsgf(_("E709: [:] requires a List value"));
+ EMSG(_("E709: [:] requires a List value"));
}
tv_clear(&var1);
return NULL;
@@ -2170,7 +2179,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,
if (*p != ']') {
if (!quiet) {
- emsgf(_(e_missbrac));
+ EMSG(_(e_missbrac));
}
tv_clear(&var1);
tv_clear(&var2);
@@ -2719,6 +2728,12 @@ void ex_call(exarg_T *eap)
lnum = eap->line1;
for (; lnum <= eap->line2; lnum++) {
if (eap->addr_count > 0) { // -V560
+ if (lnum > curbuf->b_ml.ml_line_count) {
+ // If the function deleted lines or switched to another buffer
+ // the line number may become invalid.
+ EMSG(_(e_invrange));
+ break;
+ }
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
@@ -2809,6 +2824,18 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
lval_T lv;
do {
+ if (*arg == '$') {
+ const char *name = (char *)++arg;
+
+ if (get_env_len((const char_u **)&arg) == 0) {
+ EMSG2(_(e_invarg2), name - 1);
+ return;
+ }
+ os_unsetenv(name);
+ arg = skipwhite(arg);
+ continue;
+ }
+
// Parse the name and find the end.
char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true,
eap->skip || error,
@@ -3304,7 +3331,7 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate)
* Check for the ":".
*/
if ((*arg)[0] != ':') {
- emsgf(_("E109: Missing ':' after '?'"));
+ EMSG(_("E109: Missing ':' after '?'"));
if (evaluate && result) {
tv_clear(rettv);
}
@@ -4347,7 +4374,7 @@ eval_index(
if (evaluate) {
return FAIL;
}
- // fallthrough
+ FALLTHROUGH;
}
case VAR_STRING:
case VAR_NUMBER:
@@ -4412,7 +4439,7 @@ eval_index(
/* Check for the ']'. */
if (**arg != ']') {
if (verbose) {
- emsgf(_(e_missbrac));
+ EMSG(_(e_missbrac));
}
tv_clear(&var1);
if (range) {
@@ -4524,7 +4551,7 @@ eval_index(
case VAR_DICT: {
if (range) {
if (verbose) {
- emsgf(_(e_dictrange));
+ EMSG(_(e_dictrange));
}
if (len == -1) {
tv_clear(&var1);
@@ -4714,10 +4741,11 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
++p;
/* For "\u" store the number according to
* 'encoding'. */
- if (c != 'X')
- name += (*mb_char2bytes)(nr, name);
- else
+ if (c != 'X') {
+ name += utf_char2bytes(nr, name);
+ } else {
*name++ = nr;
+ }
}
break;
@@ -4745,7 +4773,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
name += extra;
break;
}
- // FALLTHROUGH
+ FALLTHROUGH;
default: MB_COPY_CHAR(p, name);
break;
@@ -7210,7 +7238,7 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
} else {
rettv->vval.v_number = (varnumber_T)ml_find_line_or_offset(curbuf, 0,
- &boff);
+ &boff, false);
}
}
@@ -7592,9 +7620,38 @@ static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long n = 0;
- int ic = FALSE;
+ int ic = 0;
+ bool error = false;
- if (argvars[0].v_type == VAR_LIST) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ ic = tv_get_number_chk(&argvars[2], &error);
+ }
+
+ if (argvars[0].v_type == VAR_STRING) {
+ const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]);
+ const char_u *p = argvars[0].vval.v_string;
+
+ if (!error && expr != NULL && *expr != NUL && p != NULL) {
+ if (ic) {
+ const size_t len = STRLEN(expr);
+
+ while (*p != NUL) {
+ if (mb_strnicmp(p, expr, len) == 0) {
+ n++;
+ p += len;
+ } else {
+ MB_PTR_ADV(p);
+ }
+ }
+ } else {
+ char_u *next;
+ while ((next = (char_u *)strstr((char *)p, (char *)expr)) != NULL) {
+ n++;
+ p = next + STRLEN(expr);
+ }
+ }
+ }
+ } else if (argvars[0].v_type == VAR_LIST) {
listitem_T *li;
list_T *l;
long idx;
@@ -7602,9 +7659,6 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if ((l = argvars[0].vval.v_list) != NULL) {
li = tv_list_first(l);
if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
-
- ic = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
idx = tv_get_number_chk(&argvars[3], &error);
if (!error) {
@@ -7630,10 +7684,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hashitem_T *hi;
if ((d = argvars[0].vval.v_dict) != NULL) {
- bool error = false;
-
if (argvars[2].v_type != VAR_UNKNOWN) {
- ic = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
EMSG(_(e_invarg));
}
@@ -7649,8 +7700,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- } else
+ } else {
EMSG2(_(e_listdictarg), "count()");
+ }
rettv->vval.v_number = n;
}
@@ -7749,7 +7801,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
noref = tv_get_number_chk(&argvars[1], NULL);
}
if (noref < 0 || noref > 1) {
- emsgf(_(e_invarg));
+ EMSG(_(e_invarg));
} else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? get_copyID()
@@ -7789,7 +7841,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// delete a directory recursively
rettv->vval.v_number = delete_recursive(name);
} else {
- EMSG2(_(e_invexpr2), flags);
+ emsgf(_(e_invexpr2), flags);
}
}
@@ -8844,9 +8896,14 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u buf[FOLD_TEXT_LEN];
foldinfo_T foldinfo;
int fold_count;
+ static bool entered = false;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ if (entered) {
+ return; // reject recursive use
+ }
+ entered = true;
linenr_T lnum = tv_get_lnum(argvars);
// Treat illegal types and illegal string values for {lnum} the same.
if (lnum < 0) {
@@ -8860,6 +8917,8 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->vval.v_string = text;
}
+
+ entered = false;
}
/*
@@ -8910,7 +8969,7 @@ static void common_function(typval_T *argvars, typval_T *rettv,
} else if (trans_name != NULL
&& (is_funcref ? find_func(trans_name) == NULL
: !translated_function_exists((const char *)trans_name))) {
- EMSG2(_("E700: Unknown function: %s"), s);
+ emsgf(_("E700: Unknown function: %s"), s);
} else {
int dict_idx = 0;
int arg_idx = 0;
@@ -9449,10 +9508,9 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
temp[i++] = K_SPECIAL;
temp[i++] = K_SECOND(n);
temp[i++] = K_THIRD(n);
- } else if (has_mbyte)
- i += (*mb_char2bytes)(n, temp + i);
- else
- temp[i++] = n;
+ } else {
+ i += utf_char2bytes(n, temp + i);
+ }
temp[i++] = NUL;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave(temp);
@@ -9469,6 +9527,9 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* Find the window at the mouse coordinates and compute the
* text position. */
win = mouse_find_win(&row, &col);
+ if (win == NULL) {
+ return;
+ }
(void)mouse_comp_pos(win, &row, &col, &lnum);
for (wp = firstwin; wp != win; wp = wp->w_next)
++winnr;
@@ -9706,14 +9767,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (from) {
break;
}
- // fallthrough
+ FALLTHROUGH;
case kCdScopeTab:
assert(tp);
from = tp->tp_localdir;
if (from) {
break;
}
- // fallthrough
+ FALLTHROUGH;
case kCdScopeGlobal:
if (globaldir) { // `globaldir` is not always set.
from = globaldir;
@@ -9847,7 +9908,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
# endif
# ifdef S_ISSOCK
else if (S_ISSOCK(mode))
- t = "fifo";
+ t = "socket";
# endif
else
t = "other";
@@ -9912,7 +9973,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
if (what_arg->v_type == VAR_UNKNOWN) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
- (void)get_errorlist(wp, -1, rettv->vval.v_list);
+ (void)get_errorlist(NULL, wp, -1, rettv->vval.v_list);
}
} else {
tv_dict_alloc_ret(rettv);
@@ -9979,7 +10040,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (cur->conceal_char) {
char buf[MB_MAXBYTES + 1];
- buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL;
+ buf[utf_char2bytes((int)cur->conceal_char, (char_u *)buf)] = NUL;
tv_dict_add_str(dict, S_LEN("conceal"), buf);
}
@@ -10183,32 +10244,34 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *oldcurwin;
- tabpage_T *tp, *oldtabpage;
- dictitem_T *v;
+ tabpage_T *oldtabpage;
bool done = false;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
const char *const varname = tv_get_string_chk(&argvars[1]);
- tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
+ tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
if (tp != NULL && varname != NULL) {
// Set tp to be our tabpage, temporarily. Also set the window to the
// first window in the tabpage, otherwise the window is not valid.
- win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin;
+ win_T *const window = tp == curtab || tp->tp_firstwin == NULL
+ ? firstwin
+ : tp->tp_firstwin;
if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) {
// look up the variable
// Let gettabvar({nr}, "") return the "t:" dictionary.
- v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't',
- varname, strlen(varname), false);
+ const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't',
+ varname, strlen(varname),
+ false);
if (v != NULL) {
tv_copy(&v->di_tv, rettv);
done = true;
}
}
- /* restore previous notion of curwin */
- restore_win(oldcurwin, oldtabpage, TRUE);
+ // restore previous notion of curwin
+ restore_win(oldcurwin, oldtabpage, true);
}
if (!done && argvars[2].v_type != VAR_UNKNOWN) {
@@ -10233,8 +10296,10 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
+ tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow);
tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
+ tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol);
tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
tv_dict_add_nr(dict, S_LEN("loclist"),
@@ -10281,6 +10346,15 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// "win_screenpos()" function
+static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ const win_T *const wp = find_win_by_nr(&argvars[0], NULL);
+ tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
+ tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
+}
+
/*
* "getwinposx()" function
*/
@@ -10565,6 +10639,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef HAVE_ACL
"acl",
#endif
+ "autochdir",
"arabic",
"autocmd",
"browsefilter",
@@ -11121,7 +11196,7 @@ void get_user_input(const typval_T *const argvars,
char def[1] = { 0 };
if (argvars[0].v_type == VAR_DICT) {
if (argvars[1].v_type != VAR_UNKNOWN) {
- emsgf(_("E5050: {opts} must be the only argument"));
+ EMSG(_("E5050: {opts} must be the only argument"));
return;
}
dict_T *const dict = argvars[0].vval.v_dict;
@@ -11190,7 +11265,7 @@ void get_user_input(const typval_T *const argvars,
}
}
- int cmd_silent_save = cmd_silent;
+ const bool cmd_silent_save = cmd_silent;
cmd_silent = false; // Want to see the prompt.
// Only the part of the message after the last NL is considered as
@@ -11430,7 +11505,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
const DictListType what)
{
if (tv->v_type != VAR_DICT) {
- emsgf(_(e_dictreq));
+ EMSG(_(e_dictreq));
return;
}
if (tv->vval.v_dict == NULL) {
@@ -11649,7 +11724,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir((char_u *)cwd)) {
+ if (!os_isdir_executable((const char *)cwd)) {
EMSG2(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -11860,7 +11935,7 @@ static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (json_decode_string(s, len, rettv) == FAIL) {
- emsgf(_("E474: Failed to parse %.*s"), (int) len, s);
+ emsgf(_("E474: Failed to parse %.*s"), (int)len, s);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
}
@@ -12015,7 +12090,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) {
rettv->vval.v_number = -1;
} else {
- rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL);
+ rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL, false);
}
if (rettv->vval.v_number >= 0) {
rettv->vval.v_number++;
@@ -12093,8 +12168,12 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
if (!get_dict) {
// Return a string.
if (rhs != NULL) {
- rettv->vval.v_string = (char_u *)str2special_save(
- (const char *)rhs, false, false);
+ if (*rhs == NUL) {
+ rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
+ } else {
+ rettv->vval.v_string = (char_u *)str2special_save(
+ (char *)rhs, false, false);
+ }
}
} else {
@@ -12198,7 +12277,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
long start = 0;
long nth = 1;
colnr_T startcol = 0;
- int match = 0;
+ bool match = false;
list_T *l = NULL;
listitem_T *li = NULL;
long idx = 0;
@@ -12296,7 +12375,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
for (;; ) {
if (l != NULL) {
if (li == NULL) {
- match = FALSE;
+ match = false;
break;
}
xfree(tofree);
@@ -12322,7 +12401,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
startcol = (colnr_T)(regmatch.startp[0]
+ (*mb_ptr2len)(regmatch.startp[0]) - str);
if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) {
- match = FALSE;
+ match = false;
break;
}
}
@@ -12395,13 +12474,13 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
vim_regfree(regmatch.regprog);
}
- if (type == kSomeMatchStrPos && l == NULL) {
+theend:
+ if (type == kSomeMatchStrPos && l == NULL && rettv->vval.v_list != NULL) {
// matchstrpos() without a list: drop the second item
list_T *const ret_l = rettv->vval.v_list;
tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l)));
}
-theend:
xfree(tofree);
p_cpo = save_cpo;
}
@@ -12846,7 +12925,7 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
if (num < 0) {
- emsgf(_("E5070: Character number must not be less than zero"));
+ EMSG(_("E5070: Character number must not be less than zero"));
return;
}
if (num > INT_MAX) {
@@ -12992,9 +13071,9 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; // Type error; errmsg already given.
}
if (stride == 0) {
- emsgf(_("E726: Stride is zero"));
+ EMSG(_("E726: Stride is zero"));
} else if (stride > 0 ? end + 1 < start : end - 1 > start) {
- emsgf(_("E727: Start past end"));
+ EMSG(_("E727: Start past end"));
} else {
tv_list_alloc_ret(rettv, (end - start) / stride);
for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
@@ -13331,7 +13410,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (li == NULL) { // Didn't find "item2" after "item".
- emsgf(_(e_invrange));
+ EMSG(_(e_invrange));
} else {
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
cnt);
@@ -14603,6 +14682,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
int action = ' ';
+ static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *d = NULL;
@@ -14610,6 +14690,9 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
if (list_arg->v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
+ } else if (recursive != 0) {
+ EMSG(_(e_au_recursive));
+ return;
}
typval_T *action_arg = &args[1];
@@ -14642,7 +14725,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
} else if (title_arg->v_type == VAR_DICT) {
d = title_arg->vval.v_dict;
} else {
- emsgf(_(e_dictreq));
+ EMSG(_(e_dictreq));
return;
}
@@ -14651,10 +14734,12 @@ skip_args:
title = (wp ? "setloclist()" : "setqflist()");
}
+ recursive++;
list_T *const l = list_arg->vval.v_list;
if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
rettv->vval.v_number = 0;
}
+ recursive--;
}
/*
@@ -15869,7 +15954,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
while (charidx >= 0 && byteidx < len) {
if (charidx == 0) {
- rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx);
+ rettv->vval.v_number = utf_ptr2char((const char_u *)str + byteidx);
break;
}
charidx--;
@@ -16138,7 +16223,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (no < 0 || no >= NSUBEXP) {
- EMSGN(_("E935: invalid submatch number: %d"), no);
+ emsgf(_("E935: invalid submatch number: %d"), no);
return;
}
int retList = 0;
@@ -16377,9 +16462,13 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
return list;
}
+// os_system wrapper. Handles 'verbose', :profile, and v:shell_error.
static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
bool retlist)
{
+ proftime_T wait_time;
+ bool profiling = do_profiling == PROF_YES;
+
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -16406,11 +16495,28 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
return; // Already did emsg.
}
+ if (p_verbose > 3) {
+ char *cmdstr = shell_argv_to_str(argv);
+ verbose_enter_scroll();
+ smsg(_("Executing command: \"%s\""), cmdstr);
+ msg_puts("\n\n");
+ verbose_leave_scroll();
+ xfree(cmdstr);
+ }
+
+ if (profiling) {
+ prof_child_enter(&wait_time);
+ }
+
// execute the command
size_t nread = 0;
char *res = NULL;
int status = os_system(argv, input, input_len, &res, &nread);
+ if (profiling) {
+ prof_child_exit(&wait_time);
+ }
+
xfree(input);
set_vim_var_nr(VV_SHELL_ERROR, (long) status);
@@ -16663,7 +16769,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir((const char_u *)cwd)) {
+ if (!os_isdir_executable((const char *)cwd)) {
EMSG2(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -17168,6 +17274,69 @@ error:
return;
}
+// "trim({expr})" function
+static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char_u *head = (const char_u *)tv_get_string_buf_chk(&argvars[0], buf1);
+ const char_u *mask = NULL;
+ const char_u *tail;
+ const char_u *prev;
+ const char_u *p;
+ int c1;
+
+ rettv->v_type = VAR_STRING;
+ if (head == NULL) {
+ rettv->vval.v_string = NULL;
+ return;
+ }
+
+ if (argvars[1].v_type == VAR_STRING) {
+ mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2);
+ }
+
+ while (*head != NUL) {
+ c1 = PTR2CHAR(head);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == PTR2CHAR(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ MB_PTR_ADV(head);
+ }
+
+ for (tail = head + STRLEN(head); tail > head; tail = prev) {
+ prev = tail;
+ MB_PTR_BACK(head, prev);
+ c1 = PTR2CHAR(prev);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == PTR2CHAR(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ }
+ rettv->vval.v_string = vim_strnsave(head, (int)(tail - head));
+}
+
/*
* "type(expr)" function
*/
@@ -17419,7 +17588,7 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_DICT
|| (dict = argvars[0].vval.v_dict) == NULL) {
- emsgf(_(e_invarg));
+ EMSG(_(e_invarg));
} else {
dictitem_T *di;
if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
@@ -18186,9 +18355,9 @@ varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE
return vimvars[idx].vv_nr;
}
-/*
- * Get string v: variable value. Uses a static buffer, can only be used once.
- */
+// Get string v: variable value. Uses a static buffer, can only be used once.
+// If the String variable has never been set, return an empty string.
+// Never returns NULL;
char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
return (char_u *)tv_get_string(&vimvars[idx].vv_tv);
@@ -18217,12 +18386,7 @@ void set_vim_var_char(int c)
{
char buf[MB_MAXBYTES + 1];
- if (has_mbyte) {
- buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL;
- } else {
- buf[0] = c;
- buf[1] = NUL;
- }
+ buf[utf_char2bytes(c, (char_u *)buf)] = NUL;
set_vim_var_string(VV_CHAR, buf, -1);
}
@@ -19210,7 +19374,7 @@ static bool var_check_fixed(const int flags, const char *name,
} else if (name_len == TV_CSTRING) {
name_len = strlen(name);
}
- emsgf(_("E795: Cannot delete variable %.*s"), (int)name_len, name);
+ EMSG3(_("E795: Cannot delete variable %.*s"), (int)name_len, name);
return true;
}
return false;
@@ -19680,7 +19844,7 @@ void ex_function(exarg_T *eap)
// s:func script-local function name
// g:func global function name, same as "func"
p = eap->arg;
- name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
+ name = trans_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
/*
@@ -19819,7 +19983,7 @@ void ex_function(exarg_T *eap)
if (*p == '\n') {
line_arg = p + 1;
} else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) {
- emsgf(_(e_trailing));
+ EMSG(_(e_trailing));
}
/*
@@ -20224,7 +20388,7 @@ trans_function_name(
}
// Note that TFN_ flags use the same values as GLV_ flags.
- end = get_lval((char_u *)start, NULL, &lv, false, skip, flags,
+ end = get_lval((char_u *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
lead > 2 ? 0 : FNE_CHECK_START);
if (end == start) {
if (!skip)
@@ -21640,15 +21804,15 @@ void ex_return(exarg_T *eap)
} else {
tv_clear(&rettv);
}
- }
- /* It's safer to return also on error. */
- else if (!eap->skip) {
- /*
- * Return unless the expression evaluation has been cancelled due to an
- * aborting error, an interrupt, or an exception.
- */
- if (!aborting())
- returning = do_return(eap, FALSE, TRUE, NULL);
+ } else if (!eap->skip) { // It's safer to return also on error.
+ // In return statement, cause_abort should be force_abort.
+ update_force_abort();
+
+ // Return unless the expression evaluation has been cancelled due to an
+ // aborting error, an interrupt, or an exception.
+ if (!aborting()) {
+ returning = do_return(eap, false, true, NULL);
+ }
}
/* When skipping or the return gets pending, advance to the next command