summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-07-22 21:50:18 +0200
committerBram Moolenaar <Bram@vim.org>2016-07-22 21:50:18 +0200
commitdf48fb456fb6bf63d94cad9b302ff01d8ee8d311 (patch)
treee40ac7facad1b205bf5990a535c8c1686fbeb05b /src
parent36edf0685c8b55ee3ce709058d83ada8027fec1e (diff)
patch 7.4.2090v7.4.2090
Problem: Using submatch() in a lambda passed to substitute() is verbose. Solution: Use a static list and pass it as an optional argument to the function. Fix memory leak.
Diffstat (limited to 'src')
-rw-r--r--src/channel.c8
-rw-r--r--src/eval.c11
-rw-r--r--src/evalfunc.c2
-rw-r--r--src/ex_cmds2.c2
-rw-r--r--src/list.c31
-rw-r--r--src/proto/list.pro1
-rw-r--r--src/proto/userfunc.pro2
-rw-r--r--src/regexp.c77
-rw-r--r--src/structs.h8
-rw-r--r--src/testdir/test_expr.vim19
-rw-r--r--src/userfunc.c14
-rw-r--r--src/version.c2
12 files changed, 152 insertions, 25 deletions
diff --git a/src/channel.c b/src/channel.c
index ae5bdf220b..722dcb8de9 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1533,8 +1533,8 @@ invoke_callback(channel_T *channel, char_u *callback, partial_T *partial,
argv[0].v_type = VAR_CHANNEL;
argv[0].vval.v_channel = channel;
- call_func(callback, (int)STRLEN(callback),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL);
+ call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, TRUE, partial, NULL);
clear_tv(&rettv);
channel_need_redraw = TRUE;
}
@@ -2695,7 +2695,7 @@ channel_close(channel_T *channel, int invoke_close_cb)
argv[0].v_type = VAR_CHANNEL;
argv[0].vval.v_channel = channel;
call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb),
- &rettv, 1, argv, 0L, 0L, &dummy, TRUE,
+ &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
channel->ch_close_partial, NULL);
clear_tv(&rettv);
channel_need_redraw = TRUE;
@@ -4758,7 +4758,7 @@ job_status(job_T *job)
argv[1].v_type = VAR_NUMBER;
argv[1].vval.v_number = job->jv_exitval;
call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
+ &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
job->jv_exit_partial, NULL);
clear_tv(&rettv);
--job->jv_refcount;
diff --git a/src/eval.c b/src/eval.c
index 2ba90f94d6..53c188baef 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -988,7 +988,7 @@ call_vim_function(
}
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
- ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
+ ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, TRUE, NULL, NULL);
if (safe)
@@ -9930,8 +9930,8 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
if (expr->v_type == VAR_FUNC)
{
s = expr->vval.v_string;
- if (call_func(s, (int)STRLEN(s),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
goto theend;
}
else if (expr->v_type == VAR_PARTIAL)
@@ -9939,9 +9939,8 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
partial_T *partial = expr->vval.v_partial;
s = partial->pt_name;
- if (call_func(s, (int)STRLEN(s),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL)
- == FAIL)
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
goto theend;
}
else
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 54b5a53bb9..61dce74fd3 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -10101,7 +10101,7 @@ item_compare2(const void *s1, const void *s2)
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
res = call_func(func_name, (int)STRLEN(func_name),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
+ &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
partial, sortinfo->item_compare_selfdict);
clear_tv(&argv[0]);
clear_tv(&argv[1]);
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index 2b2d63549f..adc53529b4 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -1163,7 +1163,7 @@ timer_callback(timer_T *timer)
argv[1].v_type = VAR_UNKNOWN;
call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
- &rettv, 1, argv, 0L, 0L, &dummy, TRUE,
+ &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
timer->tr_partial, NULL);
clear_tv(&rettv);
}
diff --git a/src/list.c b/src/list.c
index 13f920039c..9e0e26fafd 100644
--- a/src/list.c
+++ b/src/list.c
@@ -924,4 +924,35 @@ write_list(FILE *fd, list_T *list, int binary)
return ret;
}
+/*
+ * Initialize a static list with 10 items.
+ */
+ void
+init_static_list(staticList10_T *sl)
+{
+ list_T *l = &sl->sl_list;
+ int i;
+
+ memset(sl, 0, sizeof(staticList10_T));
+ l->lv_first = &sl->sl_items[0];
+ l->lv_last = &sl->sl_items[9];
+ l->lv_refcount = DO_NOT_FREE_CNT;
+ l->lv_lock = VAR_FIXED;
+ sl->sl_list.lv_len = 10;
+
+ for (i = 0; i < 10; ++i)
+ {
+ listitem_T *li = &sl->sl_items[i];
+
+ if (i == 0)
+ li->li_prev = NULL;
+ else
+ li->li_prev = li - 1;
+ if (i == 9)
+ li->li_next = NULL;
+ else
+ li->li_next = li + 1;
+ }
+}
+
#endif /* defined(FEAT_EVAL) */
diff --git a/src/proto/list.pro b/src/proto/list.pro
index 9849379597..56f0ddc4fb 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -32,4 +32,5 @@ char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID);
int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
int write_list(FILE *fd, list_T *list, int binary);
+void init_static_list(staticList10_T *sl);
/* vim: set ft=c : */
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index b354197a32..feacd4c004 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -5,7 +5,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_au
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict);
void free_all_functions(void);
int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
-int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
+int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
void ex_function(exarg_T *eap);
int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name);
diff --git a/src/regexp.c b/src/regexp.c
index 1a7775894a..6b23cfbec9 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -7290,6 +7290,50 @@ static int submatch_line_lbr;
#endif
#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO)
+
+/*
+ * Put the submatches in "argv[0]" which is a list passed into call_func() by
+ * vim_regsub_both().
+ */
+ static int
+fill_submatch_list(int argc UNUSED, typval_T *argv, int argcount)
+{
+ listitem_T *li;
+ int i;
+ char_u *s;
+
+ if (argcount == 0)
+ /* called function doesn't take an argument */
+ return 0;
+
+ /* Relies on sl_list to be the first item in staticList10_T. */
+ init_static_list((staticList10_T *)(argv->vval.v_list));
+
+ /* There are always 10 list items in staticList10_T. */
+ li = argv->vval.v_list->lv_first;
+ for (i = 0; i < 10; ++i)
+ {
+ s = submatch_match->startp[i];
+ if (s == NULL || submatch_match->endp[i] == NULL)
+ s = NULL;
+ else
+ s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
+ li->li_tv.v_type = VAR_STRING;
+ li->li_tv.vval.v_string = s;
+ li = li->li_next;
+ }
+ return 1;
+}
+
+ static void
+clear_submatch_list(staticList10_T *sl)
+{
+ int i;
+
+ for (i = 0; i < 10; ++i)
+ vim_free(sl->sl_items[i].li_tv.vval.v_string);
+}
+
/*
* vim_regsub() - perform substitutions after a vim_regexec() or
* vim_regexec_multi() match.
@@ -7427,10 +7471,11 @@ vim_regsub_both(
if (expr != NULL)
{
- typval_T argv[1];
+ typval_T argv[2];
int dummy;
char_u buf[NUMBUFLEN];
typval_T rettv;
+ staticList10_T matchList;
rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL;
@@ -7438,23 +7483,35 @@ vim_regsub_both(
{
/* can't do this recursively */
}
- else if (expr->v_type == VAR_FUNC)
+ else
{
- s = expr->vval.v_string;
- call_func(s, (int)STRLEN(s), &rettv, 0, argv,
+ argv[0].v_type = VAR_LIST;
+ argv[0].vval.v_list = &matchList.sl_list;
+ matchList.sl_list.lv_len = 0;
+ if (expr->v_type == VAR_FUNC)
+ {
+ s = expr->vval.v_string;
+ call_func(s, (int)STRLEN(s), &rettv,
+ 1, argv, fill_submatch_list,
0L, 0L, &dummy, TRUE, NULL, NULL);
- }
- else if (expr->v_type == VAR_PARTIAL)
- {
- partial_T *partial = expr->vval.v_partial;
+ }
+ else if (expr->v_type == VAR_PARTIAL)
+ {
+ partial_T *partial = expr->vval.v_partial;
- s = partial->pt_name;
- call_func(s, (int)STRLEN(s), &rettv, 0, argv,
+ s = partial->pt_name;
+ call_func(s, (int)STRLEN(s), &rettv,
+ 1, argv, fill_submatch_list,
0L, 0L, &dummy, TRUE, partial, NULL);
+ }
+ if (matchList.sl_list.lv_len > 0)
+ /* fill_submatch_list() was called */
+ clear_submatch_list(&matchList);
}
eval_result = get_tv_string_buf_chk(&rettv, buf);
if (eval_result != NULL)
eval_result = vim_strsave(eval_result);
+ clear_tv(&rettv);
}
else
eval_result = eval_to_string(source + 2, NULL, TRUE);
diff --git a/src/structs.h b/src/structs.h
index 191fa1f923..7375c7c2b8 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1245,6 +1245,14 @@ struct listvar_S
};
/*
+ * Static list with 10 items. Use init_static_list() to initialize.
+ */
+typedef struct {
+ list_T sl_list; /* must be first */
+ listitem_T sl_items[10];
+} staticList10_T;
+
+/*
* Structure to hold an item of a Dictionary.
* Also used for a variable.
* The key is copied into "di_key" to avoid an extra alloc/free for it.
diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim
index 235dd8d65a..8db4d8e5f3 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -153,3 +153,22 @@ func Test_substitute_expr()
endfunc
call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
endfunc
+
+func Test_substitute_expr_arg()
+ call assert_equal('123456789-123456789=', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456-123456=789', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456789-123456789x=', substitute('123456789',
+ \ '\(.\)\(.\)\(.*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
diff --git a/src/userfunc.c b/src/userfunc.c
index ffbbc2d21f..af1863f328 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -480,7 +480,7 @@ get_func_tv(
&argvars[i];
}
- ret = call_func(name, len, rettv, argcount, argvars,
+ ret = call_func(name, len, rettv, argcount, argvars, NULL,
firstline, lastline, doesrange, evaluate, partial, selfdict);
funcargs.ga_len -= i;
@@ -1139,7 +1139,7 @@ func_call(
}
if (item == NULL)
- r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
+ r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, TRUE, partial, selfdict);
@@ -1152,6 +1152,11 @@ func_call(
/*
* Call a function with its resolved parameters
+ *
+ * "argv_func", when not NULL, can be used to fill in arguments only when the
+ * invoked function uses them. It is called like this:
+ * new_argcount = argv_func(current_argcount, argv, called_func_argcount)
+ *
* Return FAIL when the function can't be called, OK otherwise.
* Also returns OK when an error was encountered while executing the function.
*/
@@ -1163,6 +1168,8 @@ call_func(
int argcount_in, /* number of "argvars" */
typval_T *argvars_in, /* vars for arguments, must have "argcount"
PLUS ONE elements! */
+ int (* argv_func)(int, typval_T *, int),
+ /* function to fill in argvars */
linenr_T firstline, /* first line of range */
linenr_T lastline, /* last line of range */
int *doesrange, /* return: function handled range */
@@ -1254,6 +1261,9 @@ call_func(
if (fp != NULL)
{
+ if (argv_func != NULL)
+ argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
+
if (fp->uf_flags & FC_RANGE)
*doesrange = TRUE;
if (argcount < fp->uf_args.ga_len)
diff --git a/src/version.c b/src/version.c
index dccf2ce3dc..e42229eea0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2090,
+/**/
2089,
/**/
2088,