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.c461
1 files changed, 270 insertions, 191 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 7ac51d7bd7..a1c5f958d1 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1113,19 +1113,17 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd)
}
-/*
- * Call some vimL function and return the result in "*rettv".
- * Uses argv[argc] for the function arguments. Only Number and String
- * arguments are currently supported.
- * Returns OK or FAIL.
- */
-int
-call_vim_function (
+// Call some vimL function and return the result in "*rettv".
+// Uses argv[argc] for the function arguments. Only Number and String
+// arguments are currently supported.
+//
+// Return OK or FAIL.
+int call_vim_function(
char_u *func,
int argc,
char_u **argv,
- int safe, /* use the sandbox */
- int str_arg_only, /* all arguments are strings */
+ int safe, // use the sandbox
+ int str_arg_only, // all arguments are strings
typval_T *rettv
)
{
@@ -1138,18 +1136,19 @@ call_vim_function (
typval_T *argvars = xmalloc((argc + 1) * sizeof(typval_T));
for (int i = 0; i < argc; i++) {
- /* Pass a NULL or empty argument as an empty string */
+ // Pass a NULL or empty argument as an empty string
if (argv[i] == NULL || *argv[i] == NUL) {
argvars[i].v_type = VAR_STRING;
argvars[i].vval.v_string = (char_u *)"";
continue;
}
- if (str_arg_only)
+ if (str_arg_only) {
len = 0;
- else
- /* Recognize a number argument, the others must be strings. */
- vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL);
+ } else {
+ // Recognize a number argument, the others must be strings.
+ vim_str2nr(argv[i], NULL, &len, true, true, true, &n, NULL);
+ }
if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n;
@@ -1166,16 +1165,17 @@ call_vim_function (
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &doesrange, TRUE, NULL);
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &doesrange, true, NULL);
if (safe) {
--sandbox;
restore_funccal(save_funccalp);
}
xfree(argvars);
- if (ret == FAIL)
+ if (ret == FAIL) {
clear_tv(rettv);
+ }
return ret;
}
@@ -2890,9 +2890,12 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
else if (do_unlet(lp->ll_name, forceit) == FAIL)
ret = FAIL;
*name_end = cc;
- } else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name))
+ } else if ((lp->ll_list != NULL
+ && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name))
+ || (lp->ll_dict != NULL
+ && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name))) {
return FAIL;
- else if (lp->ll_range) {
+ } else if (lp->ll_range) {
listitem_T *li;
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
@@ -2953,17 +2956,30 @@ int do_unlet(char_u *name, int forceit)
hashtab_T *ht;
hashitem_T *hi;
char_u *varname;
+ dict_T *d;
dictitem_T *di;
dict_T *dict;
ht = find_var_ht_dict(name, &varname, &dict);
if (ht != NULL && *varname != NUL) {
+ if (ht == &globvarht) {
+ d = &globvardict;
+ } else if (current_funccal != NULL
+ && ht == &current_funccal->l_vars.dv_hashtab) {
+ d = &current_funccal->l_vars;
+ } else {
+ di = find_var_in_ht(ht, *name, (char_u *)"", false);
+ d = di->di_tv.vval.v_dict;
+ }
+
hi = hash_find(ht, varname);
if (!HASHITEM_EMPTY(hi)) {
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name)
- || var_check_ro(di->di_flags, name))
+ || var_check_ro(di->di_flags, name)
+ || tv_check_lock(d->dv_lock, name)) {
return FAIL;
+ }
typval_T oldtv;
bool watched = is_watched(dict);
@@ -4025,38 +4041,35 @@ eval6 (
return OK;
}
-/*
- * Handle sixth level expression:
- * number number constant
- * "string" string constant
- * 'string' literal string constant
- * &option-name option value
- * @r register contents
- * identifier variable value
- * function() function call
- * $VAR environment variable
- * (expression) nested expression
- * [expr, expr] List
- * {key: val, key: val} Dictionary
- *
- * Also handle:
- * ! in front logical NOT
- * - in front unary minus
- * + in front unary plus (ignored)
- * trailing [] subscript in String or List
- * trailing .name entry in Dictionary
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int
-eval7 (
+// Handle sixth level expression:
+// number number constant
+// "string" string constant
+// 'string' literal string constant
+// &option-name option value
+// @r register contents
+// identifier variable value
+// function() function call
+// $VAR environment variable
+// (expression) nested expression
+// [expr, expr] List
+// {key: val, key: val} Dictionary
+//
+// Also handle:
+// ! in front logical NOT
+// - in front unary minus
+// + in front unary plus (ignored)
+// trailing [] subscript in String or List
+// trailing .name entry in Dictionary
+//
+// "arg" must point to the first non-white of the expression.
+// "arg" is advanced to the next non-white after the recognized expression.
+//
+// Return OK or FAIL.
+static int eval7(
char_u **arg,
typval_T *rettv,
int evaluate,
- int want_string /* after "." operator */
+ int want_string // after "." operator
)
{
long n;
@@ -4066,24 +4079,19 @@ eval7 (
int ret = OK;
char_u *alias;
- /*
- * Initialise variable so that clear_tv() can't mistake this for a
- * string and free a string that isn't there.
- */
+ // Initialise variable so that clear_tv() can't mistake this for a
+ // string and free a string that isn't there.
rettv->v_type = VAR_UNKNOWN;
- /*
- * Skip '!' and '-' characters. They are handled later.
- */
+ // Skip '!' and '-' characters. They are handled later.
start_leader = *arg;
- while (**arg == '!' || **arg == '-' || **arg == '+')
+ while (**arg == '!' || **arg == '-' || **arg == '+') {
*arg = skipwhite(*arg + 1);
+ }
end_leader = *arg;
switch (**arg) {
- /*
- * Number constant.
- */
+ // Number constant.
case '0':
case '1':
case '2':
@@ -4096,27 +4104,30 @@ eval7 (
case '9':
{
char_u *p = skipdigits(*arg + 1);
- int get_float = FALSE;
+ int get_float = false;
- /* We accept a float when the format matches
- * "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
- * strict to avoid backwards compatibility problems.
- * Don't look for a float after the "." operator, so that
- * ":let vers = 1.2.3" doesn't fail. */
+ // We accept a float when the format matches
+ // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
+ // strict to avoid backwards compatibility problems.
+ // Don't look for a float after the "." operator, so that
+ // ":let vers = 1.2.3" doesn't fail.
if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) {
- get_float = TRUE;
+ get_float = true;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E') {
++p;
- if (*p == '-' || *p == '+')
+ if (*p == '-' || *p == '+') {
++p;
- if (!ascii_isdigit(*p))
- get_float = FALSE;
- else
+ }
+ if (!ascii_isdigit(*p)) {
+ get_float = false;
+ } else {
p = skipdigits(p + 1);
+ }
+ }
+ if (ASCII_ISALPHA(*p) || *p == '.') {
+ get_float = false;
}
- if (ASCII_ISALPHA(*p) || *p == '.')
- get_float = FALSE;
}
if (get_float) {
float_T f;
@@ -4127,7 +4138,7 @@ eval7 (
rettv->vval.v_float = f;
}
} else {
- vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL);
+ vim_str2nr(*arg, NULL, &len, true, true, true, &n, NULL);
*arg += len;
if (evaluate) {
rettv->v_type = VAR_NUMBER;
@@ -4137,62 +4148,47 @@ eval7 (
break;
}
- /*
- * String constant: "string".
- */
+ // String constant: "string".
case '"': ret = get_string_tv(arg, rettv, evaluate);
break;
- /*
- * Literal string constant: 'str''ing'.
- */
+ // Literal string constant: 'str''ing'.
case '\'': ret = get_lit_string_tv(arg, rettv, evaluate);
break;
- /*
- * List: [expr, expr]
- */
+ // List: [expr, expr]
case '[': ret = get_list_tv(arg, rettv, evaluate);
break;
- /*
- * Dictionary: {key: val, key: val}
- */
+ // Dictionary: {key: val, key: val}
case '{': ret = get_dict_tv(arg, rettv, evaluate);
break;
- /*
- * Option value: &name
- */
+ // Option value: &name
case '&': ret = get_option_tv(arg, rettv, evaluate);
break;
- /*
- * Environment variable: $VAR.
- */
+ // Environment variable: $VAR.
case '$': ret = get_env_tv(arg, rettv, evaluate);
break;
- /*
- * Register contents: @r.
- */
+ // Register contents: @r.
case '@': ++*arg;
if (evaluate) {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
}
- if (**arg != NUL)
+ if (**arg != NUL) {
++*arg;
+ }
break;
- /*
- * nested expression: (expression).
- */
+ // nested expression: (expression).
case '(': *arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); /* recursive! */
- if (**arg == ')')
+ ret = eval1(arg, rettv, evaluate); // recursive!
+ if (**arg == ')') {
++*arg;
- else if (ret == OK) {
+ } else if (ret == OK) {
EMSG(_("E110: Missing ')'"));
clear_tv(rettv);
ret = FAIL;
@@ -4204,71 +4200,72 @@ eval7 (
}
if (ret == NOTDONE) {
- /*
- * Must be a variable or function name.
- * Can also be a curly-braces kind of name: {expr}.
- */
+ // Must be a variable or function name.
+ // Can also be a curly-braces kind of name: {expr}.
s = *arg;
- len = get_name_len(arg, &alias, evaluate, TRUE);
- if (alias != NULL)
+ len = get_name_len(arg, &alias, evaluate, true);
+ if (alias != NULL) {
s = alias;
+ }
- if (len <= 0)
+ if (len <= 0) {
ret = FAIL;
- else {
- if (**arg == '(') { /* recursive! */
- /* If "s" is the name of a variable of type VAR_FUNC
- * use its contents. */
+ } else {
+ if (**arg == '(') { // recursive!
+ // If "s" is the name of a variable of type VAR_FUNC
+ // use its contents.
s = deref_func_name(s, &len, !evaluate);
- /* Invoke the function. */
+ // Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, NULL);
- /* If evaluate is FALSE rettv->v_type was not set in
- * get_func_tv, but it's needed in handle_subscript() to parse
- * what follows. So set it here. */
+ // If evaluate is false rettv->v_type was not set in
+ // get_func_tv, but it's needed in handle_subscript() to parse
+ // what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
rettv->vval.v_string = empty_string;
rettv->v_type = VAR_FUNC;
}
- /* Stop the expression evaluation when immediately
- * aborting on error, or when an interrupt occurred or
- * an exception was thrown but not caught. */
+ // Stop the expression evaluation when immediately
+ // aborting on error, or when an interrupt occurred or
+ // an exception was thrown but not caught.
if (aborting()) {
- if (ret == OK)
+ if (ret == OK) {
clear_tv(rettv);
+ }
ret = FAIL;
}
- } else if (evaluate)
- ret = get_var_tv(s, len, rettv, TRUE, FALSE);
- else
+ } else if (evaluate) {
+ ret = get_var_tv(s, len, rettv, true, false);
+ } else {
ret = OK;
+ }
}
xfree(alias);
}
*arg = skipwhite(*arg);
- /* Handle following '[', '(' and '.' for expr[expr], expr.name,
- * expr(expr). */
- if (ret == OK)
- ret = handle_subscript(arg, rettv, evaluate, TRUE);
+ // Handle following '[', '(' and '.' for expr[expr], expr.name,
+ // expr(expr).
+ if (ret == OK) {
+ ret = handle_subscript(arg, rettv, evaluate, true);
+ }
- /*
- * Apply logical NOT and unary '-', from right to left, ignore '+'.
- */
+ // Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
- int error = FALSE;
+ int error = false;
int val = 0;
float_T f = 0.0;
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = rettv->vval.v_float;
- else
+ } else {
val = get_tv_number_chk(rettv, &error);
+ }
if (error) {
clear_tv(rettv);
ret = FAIL;
@@ -4276,15 +4273,17 @@ eval7 (
while (end_leader > start_leader) {
--end_leader;
if (*end_leader == '!') {
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = !f;
- else
+ } else {
val = !val;
+ }
} else if (*end_leader == '-') {
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = -f;
- else
+ } else {
val = -val;
+ }
}
}
if (rettv->v_type == VAR_FLOAT) {
@@ -4655,10 +4654,13 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
int n, nr;
int c = toupper(*p);
- if (c == 'X')
+ if (c == 'X') {
n = 2;
- else
+ } else if (*p == 'u') {
n = 4;
+ } else {
+ n = 8;
+ }
nr = 0;
while (--n >= 0 && ascii_isxdigit(p[1])) {
++p;
@@ -6069,7 +6071,7 @@ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
#ifndef __clang_analyzer__
STRCPY(di->di_key, key);
#endif
- di->di_flags = 0;
+ di->di_flags = DI_FLAGS_ALLOC;
return di;
}
@@ -6081,7 +6083,7 @@ static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET
dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key));
STRCPY(di->di_key, org->di_key);
- di->di_flags = 0;
+ di->di_flags = DI_FLAGS_ALLOC;
copy_tv(&org->di_tv, &di->di_tv);
return di;
@@ -6109,7 +6111,9 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item)
void dictitem_free(dictitem_T *item)
{
clear_tv(&item->di_tv);
- xfree(item);
+ if (item->di_flags & DI_FLAGS_ALLOC) {
+ xfree(item);
+ }
}
/// Make a copy of dictionary
@@ -7205,9 +7209,9 @@ static struct fst {
{ "getwinposx", 0, 0, f_getwinposx },
{ "getwinposy", 0, 0, f_getwinposy },
{ "getwinvar", 2, 3, f_getwinvar },
- { "glob", 1, 3, f_glob },
+ { "glob", 1, 4, f_glob },
{ "glob2regpat", 1, 1, f_glob2regpat },
- { "globpath", 2, 4, f_globpath },
+ { "globpath", 2, 5, f_globpath },
{ "has", 1, 1, f_has },
{ "has_key", 2, 2, f_has_key },
{ "haslocaldir", 0, 0, f_haslocaldir },
@@ -7236,6 +7240,7 @@ static struct fst {
{ "islocked", 1, 1, f_islocked },
{ "items", 1, 1, f_items },
{ "jobclose", 1, 2, f_jobclose },
+ { "jobpid", 1, 1, f_jobpid },
{ "jobresize", 3, 3, f_jobresize },
{ "jobsend", 2, 2, f_jobsend },
{ "jobstart", 1, 2, f_jobstart },
@@ -9225,6 +9230,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
hashitem_T *hi2;
int todo;
bool watched = is_watched(d1);
+ char *arg_errmsg = N_("extend() argument");
todo = (int)d2->dv_hashtab.ht_used;
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) {
@@ -9258,6 +9264,11 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
} else if (*action == 'f' && HI2DI(hi2) != di1) {
typval_T oldtv;
+ if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg))
+ || var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg))) {
+ break;
+ }
+
if (watched) {
copy_tv(&di1->di_tv, &oldtv);
}
@@ -9473,12 +9484,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) == NULL
- || tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))
+ || (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))) {
return;
+ }
} else if (argvars[0].v_type == VAR_DICT) {
if ((d = argvars[0].vval.v_dict) == NULL
- || tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))
+ || (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))) {
return;
+ }
} else {
EMSG2(_(e_listdictarg), ermsg);
return;
@@ -9507,17 +9520,26 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
for (hi = ht->ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
--todo;
+
di = HI2DI(hi);
- if (tv_check_lock(di->di_tv.v_lock,
- (char_u *)_(arg_errmsg)))
+ if (map
+ && (tv_check_lock(di->di_tv.v_lock, (char_u *)_(arg_errmsg))
+ || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg)))) {
break;
+ }
+
vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
int r = filter_map_one(&di->di_tv, expr, map, &rem);
clear_tv(&vimvars[VV_KEY].vv_tv);
if (r == FAIL || did_emsg)
break;
- if (!map && rem)
+ if (!map && rem) {
+ if (var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg))
+ || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) {
+ break;
+ }
dictitem_remove(d, di);
+ }
}
}
hash_unlock(ht);
@@ -9525,8 +9547,9 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
vimvars[VV_KEY].vv_type = VAR_NUMBER;
for (li = l->lv_first; li != NULL; li = nli) {
- if (tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg)))
+ if (map && tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) {
break;
+ }
nli = li->li_next;
vimvars[VV_KEY].vv_nr = idx;
if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
@@ -10717,10 +10740,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
if (get_tv_number_chk(&argvars[1], &error))
options |= WILD_KEEP_ALL;
- if (argvars[2].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[2], &error)) {
- rettv->v_type = VAR_LIST;
- rettv->vval.v_list = NULL;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ if (get_tv_number_chk(&argvars[2], &error)) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ if (argvars[3].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[3], &error)) {
+ options |= WILD_ALLLINKS;
+ }
}
}
if (!error) {
@@ -10759,10 +10787,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv)
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;
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ if (get_tv_number_chk(&argvars[3], &error)) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[4], &error)) {
+ flags |= WILD_ALLLINKS;
+ }
}
}
@@ -10789,15 +10822,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv)
}
}
-/*
- * "glob2regpat()" function
- */
+// "glob2regpat()" function
static void f_glob2regpat(typval_T *argvars, typval_T *rettv)
{
- char_u *pat = get_tv_string_chk(&argvars[0]);
+ char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (pat == NULL)
+ ? NULL
+ : file_pat_to_reg_pat(pat, NULL, NULL, false);
}
/*
@@ -11611,6 +11644,31 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv)
}
}
+// "jobpid(id)" function
+static void f_jobpid(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ TerminalJobData *data = find_job(argvars[0].vval.v_number);
+ if (!data) {
+ EMSG(_(e_invjob));
+ return;
+ }
+
+ Process *proc = (Process *)&data->proc;
+ rettv->vval.v_number = proc->pid;
+}
+
// "jobsend()" function
static void f_jobsend(typval_T *argvars, typval_T *rettv)
{
@@ -11771,8 +11829,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
}
bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0;
+ bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0;
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, pty);
+ job_opts, pty, detach);
Process *proc = (Process *)&data->proc;
if (pty) {
@@ -13855,9 +13914,10 @@ static void f_remove(typval_T *argvars, typval_T *rettv)
key = get_tv_string_chk(&argvars[1]);
if (key != NULL) {
di = dict_find(d, key, -1);
- if (di == NULL)
+ if (di == NULL) {
EMSG2(_(e_dictkey), key);
- else {
+ } else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg))
+ && !var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) {
*rettv = di->di_tv;
init_tv(&di->di_tv);
dictitem_remove(d, di);
@@ -15971,9 +16031,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_FLOAT;
}
-/*
- * "str2nr()" function
- */
+// "str2nr()" function
static void f_str2nr(typval_T *argvars, typval_T *rettv)
{
int base = 10;
@@ -15982,16 +16040,21 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
base = get_tv_number(&argvars[1]);
- if (base != 8 && base != 10 && base != 16) {
+ if (base != 2 && base != 8 && base != 10 && base != 16) {
EMSG(_(e_invarg));
return;
}
}
p = skipwhite(get_tv_string(&argvars[0]));
- if (*p == '+')
+ if (*p == '+') {
p = skipwhite(p + 1);
- vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL);
+ }
+ vim_str2nr(p, NULL, NULL,
+ base == 2 ? 2 : 0,
+ base == 8 ? 2 : 0,
+ base == 16 ? 2 : 0,
+ &n, NULL);
rettv->vval.v_number = n;
}
@@ -16776,7 +16839,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
}
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, true);
+ job_opts, true, false);
data->proc.pty.width = curwin->w_width;
data->proc.pty.height = curwin->w_height;
data->proc.pty.term_name = xstrdup("xterm-256color");
@@ -18271,9 +18334,10 @@ long get_tv_number_chk(typval_T *varp, int *denote)
EMSG(_("E703: Using a Funcref as a Number"));
break;
case VAR_STRING:
- if (varp->vval.v_string != NULL)
+ if (varp->vval.v_string != NULL) {
vim_str2nr(varp->vval.v_string, NULL, NULL,
- TRUE, TRUE, &n, NULL);
+ true, true, true, &n, NULL);
+ }
return n;
case VAR_LIST:
EMSG(_("E745: Using a List as a Number"));
@@ -18285,10 +18349,12 @@ long get_tv_number_chk(typval_T *varp, int *denote)
EMSG2(_(e_intern2), "get_tv_number()");
break;
}
- if (denote == NULL) /* useful for values that must be unsigned */
+ if (denote == NULL) {
+ // useful for values that must be unsigned
n = -1;
- else
- *denote = TRUE;
+ } else {
+ *denote = true;
+ }
return n;
}
@@ -18619,14 +18685,16 @@ static void vars_clear_ext(hashtab_T *ht, int free_val)
if (!HASHITEM_EMPTY(hi)) {
--todo;
- /* Free the variable. Don't remove it from the hashtab,
- * ht_array might change then. hash_clear() takes care of it
- * later. */
+ // Free the variable. Don't remove it from the hashtab,
+ // ht_array might change then. hash_clear() takes care of it
+ // later.
v = HI2DI(hi);
- if (free_val)
+ if (free_val) {
clear_tv(&v->di_tv);
- if ((v->di_flags & DI_FLAGS_FIX) == 0)
+ }
+ if (v->di_flags & DI_FLAGS_ALLOC) {
xfree(v);
+ }
}
}
hash_clear(ht);
@@ -18800,7 +18868,7 @@ set_var (
xfree(v);
return;
}
- v->di_flags = 0;
+ v->di_flags = DI_FLAGS_ALLOC;
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
@@ -20691,7 +20759,7 @@ call_user_func (
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
- v->di_flags = DI_FLAGS_RO;
+ v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
@@ -21782,8 +21850,13 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
return ret;
}
-static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout,
- ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self, bool pty)
+static inline TerminalJobData *common_job_init(char **argv,
+ ufunc_T *on_stdout,
+ ufunc_T *on_stderr,
+ ufunc_T *on_exit,
+ dict_T *self,
+ bool pty,
+ bool detach)
{
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
data->stopped = false;
@@ -21806,6 +21879,7 @@ static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout,
}
proc->cb = on_process_exit;
proc->events = data->events;
+ proc->detach = detach;
return data;
}
@@ -21833,8 +21907,13 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout,
static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
{
- data->refcount++;
Process *proc = (Process *)&data->proc;
+ if (proc->type == kProcessTypePty && proc->detach) {
+ EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
+ return false;
+ }
+
+ data->refcount++;
char *cmd = xstrdup(proc->argv[0]);
if (!process_spawn(proc)) {
EMSG2(_(e_jobspawn), cmd);