aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/userfunc.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/eval/userfunc.c
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.gz
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.bz2
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/eval/userfunc.c')
-rw-r--r--src/nvim/eval/userfunc.c1043
1 files changed, 569 insertions, 474 deletions
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c46cb6ba5d..22c5b1954d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -3,45 +3,52 @@
// User defined function support
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lauxlib.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
+#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-// flags used in uf_flags
-#define FC_ABORT 0x01 // abort function on error
-#define FC_RANGE 0x02 // function accepts range
-#define FC_DICT 0x04 // Dict function, uses "self"
-#define FC_CLOSURE 0x08 // closure, uses outer scope variables
-#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
-#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
-#define FC_SANDBOX 0x40 // function defined in the sandbox
-#define FC_DEAD 0x80 // function kept only for reference to dfunc
-#define FC_EXPORT 0x100 // "export def Func()"
-#define FC_NOARGS 0x200 // no a: variables in lambda
-#define FC_VIM9 0x400 // defined in vim9 script file
-#define FC_CFUNC 0x800 // C function extension
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.c.generated.h"
#endif
@@ -62,6 +69,8 @@ static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace
static char *e_funcdict = N_("E717: Dictionary entry already exists");
static char *e_funcref = N_("E718: Funcref required");
static char *e_nofunc = N_("E130: Unknown function: %s");
+static char e_no_white_space_allowed_before_str_str[]
+ = N_("E1068: No white space allowed before '%s': %s");
void func_init(void)
{
@@ -75,7 +84,7 @@ hashtab_T *func_tbl_get(void)
}
/// Get function arguments.
-static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int *varargs,
+static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
{
bool mustend = false;
@@ -85,10 +94,10 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
int i;
if (newargs != NULL) {
- ga_init(newargs, (int)sizeof(char_u *), 3);
+ ga_init(newargs, (int)sizeof(char *), 3);
}
if (default_args != NULL) {
- ga_init(default_args, (int)sizeof(char_u *), 3);
+ ga_init(default_args, (int)sizeof(char *), 3);
}
if (varargs != NULL) {
@@ -97,7 +106,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Isolate the arguments: "arg1, arg2, ...)"
bool any_default = false;
- while (*p != (char)endchar) {
+ while (*p != endchar) {
if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
if (varargs != NULL) {
*varargs = true;
@@ -109,9 +118,9 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
while (ASCII_ISALNUM(*p) || *p == '_') {
p++;
}
- if (arg == p || isdigit(*arg)
- || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
- || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
+ if (arg == p || isdigit((uint8_t)(*arg))
+ || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
if (!skip) {
semsg(_("E125: Illegal argument: %s"), arg);
}
@@ -125,7 +134,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; i++) {
- if (STRCMP(((char **)(newargs->ga_data))[i], arg) == 0) {
+ if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) {
semsg(_("E853: Duplicate argument name: %s"), arg);
xfree(arg);
goto err_ret;
@@ -142,18 +151,18 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
any_default = true;
p = skipwhite(p) + 1;
p = skipwhite(p);
- char_u *expr = (char_u *)p;
+ char *expr = p;
if (eval1(&p, &rettv, false) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
- while (p > (char *)expr && ascii_iswhite(p[-1])) {
+ while (p > expr && ascii_iswhite(p[-1])) {
p--;
}
c = (char_u)(*p);
*p = NUL;
- expr = vim_strsave(expr);
- ((char **)(default_args->ga_data))[default_args->ga_len] = (char *)expr;
+ expr = xstrdup(expr);
+ ((char **)(default_args->ga_data))[default_args->ga_len] = expr;
default_args->ga_len++;
*p = (char)c;
} else {
@@ -163,6 +172,15 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
emsg(_("E989: Non-default argument follows default argument"));
mustend = true;
}
+
+ if (ascii_iswhite(*p) && *skipwhite(p) == ',') {
+ // Be tolerant when skipping
+ if (!skip) {
+ semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
+ goto err_ret;
+ }
+ p = skipwhite(p);
+ }
if (*p == ',') {
p++;
} else {
@@ -170,14 +188,14 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
}
}
p = skipwhite(p);
- if (mustend && *p != (char)endchar) {
+ if (mustend && *p != endchar) {
if (!skip) {
semsg(_(e_invarg2), *argp);
}
break;
}
}
- if (*p != (char)endchar) {
+ if (*p != endchar) {
goto err_ret;
}
p++; // skip "endchar"
@@ -211,21 +229,21 @@ static void register_closure(ufunc_T *fp)
}
/// @return a name for a lambda. Returned in static memory.
-char_u *get_lambda_name(void)
+char *get_lambda_name(void)
{
- static char_u name[30];
+ static char name[30];
static int lambda_no = 0;
- snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no);
+ snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
return name;
}
-static void set_ufunc_name(ufunc_T *fp, char_u *name)
+static void set_ufunc_name(ufunc_T *fp, char *name)
{
STRCPY(fp->uf_name, name);
- if (name[0] == K_SPECIAL) {
- fp->uf_name_exp = xmalloc(STRLEN(name) + 3);
+ if ((uint8_t)name[0] == K_SPECIAL) {
+ fp->uf_name_exp = xmalloc(strlen(name) + 3);
STRCPY(fp->uf_name_exp, "<SNR>");
STRCAT(fp->uf_name_exp, fp->uf_name + 3);
}
@@ -242,13 +260,13 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
partial_T *pt = NULL;
int varargs;
int ret;
- char_u *start = (char_u *)skipwhite(*arg + 1);
- char_u *s, *e;
+ char *start = skipwhite(*arg + 1);
+ char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args((char **)&start, '-', NULL, NULL, NULL, true);
+ ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *start != '>') {
return NOTDONE;
}
@@ -272,38 +290,39 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = (char_u *)(*arg);
+ s = *arg;
ret = skip_expr(arg);
if (ret == FAIL) {
goto errret;
}
- e = (char_u *)(*arg);
+ e = *arg;
*arg = skipwhite(*arg);
if (**arg != '}') {
+ semsg(_("E451: Expected }: %s"), *arg);
goto errret;
}
(*arg)++;
if (evaluate) {
int flags = 0;
- char_u *p;
+ char *p;
garray_T newlines;
- char_u *name = get_lambda_name();
+ char *name = get_lambda_name();
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
pt = xcalloc(1, sizeof(partial_T));
- ga_init(&newlines, (int)sizeof(char_u *), 1);
+ ga_init(&newlines, (int)sizeof(char *), 1);
ga_grow(&newlines, 1);
// Add "return " before the expression.
size_t len = (size_t)(7 + e - s + 1);
- p = (char_u *)xmalloc(len);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
+ p = xmalloc(len);
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- STRLCPY(p + 7, s, e - s + 1);
- if (strstr((char *)p + 7, "a:") == NULL) {
+ xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
}
@@ -312,7 +331,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
- ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
+ ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
flags |= FC_CLOSURE;
@@ -365,7 +384,7 @@ errret:
/// was not found.
///
/// @return name of the function.
-char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
+char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
if (partialp != NULL) {
@@ -376,10 +395,10 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
if (v->di_tv.vval.v_string == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
- *lenp = (int)STRLEN(v->di_tv.vval.v_string);
- return (char_u *)v->di_tv.vval.v_string;
+ *lenp = (int)strlen(v->di_tv.vval.v_string);
+ return v->di_tv.vval.v_string;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
@@ -387,31 +406,31 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (pt == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
if (partialp != NULL) {
*partialp = pt;
}
- char_u *s = (char_u *)partial_name(pt);
- *lenp = (int)STRLEN(s);
+ char *s = partial_name(pt);
+ *lenp = (int)strlen(s);
return s;
}
- return (char_u *)name;
+ return (char *)name;
}
/// Give an error message with a function name. Handle <SNR> things.
///
/// @param ermsg must be passed without translation (use N_() instead of _()).
/// @param name function name
-void emsg_funcname(char *ermsg, const char_u *name)
+void emsg_funcname(char *ermsg, const char *name)
{
- char_u *p;
+ char *p;
- if (*name == K_SPECIAL) {
- p = concat_str((char_u *)"<SNR>", name + 3);
+ if ((uint8_t)(*name) == K_SPECIAL) {
+ p = concat_str("<SNR>", name + 3);
} else {
- p = (char_u *)name;
+ p = (char *)name;
}
semsg(_(ermsg), p);
@@ -429,7 +448,7 @@ void emsg_funcname(char *ermsg, const char_u *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
{
char *argp;
int ret = OK;
@@ -439,12 +458,12 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
// Get the arguments.
argp = *arg;
while (argcount < MAX_FUNC_ARGS
- - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
+ - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) {
argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
ret = FAIL;
break;
}
@@ -463,7 +482,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
int i = 0;
if (get_vim_var_nr(VV_TESTING)) {
- // Prepare for calling garbagecollect_for_testing(), need to know
+ // Prepare for calling test_garbagecollect_now(), need to know
// what variables are used on the call stack.
if (funcargs.ga_itemsize == 0) {
ga_init(&funcargs, (int)sizeof(typval_T *), 50);
@@ -473,7 +492,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
}
}
- ret = call_func((char *)name, len, rettv, argcount, argvars, funcexe);
+ ret = call_func(name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
} else if (!aborting()) {
@@ -525,48 +544,47 @@ static inline bool eval_fname_sid(const char *const name)
///
/// @return transformed name: either `fname_buf` or a pointer to an allocated
/// memory.
-static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf,
- char_u **const tofree, int *const error)
+static char *fname_trans_sid(const char *const name, char *const fname_buf, char **const tofree,
+ int *const error)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *fname;
- const int llen = eval_fname_script((const char *)name);
- if (llen > 0) {
- fname_buf[0] = K_SPECIAL;
- fname_buf[1] = KS_EXTRA;
- fname_buf[2] = KE_SNR;
- int i = 3;
- if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
- if (current_sctx.sc_sid <= 0) {
- *error = ERROR_SCRIPT;
- } else {
- snprintf((char *)fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- i = (int)STRLEN(fname_buf);
- }
- }
- if ((size_t)i + STRLEN(name + llen) < FLEN_FIXED) {
- STRCPY(fname_buf + i, name + llen);
- fname = fname_buf;
+ const int llen = eval_fname_script(name);
+ if (llen == 0) {
+ return (char *)name; // no prefix
+ }
+
+ fname_buf[0] = (char)K_SPECIAL;
+ fname_buf[1] = (char)KS_EXTRA;
+ fname_buf[2] = KE_SNR;
+ int i = 3;
+ if (eval_fname_sid(name)) { // "<SID>" or "s:"
+ if (current_sctx.sc_sid <= 0) {
+ *error = FCERR_SCRIPT;
} else {
- fname = xmalloc((size_t)i + STRLEN(name + llen) + 1);
- *tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
+ (int64_t)current_sctx.sc_sid);
+ i = (int)strlen(fname_buf);
}
+ }
+ char *fname;
+ if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
+ STRCPY(fname_buf + i, name + llen);
+ fname = fname_buf;
} else {
- fname = (char_u *)name;
+ fname = xmalloc((size_t)i + strlen(name + llen) + 1);
+ *tofree = fname;
+ memmove(fname, fname_buf, (size_t)i);
+ STRCPY(fname + i, name + llen);
}
-
return fname;
}
/// Find a function by name, return pointer to it in ufuncs.
///
/// @return NULL for unknown function.
-ufunc_T *find_func(const char_u *name)
+ufunc_T *find_func(const char *name)
{
- hashitem_T *hi = hash_find(&func_hashtab, (char *)name);
+ hashitem_T *hi = hash_find(&func_hashtab, name);
if (!HASHITEM_EMPTY(hi)) {
return HI2UF(hi);
}
@@ -576,9 +594,9 @@ ufunc_T *find_func(const char_u *name)
/// Copy the function name of "fp" to buffer "buf".
/// "buf" must be able to hold the function name plus three bytes.
/// Takes care of script-local function names.
-static void cat_func_name(char_u *buf, ufunc_T *fp)
+static void cat_func_name(char *buf, ufunc_T *fp)
{
- if (fp->uf_name[0] == K_SPECIAL) {
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
STRCPY(buf, "<SNR>");
STRCAT(buf, fp->uf_name + 3);
} else {
@@ -593,7 +611,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
STRCPY(v->di_key, name);
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(dp, v);
+ hash_add(&dp->dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_NUMBER;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_number = nr;
@@ -758,9 +776,9 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_lines));
XFREE_CLEAR(fp->uf_name_exp);
- if (fp->uf_cb_free != NULL) {
- fp->uf_cb_free(fp->uf_cb_state);
- fp->uf_cb_free = NULL;
+ if (fp->uf_flags & FC_LUAREF) {
+ api_free_luaref(fp->uf_luaref);
+ fp->uf_luaref = LUA_NOREF;
}
XFREE_CLEAR(fp->uf_tml_count);
@@ -828,7 +846,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
int fixvar_idx = 0; // index in fixvar[]
int ai;
bool islambda = false;
- char_u numbuf[NUMBUFLEN];
+ char numbuf[NUMBUFLEN];
char *name;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
@@ -852,7 +870,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
saveRedobuff(&save_redo);
did_save_redo = true;
}
- ++fp->uf_calls;
+ fp->uf_calls++;
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
@@ -863,14 +881,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
fc->rettv = rettv;
fc->level = ex_nesting_level;
// Check if this function has a breakpoint.
- fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0);
+ fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
// Set up fields for closure.
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
- if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ if (strncmp(fp->uf_name, "<lambda>", 8) == 0) {
islambda = true;
}
@@ -889,11 +907,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "self");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = VAR_UNLOCKED;
v->di_tv.vval.v_dict = selfdict;
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
// Init a: variables, unless none found (in lambda).
@@ -915,7 +933,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "000");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist;
@@ -967,14 +985,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
break;
}
// "..." argument a:1, a:2, etc.
- snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
- name = (char *)numbuf;
+ snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
+ name = numbuf;
}
- if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
+ if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
- v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
+ v = xmalloc(sizeof(dictitem_T) + strlen(name));
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
@@ -993,16 +1011,16 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Named arguments can be accessed without the "a:" prefix in lambda
// expressions. Add to the l: dict.
tv_copy(&v->di_tv, &v->di_tv);
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
} else {
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
listitem_T *li = &fc->l_listitems[ai];
*TV_LIST_ITEM_TV(li) = argvars[i];
- TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
+ TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
tv_list_append(&fc->l_varlist, li);
}
}
@@ -1058,7 +1076,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool func_not_yet_profiling_but_should =
do_profiling_yes
- && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
+ && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL);
if (func_not_yet_profiling_but_should) {
started_profiling = true;
@@ -1071,7 +1089,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|| (fc->caller != NULL && fc->caller->func->uf_profiling));
if (func_or_func_caller_profiling) {
- ++fp->uf_tm_count;
+ fp->uf_tm_count++;
call_start = profile_start();
fp->uf_tm_children = profile_zero();
}
@@ -1083,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
const sctx_T save_current_sctx = current_sctx;
current_sctx = fp->uf_script_ctx;
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
if (default_arg_err && (fp->uf_flags & FC_ABORT)) {
did_emsg = true;
@@ -1208,9 +1226,37 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
/// For the first we only count the name stored in func_hashtab as a reference,
/// using function() does not count as a reference, because the function is
/// looked up by name.
-static bool func_name_refcount(char_u *name)
+static bool func_name_refcount(const char *name)
+{
+ return isdigit((uint8_t)(*name)) || *name == '<';
+}
+
+/// Call a user function after checking the arguments.
+static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv,
+ funcexe_T *funcexe, dict_T *selfdict)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5)
{
- return isdigit(*name) || *name == '<';
+ if (fp->uf_flags & FC_LUAREF) {
+ return typval_exec_lua_callable(fp->uf_luaref, argcount, argvars, rettv);
+ }
+
+ if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = true;
+ }
+ int error;
+ if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
+ error = FCERR_TOOFEW;
+ } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
+ error = FCERR_TOOMANY;
+ } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
+ error = FCERR_DICT;
+ } else {
+ // Call the user function.
+ call_user_func(fp, argcount, argvars, rettv, funcexe->fe_firstline, funcexe->fe_lastline,
+ (fp->uf_flags & FC_DICT) ? selfdict : NULL);
+ error = FCERR_NONE;
+ }
+ return error;
}
static funccal_entry_T *funccal_stack = NULL;
@@ -1252,7 +1298,7 @@ void free_all_functions(void)
ufunc_T *fp;
uint64_t skipped = 0;
uint64_t todo = 1;
- uint64_t used;
+ int changed;
// Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
@@ -1276,9 +1322,9 @@ void free_all_functions(void)
if (func_name_refcount(fp->uf_name)) {
skipped++;
} else {
- used = func_hashtab.ht_used;
+ changed = func_hashtab.ht_changed;
func_clear(fp, true);
- if (used != func_hashtab.ht_used) {
+ if (changed != func_hashtab.ht_changed) {
skipped = 0;
break;
}
@@ -1322,10 +1368,10 @@ void free_all_functions(void)
/// @param[in] len length of "name", or -1 for NUL terminated.
///
/// @return true if "name" looks like a builtin function name: starts with a
-/// lower case letter and doesn't contain AUTOLOAD_CHAR.
+/// lower case letter and doesn't contain AUTOLOAD_CHAR or ':'.
static bool builtin_function(const char *name, int len)
{
- if (!ASCII_ISLOWER(name[0])) {
+ if (!ASCII_ISLOWER(name[0]) || name[1] == ':') {
return false;
}
@@ -1336,7 +1382,7 @@ static bool builtin_function(const char *name, int len)
return p == NULL;
}
-int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
+int func_call(char *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
{
typval_T argv[MAX_FUNC_ARGS + 1];
int argc = 0;
@@ -1353,12 +1399,12 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
});
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- funcexe.partial = partial;
- funcexe.selfdict = selfdict;
- r = call_func((char *)name, -1, rettv, argc, argv, &funcexe);
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = selfdict;
+ r = call_func(name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call:
// Free the arguments.
@@ -1369,35 +1415,50 @@ func_call_skip_call:
return r;
}
+/// call the 'callback' function and return the result as a number.
+/// Returns -2 when calling the function fails. Uses argv[0] to argv[argc - 1]
+/// for the function arguments. argv[argc] should have type VAR_UNKNOWN.
+///
+/// @param argcount number of "argvars"
+/// @param argvars vars for arguments, must have "argcount" PLUS ONE elements!
+varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars)
+{
+ typval_T rettv;
+ if (!callback_call(callback, argcount, argvars, &rettv)) {
+ return -2;
+ }
+
+ varnumber_T retval = tv_get_number_chk(&rettv, NULL);
+ tv_clear(&rettv);
+ return retval;
+}
+
/// Give an error message for the result of a function.
/// Nothing if "error" is FCERR_NONE.
-static void user_func_error(int error, const char_u *name)
+static void user_func_error(int error, const char *name)
FUNC_ATTR_NONNULL_ALL
{
switch (error) {
- case ERROR_UNKNOWN:
+ case FCERR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name);
break;
- case ERROR_NOTMETHOD:
+ case FCERR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
break;
- case ERROR_DELETED:
+ case FCERR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name);
break;
- case ERROR_TOOMANY:
+ case FCERR_TOOMANY:
emsg_funcname(_(e_toomanyarg), name);
break;
- case ERROR_TOOFEW:
- emsg_funcname(N_("E119: Not enough arguments for function: %s"),
- name);
+ case FCERR_TOOFEW:
+ emsg_funcname(N_("E119: Not enough arguments for function: %s"), name);
break;
- case ERROR_SCRIPT:
- emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
- name);
+ case FCERR_SCRIPT:
+ emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name);
break;
- case ERROR_DICT:
- emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
- name);
+ case FCERR_DICT:
+ emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), name);
break;
}
}
@@ -1435,27 +1496,27 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{
int ret = FAIL;
- int error = ERROR_NONE;
+ int error = FCERR_NONE;
ufunc_T *fp = NULL;
- char_u fname_buf[FLEN_FIXED + 1];
- char_u *tofree = NULL;
- char_u *fname = NULL;
- char_u *name = NULL;
+ char fname_buf[FLEN_FIXED + 1];
+ char *tofree = NULL;
+ char *fname = NULL;
+ char *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
- dict_T *selfdict = funcexe->selfdict;
+ dict_T *selfdict = funcexe->fe_selfdict;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
- // "funcexe->basetv" is not NULL
+ // "funcexe->fe_basetv" is not NULL
int argv_clear = 0;
int argv_base = 0;
- partial_T *partial = funcexe->partial;
+ partial_T *partial = funcexe->fe_partial;
// Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
if (len <= 0) {
- len = (int)STRLEN(funcname);
+ len = (int)strlen(funcname);
}
if (partial != NULL) {
fp = partial->pt_func;
@@ -1463,12 +1524,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (fp == NULL) {
// Make a copy of the name, if it comes from a funcref variable it could
// be changed or deleted in the called function.
- name = vim_strnsave((char_u *)funcname, (size_t)len);
+ name = xstrnsave(funcname, (size_t)len);
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
- if (funcexe->doesrange != NULL) {
- *funcexe->doesrange = false;
+ if (funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = false;
}
if (partial != NULL) {
@@ -1478,10 +1539,10 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
selfdict = partial->pt_dict;
}
- if (error == ERROR_NONE && partial->pt_argc > 0) {
+ if (error == FCERR_NONE && partial->pt_argc > 0) {
for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) {
if (argv_clear + argcount_in >= MAX_FUNC_ARGS) {
- error = ERROR_TOOMANY;
+ error = FCERR_TOOMANY;
goto theend;
}
tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]);
@@ -1494,8 +1555,8 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
}
}
- if (error == ERROR_NONE && funcexe->evaluate) {
- char_u *rfname = fname;
+ if (error == FCERR_NONE && funcexe->fe_evaluate) {
+ char *rfname = fname;
// Ignore "g:" before a function name.
if (fp == NULL && fname[0] == 'g' && fname[1] == ':') {
@@ -1504,12 +1565,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
- error = ERROR_UNKNOWN;
+ error = FCERR_UNKNOWN;
if (is_luafunc(partial)) {
if (len > 0) {
- error = ERROR_NONE;
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ error = FCERR_NONE;
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
nlua_typval_call(funcname, (size_t)len, argvars, argcount, rettv);
} else {
// v:lua was called directly; show its name in the emsg
@@ -1524,71 +1585,50 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
// Trigger FuncUndefined event, may load the function.
if (fp == NULL
- && apply_autocmds(EVENT_FUNCUNDEFINED, (char *)rfname, (char *)rfname, true, NULL)
+ && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, true, NULL)
&& !aborting()) {
// executed an autocommand, search for the function again
fp = find_func(rfname);
}
// Try loading a package.
- if (fp == NULL && script_autoload((const char *)rfname, STRLEN(rfname),
+ if (fp == NULL && script_autoload((const char *)rfname, strlen(rfname),
true) && !aborting()) {
// Loaded a package, search for the function again.
fp = find_func(rfname);
}
if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
- error = ERROR_DELETED;
- } else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) {
- cfunc_T cb = fp->uf_cb;
- error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
+ error = FCERR_DELETED;
} else if (fp != NULL) {
- if (funcexe->argv_func != NULL) {
+ if (funcexe->fe_argv_func != NULL) {
// postponed filling in the arguments, do it now
- argcount = funcexe->argv_func(argcount, argvars, argv_clear,
- fp->uf_args.ga_len);
+ argcount = funcexe->fe_argv_func(argcount, argvars, argv_clear, fp);
}
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
- if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) {
- *funcexe->doesrange = true;
- }
- if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
- error = ERROR_TOOFEW;
- } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
- error = ERROR_TOOMANY;
- } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
- error = ERROR_DICT;
- } else {
- // Call the user function.
- call_user_func(fp, argcount, argvars, rettv, funcexe->firstline,
- funcexe->lastline,
- (fp->uf_flags & FC_DICT) ? selfdict : NULL);
- error = ERROR_NONE;
- }
+ error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict);
}
- } else if (funcexe->basetv != NULL) {
+ } else if (funcexe->fe_basetv != NULL) {
// expr->method(): Find the method name in the table, call its
// implementation with the base as one of the arguments.
error = call_internal_method(fname, argcount, argvars, rettv,
- funcexe->basetv);
+ funcexe->fe_basetv);
} else {
// Find the function name in the table, call its implementation.
error = call_internal_func(fname, argcount, argvars, rettv);
}
- /*
- * The function call (or "FuncUndefined" autocommand sequence) might
- * have been aborted by an error, an interrupt, or an explicitly thrown
- * exception that has not been caught so far. This situation can be
- * tested for by calling aborting(). For an error in an internal
- * function or for the "E132" error in call_user_func(), however, the
- * throw point at which the "force_abort" flag (temporarily reset by
- * emsg()) is normally updated has not been reached yet. We need to
- * update that flag first to make aborting() reliable.
- */
+ // The function call (or "FuncUndefined" autocommand sequence) might
+ // have been aborted by an error, an interrupt, or an explicitly thrown
+ // exception that has not been caught so far. This situation can be
+ // tested for by calling aborting(). For an error in an internal
+ // function or for the "E132" error in call_user_func(), however, the
+ // throw point at which the "force_abort" flag (temporarily reset by
+ // emsg()) is normally updated has not been reached yet. We need to
+ // update that flag first to make aborting() reliable.
update_force_abort();
}
- if (error == ERROR_NONE) {
+ if (error == FCERR_NONE) {
ret = OK;
}
@@ -1596,7 +1636,7 @@ theend:
// Report an error unless the argument evaluation or function call has been
// cancelled due to an aborting error, an interrupt, or an exception.
if (!aborting()) {
- user_func_error(error, (name != NULL) ? name : (char_u *)funcname);
+ user_func_error(error, (name != NULL) ? name : funcname);
}
// clear the copies made from the partial
@@ -1610,6 +1650,11 @@ theend:
return ret;
}
+char *printable_func_name(ufunc_T *fp)
+{
+ return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
+}
+
/// List the head of the function: "name(arg1, arg2)".
///
/// @param[in] fp Function pointer.
@@ -1679,12 +1724,12 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
/// @param partial return: partial of a FuncRef
///
/// @return the function name in allocated memory, or NULL for failure.
-char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
+char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *name = NULL;
- const char_u *start;
- const char_u *end;
+ char *name = NULL;
+ const char *start;
+ const char *end;
int lead;
int len;
lval_T lv;
@@ -1692,7 +1737,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
if (fdp != NULL) {
CLEAR_POINTER(fdp);
}
- start = (char_u *)(*pp);
+ start = *pp;
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
@@ -1700,19 +1745,19 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
&& (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
- return (char_u *)xmemdupz(start, (size_t)len);
+ return xmemdupz(start, (size_t)len);
}
// A name starting with "<SID>" or "<SNR>" is local to a script. But
// don't skip over "s:", get_lval() needs it for "s:dict.func".
- lead = eval_fname_script((const char *)start);
+ lead = eval_fname_script(start);
if (lead > 2) {
start += lead;
}
// Note that TFN_ flags use the same values as GLV_ flags.
- end = (char_u *)get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
- lead > 2 ? 0 : FNE_CHECK_START);
+ end = get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
+ lead > 2 ? 0 : FNE_CHECK_START);
if (end == start) {
if (!skip) {
emsg(_("E129: Function name required"));
@@ -1720,11 +1765,9 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
goto theend;
}
if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) {
- /*
- * Report an invalid expression in braces, unless the expression
- * evaluation has been cancelled due to an aborting error, an
- * interrupt, or an exception.
- */
+ // Report an invalid expression in braces, unless the expression
+ // evaluation has been cancelled due to an aborting error, an
+ // interrupt, or an exception.
if (!aborting()) {
if (end != NULL) {
semsg(_(e_invarg2), start);
@@ -1738,17 +1781,17 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
if (lv.ll_tv != NULL) {
if (fdp != NULL) {
fdp->fd_dict = lv.ll_dict;
- fdp->fd_newkey = (char_u *)lv.ll_newkey;
+ fdp->fd_newkey = lv.ll_newkey;
lv.ll_newkey = NULL;
fdp->fd_di = lv.ll_di;
}
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
- name = vim_strsave((char_u *)lv.ll_tv->vval.v_string);
+ name = xstrdup(lv.ll_tv->vval.v_string);
*pp = (char *)end;
} else if (lv.ll_tv->v_type == VAR_PARTIAL
&& lv.ll_tv->vval.v_partial != NULL) {
if (is_luafunc(lv.ll_tv->vval.v_partial) && *end == '.') {
- len = check_luafunc_name((const char *)end + 1, true);
+ len = check_luafunc_name(end + 1, true);
if (len == 0) {
semsg(e_invexpr2, "v:lua");
goto theend;
@@ -1757,7 +1800,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
memcpy(name, end + 1, (size_t)len);
*pp = (char *)end + 1 + len;
} else {
- name = vim_strsave((char_u *)partial_name(lv.ll_tv->vval.v_partial));
+ name = xstrdup(partial_name(lv.ll_tv->vval.v_partial));
*pp = (char *)end;
}
if (partial != NULL) {
@@ -1791,22 +1834,21 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
name = NULL;
}
} else if (!(flags & TFN_NO_DEREF)) {
- len = (int)(end - (char_u *)(*pp));
- name = deref_func_name((const char *)(*pp), &len, partial,
- flags & TFN_NO_AUTOLOAD);
- if (name == (char_u *)(*pp)) {
+ len = (int)(end - *pp);
+ name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
+ if (name == *pp) {
name = NULL;
}
}
if (name != NULL) {
- name = vim_strsave(name);
+ name = xstrdup(name);
*pp = (char *)end;
- if (STRNCMP(name, "<SNR>", 5) == 0) {
+ if (strncmp(name, "<SNR>", 5) == 0) {
// Change "<SNR>" to the byte sequence.
- name[0] = K_SPECIAL;
- name[1] = KS_EXTRA;
+ name[0] = (char)K_SPECIAL;
+ name[1] = (char)KS_EXTRA;
name[2] = KE_SNR;
- memmove(name + 3, name + 5, strlen((char *)name + 5) + 1);
+ memmove(name + 3, name + 5, strlen(name + 5) + 1);
}
goto theend;
}
@@ -1828,7 +1870,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
lv.ll_name += 2;
lv.ll_name_len -= 2;
}
- len = (int)((const char *)end - lv.ll_name);
+ len = (int)(end - lv.ll_name);
}
size_t sid_buf_len = 0;
@@ -1859,7 +1901,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
}
if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) {
- char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len);
+ char *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len);
if (cp != NULL && cp < end) {
semsg(_("E884: Function name cannot contain a colon: %s"), start);
@@ -1869,8 +1911,8 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
name = xmalloc((size_t)len + (size_t)lead + 1);
if (!skip && lead > 0) {
- name[0] = K_SPECIAL;
- name[1] = KS_EXTRA;
+ name[0] = (char)K_SPECIAL;
+ name[1] = (char)KS_EXTRA;
name[2] = KE_SNR;
if (sid_buf_len > 0) { // If it's "<SID>"
memcpy(name + 3, sid_buf, sid_buf_len);
@@ -1885,18 +1927,105 @@ theend:
return name;
}
+/// If the "funcname" starts with "s:" or "<SID>", then expands it to the
+/// current script ID and returns the expanded function name. The caller should
+/// free the returned name. If not called from a script context or the function
+/// name doesn't start with these prefixes, then returns NULL.
+/// This doesn't check whether the script-local function exists or not.
+char *get_scriptlocal_funcname(char *funcname)
+{
+ if (funcname == NULL) {
+ return NULL;
+ }
+
+ if (strncmp(funcname, "s:", 2) != 0
+ && strncmp(funcname, "<SID>", 5) != 0) {
+ // The function name is not a script-local function name
+ return NULL;
+ }
+
+ if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) {
+ emsg(_(e_usingsid));
+ return NULL;
+ }
+
+ char sid_buf[25];
+ // Expand s: and <SID> prefix into <SNR>nr_<name>
+ snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
+ (int64_t)current_sctx.sc_sid);
+ const int off = *funcname == 's' ? 2 : 5;
+ char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1);
+ STRCPY(newname, sid_buf);
+ STRCAT(newname, funcname + off);
+
+ return newname;
+}
+
+/// Call trans_function_name(), except that a lambda is returned as-is.
+/// Returns the name in allocated memory.
+char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
+{
+ char *p = *name;
+ char *saved;
+
+ if (strncmp(p, "<lambda>", 8) == 0) {
+ p += 8;
+ (void)getdigits(&p, false, 0);
+ saved = xstrndup(*name, (size_t)(p - *name));
+ if (fudi != NULL) {
+ CLEAR_POINTER(fudi);
+ }
+ } else {
+ saved = trans_function_name(&p, skip, flags, fudi, NULL);
+ }
+ *name = p;
+ return saved;
+}
+
+#define MAX_FUNC_NESTING 50
+
+/// List functions.
+///
+/// @param regmatch When NULL, all of them.
+/// Otherwise functions matching "regmatch".
+static void list_functions(regmatch_T *regmatch)
+{
+ const int changed = func_hashtab.ht_changed;
+ size_t todo = func_hashtab.ht_used;
+ const hashitem_T *const ht_array = func_hashtab.ht_array;
+
+ for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ ufunc_T *fp = HI2UF(hi);
+ todo--;
+ if ((fp->uf_flags & FC_DEAD) == 0
+ && (regmatch == NULL
+ ? (!message_filtered((char *)fp->uf_name)
+ && !func_name_refcount(fp->uf_name))
+ : (!isdigit((uint8_t)(*fp->uf_name))
+ && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) {
+ list_func_head(fp, false, false);
+ if (changed != func_hashtab.ht_changed) {
+ emsg(_("E454: function list was modified"));
+ return;
+ }
+ }
+ }
+ }
+}
+
/// ":function"
void ex_function(exarg_T *eap)
{
- char_u *theline;
- char_u *line_to_free = NULL;
- char_u c;
+ char *theline;
+ char *line_to_free = NULL;
+ char c;
int saved_did_emsg;
bool saved_wait_return = need_wait_return;
- char_u *name = NULL;
- char_u *p;
- char_u *arg;
- char_u *line_arg = NULL;
+ char *name = NULL;
+ char *p;
+ char *arg;
+ char *line_arg = NULL;
garray_T newargs;
garray_T default_args;
garray_T newlines;
@@ -1911,44 +2040,27 @@ void ex_function(exarg_T *eap)
static int func_nr = 0; // number for nameless function
int paren;
hashtab_T *ht;
- int todo;
hashitem_T *hi;
linenr_T sourcing_lnum_off;
linenr_T sourcing_lnum_top;
bool is_heredoc = false;
- char_u *skip_until = NULL;
- char_u *heredoc_trimmed = NULL;
+ char *skip_until = NULL;
+ char *heredoc_trimmed = NULL;
bool show_block = false;
bool do_concat = true;
- /*
- * ":function" without argument: list functions.
- */
+ // ":function" without argument: list functions.
if (ends_excmd(*eap->arg)) {
if (!eap->skip) {
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (message_filtered(fp->uf_name)) {
- continue;
- }
- if (!func_name_refcount(fp->uf_name)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(NULL);
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = check_nextcmd(eap->arg);
return;
}
- /*
- * ":function /pat": list functions matching pattern.
- */
+ // ":function /pat": list functions matching pattern.
if (*eap->arg == '/') {
- p = skip_regexp((char_u *)eap->arg + 1, '/', true, NULL);
+ p = skip_regexp(eap->arg + 1, '/', true);
if (!eap->skip) {
regmatch_T regmatch;
@@ -1958,25 +2070,14 @@ void ex_function(exarg_T *eap)
*p = c;
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
-
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (!isdigit(*fp->uf_name)
- && vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(&regmatch);
vim_regfree(regmatch.regprog);
}
}
if (*p == '/') {
p++;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
return;
}
@@ -1994,30 +2095,27 @@ void ex_function(exarg_T *eap)
// "fudi.fd_di" set, "fudi.fd_newkey" == NULL
// s:func script-local function name
// g:func global function name, same as "func"
- p = (char_u *)eap->arg;
- name = trans_function_name((char **)&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
- paren = (vim_strchr((char *)p, '(') != NULL);
+ p = eap->arg;
+ name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi);
+ paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
- /*
- * Return on an invalid expression in braces, unless the expression
- * evaluation has been cancelled due to an aborting error, an
- * interrupt, or an exception.
- */
+ // Return on an invalid expression in braces, unless the expression
+ // evaluation has been cancelled due to an aborting error, an
+ // interrupt, or an exception.
if (!aborting()) {
if (fudi.fd_newkey != NULL) {
semsg(_(e_dictkey), fudi.fd_newkey);
}
xfree(fudi.fd_newkey);
return;
- } else {
- eap->skip = TRUE;
}
+ eap->skip = true;
}
// An error in a function call during evaluation of an expression in magic
// braces should not cause the function not to be defined.
saved_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
//
// ":function func" with only function name: list function.
@@ -2026,11 +2124,11 @@ void ex_function(exarg_T *eap)
// - exclude line numbers from function body
//
if (!paren) {
- if (!ends_excmd(*skipwhite((char *)p))) {
+ if (!ends_excmd(*skipwhite(p))) {
semsg(_(e_trailing_arg), p);
goto ret_free;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
@@ -2052,7 +2150,7 @@ void ex_function(exarg_T *eap)
msg_putchar(' ');
}
}
- msg_prt_line((char_u *)FUNCLINE(fp, j), false);
+ msg_prt_line(FUNCLINE(fp, j), false);
ui_flush(); // show a line at a time
os_breakcheck();
}
@@ -2067,24 +2165,22 @@ void ex_function(exarg_T *eap)
goto ret_free;
}
- /*
- * ":function name(arg1, arg2)" Define function.
- */
- p = (char_u *)skipwhite((char *)p);
+ // ":function name(arg1, arg2)" Define function.
+ p = skipwhite(p);
if (*p != '(') {
if (!eap->skip) {
semsg(_("E124: Missing '(': %s"), eap->arg);
goto ret_free;
}
// attempt to continue by skipping some text
- if (vim_strchr((char *)p, '(') != NULL) {
- p = (char_u *)vim_strchr((char *)p, '(');
+ if (vim_strchr(p, '(') != NULL) {
+ p = vim_strchr(p, '(');
}
}
- p = (char_u *)skipwhite((char *)p + 1);
+ p = skipwhite(p + 1);
- ga_init(&newargs, (int)sizeof(char_u *), 3);
- ga_init(&newlines, (int)sizeof(char_u *), 3);
+ ga_init(&newargs, (int)sizeof(char *), 3);
+ ga_init(&newlines, (int)sizeof(char *), 3);
if (!eap->skip) {
// Check the name of the function. Unless it's a dictionary function
@@ -2095,7 +2191,7 @@ void ex_function(exarg_T *eap)
arg = fudi.fd_newkey;
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
- int j = (*arg == K_SPECIAL) ? 3 : 0;
+ int j = ((uint8_t)(*arg) == K_SPECIAL) ? 3 : 0;
while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) {
j++;
}
@@ -2109,7 +2205,7 @@ void ex_function(exarg_T *eap)
}
}
- if (get_function_args((char **)&p, ')', &newargs, &varargs,
+ if (get_function_args(&p, ')', &newargs, &varargs,
&default_args, eap->skip) == FAIL) {
goto errret_2;
}
@@ -2121,23 +2217,22 @@ void ex_function(exarg_T *eap)
// find extra arguments "range", "dict", "abort" and "closure"
for (;;) {
- p = (char_u *)skipwhite((char *)p);
- if (STRNCMP(p, "range", 5) == 0) {
+ p = skipwhite(p);
+ if (strncmp(p, "range", 5) == 0) {
flags |= FC_RANGE;
p += 5;
- } else if (STRNCMP(p, "dict", 4) == 0) {
+ } else if (strncmp(p, "dict", 4) == 0) {
flags |= FC_DICT;
p += 4;
- } else if (STRNCMP(p, "abort", 5) == 0) {
+ } else if (strncmp(p, "abort", 5) == 0) {
flags |= FC_ABORT;
p += 5;
- } else if (STRNCMP(p, "closure", 7) == 0) {
+ } else if (strncmp(p, "closure", 7) == 0) {
flags |= FC_CLOSURE;
p += 7;
if (current_funccal == NULL) {
- emsg_funcname(N_
- ("E932: Closure function should not be at top level: %s"),
- name == NULL ? (char_u *)"" : name);
+ emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
+ name == NULL ? "" : name);
goto erret;
}
} else {
@@ -2153,9 +2248,7 @@ void ex_function(exarg_T *eap)
semsg(_(e_trailing_arg), p);
}
- /*
- * Read the body of the function, until ":endfunction" is found.
- */
+ // Read the body of the function, until ":endfunction" is found.
if (KeyTyped) {
// Check if the function already exists, don't let the user type the
// whole function before telling him it doesn't work! For a script we
@@ -2193,9 +2286,9 @@ void ex_function(exarg_T *eap)
if (line_arg != NULL) {
// Use eap->arg, split up in parts by line breaks.
theline = line_arg;
- p = (char_u *)vim_strchr((char *)theline, '\n');
+ p = vim_strchr(theline, '\n');
if (p == NULL) {
- line_arg += STRLEN(line_arg);
+ line_arg += strlen(line_arg);
} else {
*p = NUL;
line_arg = p + 1;
@@ -2205,7 +2298,7 @@ void ex_function(exarg_T *eap)
if (eap->getline == NULL) {
theline = getcmdline(':', 0L, indent, do_concat);
} else {
- theline = (char_u *)eap->getline(':', eap->cookie, indent, do_concat);
+ theline = eap->getline(':', eap->cookie, indent, do_concat);
}
line_to_free = theline;
}
@@ -2235,18 +2328,18 @@ void ex_function(exarg_T *eap)
// * ":python <<EOF" and "EOF"
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
if (heredoc_trimmed == NULL
- || (is_heredoc && (char_u *)skipwhite((char *)theline) == theline)
- || STRNCMP(theline, heredoc_trimmed,
- STRLEN(heredoc_trimmed)) == 0) {
+ || (is_heredoc && skipwhite(theline) == theline)
+ || strncmp(theline, heredoc_trimmed,
+ strlen(heredoc_trimmed)) == 0) {
if (heredoc_trimmed == NULL) {
p = theline;
} else if (is_heredoc) {
- p = (char_u *)skipwhite((char *)theline) == theline
- ? theline : theline + STRLEN(heredoc_trimmed);
+ p = skipwhite(theline) == theline
+ ? theline : theline + strlen(heredoc_trimmed);
} else {
- p = theline + STRLEN(heredoc_trimmed);
+ p = theline + strlen(heredoc_trimmed);
}
- if (STRCMP(p, skip_until) == 0) {
+ if (strcmp(p, skip_until) == 0) {
XFREE_CLEAR(skip_until);
XFREE_CLEAR(heredoc_trimmed);
do_concat = true;
@@ -2258,27 +2351,26 @@ void ex_function(exarg_T *eap)
for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
// Check for "endfunction".
- if (checkforcmd((char **)&p, "endfunction", 4) && nesting-- == 0) {
+ if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
if (*p == '!') {
p++;
}
- char_u *nextcmd = NULL;
+ char *nextcmd = NULL;
if (*p == '|') {
nextcmd = p + 1;
- } else if (line_arg != NULL && *skipwhite((char *)line_arg) != NUL) {
+ } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
- give_warning2((char_u *)_("W22: Text found after :endfunction: %s"),
- p, true);
+ give_warning2(_("W22: Text found after :endfunction: %s"), p, true);
}
if (nextcmd != NULL) {
// Another command follows. If the line came from "eap" we
// can simply point into it, otherwise we need to change
// "eap->cmdlinep".
- eap->nextcmd = (char *)nextcmd;
+ eap->nextcmd = nextcmd;
if (line_to_free != NULL) {
xfree(*eap->cmdlinep);
- *eap->cmdlinep = (char *)line_to_free;
+ *eap->cmdlinep = line_to_free;
line_to_free = NULL;
}
}
@@ -2287,46 +2379,50 @@ void ex_function(exarg_T *eap)
// Increase indent inside "if", "while", "for" and "try", decrease
// at "end".
- if (indent > 2 && STRNCMP(p, "end", 3) == 0) {
+ if (indent > 2 && strncmp(p, "end", 3) == 0) {
indent -= 2;
- } else if (STRNCMP(p, "if", 2) == 0
- || STRNCMP(p, "wh", 2) == 0
- || STRNCMP(p, "for", 3) == 0
- || STRNCMP(p, "try", 3) == 0) {
+ } else if (strncmp(p, "if", 2) == 0
+ || strncmp(p, "wh", 2) == 0
+ || strncmp(p, "for", 3) == 0
+ || strncmp(p, "try", 3) == 0) {
indent += 2;
}
// Check for defining a function inside this function.
- if (checkforcmd((char **)&p, "function", 2)) {
+ if (checkforcmd(&p, "function", 2)) {
if (*p == '!') {
- p = (char_u *)skipwhite((char *)p + 1);
+ p = skipwhite(p + 1);
}
p += eval_fname_script((const char *)p);
- xfree(trans_function_name((char **)&p, true, 0, NULL, NULL));
- if (*skipwhite((char *)p) == '(') {
- nesting++;
- indent += 2;
+ xfree(trans_function_name(&p, true, 0, NULL, NULL));
+ if (*skipwhite(p) == '(') {
+ if (nesting == MAX_FUNC_NESTING - 1) {
+ emsg(_("E1058: function nesting too deep"));
+ } else {
+ nesting++;
+ indent += 2;
+ }
}
}
// Check for ":append", ":change", ":insert".
- p = (char_u *)skip_range((char *)p, NULL);
+ p = skip_range(p, NULL);
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
&& (!ASCII_ISALPHA(p[1])
|| (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
|| (p[2] == 'a'
- && (STRNCMP(&p[3], "nge", 3) != 0
+ && (strncmp(&p[3], "nge", 3) != 0
|| !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
|| (p[2] == 's')))))) {
- skip_until = vim_strsave((char_u *)".");
+ skip_until = xstrdup(".");
}
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] == '<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
@@ -2343,22 +2439,22 @@ void ex_function(exarg_T *eap)
|| (p[0] == 'm' && p[1] == 'z'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
// ":python <<" continues until a dot, like ":append"
- p = (char_u *)skipwhite((char *)arg + 2);
+ p = skipwhite(arg + 2);
if (*p == NUL) {
- skip_until = vim_strsave((char_u *)".");
+ skip_until = xstrdup(".");
} else {
- skip_until = vim_strsave(p);
+ skip_until = xstrdup(p);
}
}
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = skipwhite(skiptowhite(p));
if (*arg == '[') {
- arg = (char_u *)vim_strchr((char *)arg, ']');
+ arg = vim_strchr(arg, ']');
}
if (arg != NULL) {
- arg = (char_u *)skipwhite((char *)skiptowhite(arg));
+ arg = skipwhite(skiptowhite(arg));
if (arg[0] == '='
&& arg[1] == '<'
&& arg[2] == '<'
@@ -2366,14 +2462,13 @@ void ex_function(exarg_T *eap)
&& p[1] == 'e'
&& (!ASCII_ISALNUM(p[2])
|| (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) {
- p = (char_u *)skipwhite((char *)arg + 3);
- if (STRNCMP(p, "trim", 4) == 0) {
+ p = skipwhite(arg + 3);
+ if (strncmp(p, "trim", 4) == 0) {
// Ignore leading white space.
- p = (char_u *)skipwhite((char *)p + 4);
- heredoc_trimmed =
- vim_strnsave(theline, (size_t)((char_u *)skipwhite((char *)theline) - theline));
+ p = skipwhite(p + 4);
+ heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline));
}
- skip_until = vim_strnsave(p, (size_t)(skiptowhite(p) - p));
+ skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2386,8 +2481,8 @@ void ex_function(exarg_T *eap)
// Copy the line to newly allocated memory. get_one_sourceline()
// allocates 250 bytes per line, this saves 80% on average. The cost
// is an extra alloc/free.
- p = vim_strsave(theline);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
+ p = xstrdup(theline);
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
// Add NULL lines for continuation lines, so that the line count is
// equal to the index in the growarray.
@@ -2407,14 +2502,11 @@ void ex_function(exarg_T *eap)
goto erret;
}
- /*
- * If there are no errors, add the function
- */
+ // If there are no errors, add the function
if (fudi.fd_dict == NULL) {
- v = find_var((const char *)name, STRLEN(name), &ht, false);
+ v = find_var((const char *)name, strlen(name), &ht, false);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
- emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
- name);
+ emsg_funcname(N_("E707: Function name conflicts with variable: %s"), name);
goto erret;
}
@@ -2429,8 +2521,7 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fp->uf_calls > 0) {
- emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"),
- name);
+ emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
goto erret;
}
if (fp->uf_refcount > 1) {
@@ -2441,7 +2532,7 @@ void ex_function(exarg_T *eap)
fp = NULL;
overwrite = true;
} else {
- char_u *exp_name = fp->uf_name_exp;
+ char *exp_name = fp->uf_name_exp;
// redefine existing function, keep the expanded name
XFREE_CLEAR(name);
fp->uf_name_exp = NULL;
@@ -2460,13 +2551,13 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ if (value_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ } else if (value_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
@@ -2474,23 +2565,23 @@ void ex_function(exarg_T *eap)
// Give the function a sequential number. Can only be used with a
// Funcref!
xfree(name);
- sprintf(numbuf, "%d", ++func_nr);
- name = vim_strsave((char_u *)numbuf);
+ sprintf(numbuf, "%d", ++func_nr); // NOLINT(runtime/printf)
+ name = xstrdup(numbuf);
}
if (fp == NULL) {
- if (fudi.fd_dict == NULL && vim_strchr((char *)name, AUTOLOAD_CHAR) != NULL) {
+ if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) {
int slen, plen;
- char_u *scriptname;
+ char *scriptname;
// Check that the autoload name matches the script name.
int j = FAIL;
if (SOURCING_NAME != NULL) {
- scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
- p = (char_u *)vim_strchr((char *)scriptname, '/');
- plen = (int)STRLEN(p);
- slen = (int)STRLEN(SOURCING_NAME);
- if (slen > plen && FNAMECMP(p, SOURCING_NAME + slen - plen) == 0) {
+ scriptname = autoload_name(name, strlen(name));
+ p = vim_strchr(scriptname, '/');
+ plen = (int)strlen(p);
+ slen = (int)strlen(SOURCING_NAME);
+ if (slen > plen && path_fnamecmp(p, SOURCING_NAME + slen - plen) == 0) {
j = OK;
}
xfree(scriptname);
@@ -2502,7 +2593,7 @@ void ex_function(exarg_T *eap)
}
}
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
@@ -2518,7 +2609,7 @@ void ex_function(exarg_T *eap)
tv_clear(&fudi.fd_di->di_tv);
}
fudi.fd_di->di_tv.v_type = VAR_FUNC;
- fudi.fd_di->di_tv.vval.v_string = (char *)vim_strsave(name);
+ fudi.fd_di->di_tv.vval.v_string = xstrdup(name);
// behave like "dict" was used
flags |= FC_DICT;
@@ -2527,8 +2618,8 @@ void ex_function(exarg_T *eap)
// insert the new function in the function list
set_ufunc_name(fp, name);
if (overwrite) {
- hi = hash_find(&func_hashtab, (char *)name);
- hi->hi_key = UF2HIKEY(fp);
+ hi = hash_find(&func_hashtab, name);
+ hi->hi_key = (char *)UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
goto erret;
@@ -2584,8 +2675,8 @@ int eval_fname_script(const char *const p)
// Use mb_strnicmp() because in Turkish comparing the "I" may not work with
// the standard library function.
if (p[0] == '<'
- && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0
- || mb_strnicmp((char_u *)p + 1, (char_u *)"SNR>", 4) == 0)) {
+ && (mb_strnicmp(p + 1, "SID>", 4) == 0
+ || mb_strnicmp(p + 1, "SNR>", 4) == 0)) {
return 5;
}
if (p[0] == 's' && p[1] == ':') {
@@ -2599,7 +2690,7 @@ bool translated_function_exists(const char *name)
if (builtin_function(name, -1)) {
return find_internal_func((char *)name) != NULL;
}
- return find_func((const char_u *)name) != NULL;
+ return find_func(name) != NULL;
}
/// Check whether function with the given name exists
@@ -2610,15 +2701,15 @@ bool translated_function_exists(const char *name)
/// @return true if it exists, false otherwise.
bool function_exists(const char *const name, bool no_deref)
{
- const char_u *nm = (const char_u *)name;
+ const char *nm = name;
bool n = false;
int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref) {
flag |= TFN_NO_DEREF;
}
- char *const p = (char *)trans_function_name((char **)&nm, false, flag, NULL, NULL);
- nm = (char_u *)skipwhite((char *)nm);
+ char *const p = trans_function_name((char **)&nm, false, flag, NULL, NULL);
+ nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
// "funcname(...", not "funcname!...".
@@ -2634,15 +2725,17 @@ bool function_exists(const char *const name, bool no_deref)
char *get_user_func_name(expand_T *xp, int idx)
{
static size_t done;
+ static int changed;
static hashitem_T *hi;
ufunc_T *fp;
if (idx == 0) {
done = 0;
hi = func_hashtab.ht_array;
+ changed = func_hashtab.ht_changed;
}
assert(hi);
- if (done < func_hashtab.ht_used) {
+ if (changed == func_hashtab.ht_changed && done < func_hashtab.ht_used) {
if (done++ > 0) {
hi++;
}
@@ -2652,11 +2745,11 @@ char *get_user_func_name(expand_T *xp, int idx)
fp = HI2UF(hi);
if ((fp->uf_flags & FC_DICT)
- || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ || strncmp(fp->uf_name, "<lambda>", 8) == 0) {
return ""; // don't show dict and lambda functions
}
- if (STRLEN(fp->uf_name) + 4 >= IOSIZE) {
+ if (strlen(fp->uf_name) + 4 >= IOSIZE) {
return (char *)fp->uf_name; // Prevent overflow.
}
@@ -2667,7 +2760,7 @@ char *get_user_func_name(expand_T *xp, int idx)
STRCAT(IObuff, ")");
}
}
- return (char *)IObuff;
+ return IObuff;
}
return NULL;
}
@@ -2676,12 +2769,12 @@ char *get_user_func_name(expand_T *xp, int idx)
void ex_delfunction(exarg_T *eap)
{
ufunc_T *fp = NULL;
- char_u *p;
- char_u *name;
+ char *p;
+ char *name;
funcdict_T fudi;
- p = (char_u *)eap->arg;
- name = trans_function_name((char **)&p, eap->skip, 0, &fudi, NULL);
+ p = eap->arg;
+ name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
xfree(fudi.fd_newkey);
if (name == NULL) {
if (fudi.fd_dict != NULL && !eap->skip) {
@@ -2689,16 +2782,23 @@ void ex_delfunction(exarg_T *eap)
}
return;
}
- if (!ends_excmd(*skipwhite((char *)p))) {
+ if (!ends_excmd(*skipwhite(p))) {
xfree(name);
semsg(_(e_trailing_arg), p);
return;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
+ if (isdigit((uint8_t)(*name)) && fudi.fd_dict == NULL) {
+ if (!eap->skip) {
+ semsg(_(e_invarg2), eap->arg);
+ }
+ xfree(name);
+ return;
+ }
if (!eap->skip) {
fp = find_func(name);
}
@@ -2749,7 +2849,7 @@ void ex_delfunction(exarg_T *eap)
/// Unreference a Function: decrement the reference count and free it when it
/// becomes zero.
-void func_unref(char_u *name)
+void func_unref(char *name)
{
ufunc_T *fp = NULL;
@@ -2758,7 +2858,7 @@ void func_unref(char_u *name)
}
fp = find_func(name);
- if (fp == NULL && isdigit(*name)) {
+ if (fp == NULL && isdigit((uint8_t)(*name))) {
#ifdef EXITFREE
if (!entered_free_all_mem) {
internal_error("func_unref()");
@@ -2791,7 +2891,7 @@ void func_ptr_unref(ufunc_T *fp)
}
/// Count a reference to a Function.
-void func_ref(char_u *name)
+void func_ref(char *name)
{
ufunc_T *fp;
@@ -2801,7 +2901,7 @@ void func_ref(char_u *name)
fp = find_func(name);
if (fp != NULL) {
(fp->uf_refcount)++;
- } else if (isdigit(*name)) {
+ } else if (isdigit((uint8_t)(*name))) {
// Only give an error for a numbered function.
// Fail silently, when named or lambda function isn't found.
internal_error("func_ref()");
@@ -2845,9 +2945,9 @@ static int can_free_funccal(funccall_T *fc, int copyID)
/// ":return [expr]"
void ex_return(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
typval_T rettv;
- int returning = FALSE;
+ int returning = false;
if (current_funccal == NULL) {
emsg(_("E133: :return not inside a function"));
@@ -2860,7 +2960,7 @@ void ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0((char *)arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2883,7 +2983,7 @@ void ex_return(exarg_T *eap)
if (returning) {
eap->nextcmd = NULL;
} else if (eap->nextcmd == NULL) { // no argument
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
if (eap->skip) {
@@ -2891,15 +2991,13 @@ void ex_return(exarg_T *eap)
}
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// ":1,25call func(arg1, arg2)" function call.
void ex_call(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *startarg;
- char_u *name;
- char_u *tofree;
+ char *arg = eap->arg;
+ char *startarg;
+ char *name;
+ char *tofree;
int len;
typval_T rettv;
linenr_T lnum;
@@ -2920,7 +3018,7 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial);
+ tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
// Still need to give an error message for missing key.
semsg(_(e_dictkey), fudi.fd_newkey);
@@ -2939,13 +3037,12 @@ void ex_call(exarg_T *eap)
// If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
// contents. For VAR_PARTIAL get its partial, unless we already have one
// from trans_function_name().
- len = (int)STRLEN(tofree);
- name = deref_func_name((const char *)tofree, &len,
- partial != NULL ? NULL : &partial, false);
+ len = (int)strlen(tofree);
+ name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
- startarg = (char_u *)skipwhite((char *)arg);
+ startarg = skipwhite(arg);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') {
@@ -2969,13 +3066,13 @@ void ex_call(exarg_T *eap)
arg = startarg;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = eap->line1;
- funcexe.lastline = eap->line2;
- funcexe.doesrange = &doesrange;
- funcexe.evaluate = true;
- funcexe.partial = partial;
- funcexe.selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, (char **)&arg, &funcexe) == FAIL) {
+ funcexe.fe_firstline = eap->line1;
+ funcexe.fe_lastline = eap->line2;
+ funcexe.fe_doesrange = &doesrange;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = fudi.fd_dict;
+ if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
failed = true;
break;
}
@@ -3004,7 +3101,7 @@ void ex_call(exarg_T *eap)
// When inside :try we need to check for following "| catch" or "| endtry".
// Not when there was an error, but do check if an exception was thrown.
- if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) {
+ if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0)) {
// Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
if (!failed && !aborting()) {
@@ -3012,7 +3109,7 @@ void ex_call(exarg_T *eap)
semsg(_(e_trailing_arg), arg);
}
} else {
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
}
@@ -3029,8 +3126,8 @@ end:
/// @param is_cmd set when called due to a ":return" command.
/// @param rettv may point to a typval_T with the return rettv.
///
-/// @return TRUE when the return can be carried out,
-/// FALSE when the return gets pending.
+/// @return true when the return can be carried out,
+/// false when the return gets pending.
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
{
int idx;
@@ -3080,7 +3177,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
}
report_make_pending(CSTP_RETURN, rettv);
} else {
- current_funccal->returned = TRUE;
+ current_funccal->returned = true;
// If the return is carried out now, store the return value. For
// a return immediately after reanimation, the value is already
@@ -3099,25 +3196,25 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
/// Generate a return command for producing the value of "rettv". The result
/// is an allocated string. Used by report_pending() for verbose messages.
-char_u *get_return_cmd(void *rettv)
+char *get_return_cmd(void *rettv)
{
- char_u *s = NULL;
- char_u *tofree = NULL;
+ char *s = NULL;
+ char *tofree = NULL;
if (rettv != NULL) {
- tofree = s = (char_u *)encode_tv2echo((typval_T *)rettv, NULL);
+ tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
}
if (s == NULL) {
- s = (char_u *)"";
+ s = "";
}
STRCPY(IObuff, ":return ");
- STRLCPY(IObuff + 8, s, IOSIZE - 8);
- if (STRLEN(s) + 8 >= IOSIZE) {
+ xstrlcpy(IObuff + 8, s, IOSIZE - 8);
+ if (strlen(s) + 8 >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
}
xfree(tofree);
- return vim_strsave(IObuff);
+ return xstrdup(IObuff);
}
/// Get next function line.
@@ -3128,12 +3225,12 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
{
funccall_T *fcp = (funccall_T *)cookie;
ufunc_T *fp = fcp->func;
- char_u *retval;
+ char *retval;
garray_T *gap; // growarray with function lines
// If breakpoints have been added/deleted need to check for it.
if (fcp->dbg_tick != debug_tick) {
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -3153,7 +3250,7 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
if (fcp->linenr >= gap->ga_len) {
retval = NULL;
} else {
- retval = (char_u *)xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
+ retval = xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
SOURCING_LNUM = fcp->linenr;
if (do_profiling == PROF_YES) {
func_line_start(cookie);
@@ -3163,16 +3260,16 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
// Did we encounter a breakpoint?
if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) {
- dbg_breakpoint(fp->uf_name, SOURCING_LNUM);
+ dbg_breakpoint((char *)fp->uf_name, SOURCING_LNUM);
// Find next breakpoint.
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
- return (char *)retval;
+ return retval;
}
-/// @return TRUE if the currently active function should be ended, because a
+/// @return true if the currently active function should be ended, because a
/// return was encountered or an error occurred. Used inside a ":while".
int func_has_ended(void *cookie)
{
@@ -3184,7 +3281,7 @@ int func_has_ended(void *cookie)
|| fcp->returned;
}
-/// @return TRUE if cookie indicates a function which "abort"s on errors.
+/// @return true if cookie indicates a function which "abort"s on errors.
int func_has_abort(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT;
@@ -3194,17 +3291,17 @@ int func_has_abort(void *cookie)
/// Changes "rettv" in-place.
void make_partial(dict_T *const selfdict, typval_T *const rettv)
{
- char_u *fname;
- char_u *tofree = NULL;
+ char *fname;
+ char *tofree = NULL;
ufunc_T *fp;
- char_u fname_buf[FLEN_FIXED + 1];
+ char fname_buf[FLEN_FIXED + 1];
int error;
if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) {
fp = rettv->vval.v_partial->pt_func;
} else {
fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
- ? (char_u *)rettv->vval.v_string
+ ? rettv->vval.v_string
: rettv->vval.v_partial->pt_name;
// Translate "s:func" to the stored function name.
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
@@ -3221,7 +3318,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
pt->pt_auto = true;
if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) {
// Just a function: Take over the function name and use selfdict.
- pt->pt_name = (char_u *)rettv->vval.v_string;
+ pt->pt_name = rettv->vval.v_string;
} else {
partial_T *ret_pt = rettv->vval.v_partial;
int i;
@@ -3230,7 +3327,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
// args. Can't take over name or args, the partial might
// be referenced elsewhere.
if (ret_pt->pt_name != NULL) {
- pt->pt_name = vim_strsave(ret_pt->pt_name);
+ pt->pt_name = xstrdup(ret_pt->pt_name);
func_ref(pt->pt_name);
} else {
pt->pt_func = ret_pt->pt_func;
@@ -3252,7 +3349,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
}
/// @return the name of the executed function.
-char_u *func_name(void *cookie)
+char *func_name(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_name;
}
@@ -3275,7 +3372,7 @@ int func_level(void *cookie)
return ((funccall_T *)cookie)->level;
}
-/// @return TRUE when a function was ended by a ":return" command.
+/// @return true when a function was ended by a ":return" command.
int current_func_returned(void)
{
return current_funccal->returned;
@@ -3539,14 +3636,14 @@ bool set_ref_in_func_args(int copyID)
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
///
/// @return true if setting references failed somehow.
-bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
+bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
{
ufunc_T *fp = fp_in;
funccall_T *fc;
- int error = ERROR_NONE;
- char_u fname_buf[FLEN_FIXED + 1];
- char_u *tofree = NULL;
- char_u *fname;
+ int error = FCERR_NONE;
+ char fname_buf[FLEN_FIXED + 1];
+ char *tofree = NULL;
+ char *fname;
bool abort = false;
if (name == NULL && fp_in == NULL) {
return false;
@@ -3565,20 +3662,18 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
return abort;
}
-/// Registers a C extension user function.
-char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
+/// Registers a luaref as a lambda.
+char *register_luafunc(LuaRef ref)
{
- char_u *name = get_lambda_name();
- ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ char *name = get_lambda_name();
+ ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
fp->uf_refcount = 1;
fp->uf_varargs = true;
- fp->uf_flags = FC_CFUNC;
+ fp->uf_flags = FC_LUAREF;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
- fp->uf_cb = cb;
- fp->uf_cb_free = cb_free;
- fp->uf_cb_state = state;
+ fp->uf_luaref = ref;
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));