summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-09-26 23:08:54 +0200
committerBram Moolenaar <Bram@vim.org>2019-09-26 23:08:54 +0200
commitd2842ea60bd608b7f9ec93c77d3f36a8e3bf5fe9 (patch)
treed10208c36706ae29c65fba43fbf02aeced49eccd /src
parentd2c1fb476d5816db129eb428ffef6a81027eb13a (diff)
patch 8.1.2080: the terminal API is limited and can't be disabledv8.1.2080
Problem: The terminal API is limited and can't be disabled. Solution: Add term_setapi() to set the function prefix. (Ozaki Kiichi, closes #2907)
Diffstat (limited to 'src')
-rw-r--r--src/channel.c8
-rw-r--r--src/evalfunc.c1
-rw-r--r--src/proto/terminal.pro1
-rw-r--r--src/structs.h3
-rw-r--r--src/terminal.c90
-rw-r--r--src/testdir/term_util.vim9
-rw-r--r--src/testdir/test_terminal.vim101
-rw-r--r--src/version.c2
8 files changed, 189 insertions, 26 deletions
diff --git a/src/channel.c b/src/channel.c
index 34ee02a662..e42c9575f9 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -5144,6 +5144,14 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
memcpy(opt->jo_ansi_colors, rgb, sizeof(rgb));
}
# endif
+ else if (STRCMP(hi->hi_key, "term_api") == 0)
+ {
+ if (!(supported2 & JO2_TERM_API))
+ break;
+ opt->jo_set2 |= JO2_TERM_API;
+ opt->jo_term_api = tv_get_string_buf_chk(item,
+ opt->jo_term_api_buf);
+ }
#endif
else if (STRCMP(hi->hi_key, "env") == 0)
{
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 04e1313916..c9d9287548 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -787,6 +787,7 @@ static funcentry_T global_functions[] =
# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
{"term_setansicolors", 2, 2, FEARG_1, f_term_setansicolors},
# endif
+ {"term_setapi", 2, 2, FEARG_1, f_term_setapi},
{"term_setkill", 2, 2, FEARG_1, f_term_setkill},
{"term_setrestore", 2, 2, FEARG_1, f_term_setrestore},
{"term_setsize", 3, 3, FEARG_1, f_term_setsize},
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 0527aa88c9..52bb1b8d4a 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -50,6 +50,7 @@ void f_term_scrape(typval_T *argvars, typval_T *rettv);
void f_term_sendkeys(typval_T *argvars, typval_T *rettv);
void f_term_getansicolors(typval_T *argvars, typval_T *rettv);
void f_term_setansicolors(typval_T *argvars, typval_T *rettv);
+void f_term_setapi(typval_T *argvars, typval_T *rettv);
void f_term_setrestore(typval_T *argvars, typval_T *rettv);
void f_term_setkill(typval_T *argvars, typval_T *rettv);
void f_term_start(typval_T *argvars, typval_T *rettv);
diff --git a/src/structs.h b/src/structs.h
index e3c73cddda..9d2ec16390 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1938,6 +1938,7 @@ struct channel_S {
#define JO2_ANSI_COLORS 0x8000 // "ansi_colors"
#define JO2_TTY_TYPE 0x10000 // "tty_type"
#define JO2_BUFNR 0x20000 // "bufnr"
+#define JO2_TERM_API 0x40000 // "term_api"
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
#define JO_CB_ALL \
@@ -2007,6 +2008,8 @@ typedef struct
long_u jo_ansi_colors[16];
# endif
int jo_tty_type; // first character of "tty_type"
+ char_u *jo_term_api;
+ char_u jo_term_api_buf[NUMBUFLEN];
#endif
} jobopt_T;
diff --git a/src/terminal.c b/src/terminal.c
index f1d89ab9e4..4f2b8bb25c 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -109,6 +109,7 @@ struct terminal_S {
#define TL_FINISH_OPEN 'o' /* ++open */
char_u *tl_opencmd;
char_u *tl_eof_chars;
+ char_u *tl_api; // prefix for terminal API function
char_u *tl_arg0_cmd; // To format the status bar
@@ -641,6 +642,11 @@ term_start(
term->tl_kill = vim_strnsave(opt->jo_term_kill, p - opt->jo_term_kill);
}
+ if (opt->jo_term_api != NULL)
+ term->tl_api = vim_strsave(opt->jo_term_api);
+ else
+ term->tl_api = vim_strsave((char_u *)"Tapi_");
+
/* System dependent: setup the vterm and maybe start the job in it. */
if (argv == NULL
&& argvar->v_type == VAR_STRING
@@ -708,44 +714,58 @@ ex_terminal(exarg_T *eap)
cmd += 2;
p = skiptowhite(cmd);
ep = vim_strchr(cmd, '=');
- if (ep != NULL && ep < p)
- p = ep;
+ if (ep != NULL)
+ {
+ if (ep < p)
+ p = ep;
+ else
+ ep = NULL;
+ }
- if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0)
+# define OPTARG_HAS(name) ((int)(p - cmd) == sizeof(name) - 1 \
+ && STRNICMP(cmd, name, sizeof(name) - 1) == 0)
+ if (OPTARG_HAS("close"))
opt.jo_term_finish = 'c';
- else if ((int)(p - cmd) == 7 && STRNICMP(cmd, "noclose", 7) == 0)
+ else if (OPTARG_HAS("noclose"))
opt.jo_term_finish = 'n';
- else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0)
+ else if (OPTARG_HAS("open"))
opt.jo_term_finish = 'o';
- else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "curwin", 6) == 0)
+ else if (OPTARG_HAS("curwin"))
opt.jo_curwin = 1;
- else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0)
+ else if (OPTARG_HAS("hidden"))
opt.jo_hidden = 1;
- else if ((int)(p - cmd) == 9 && STRNICMP(cmd, "norestore", 9) == 0)
+ else if (OPTARG_HAS("norestore"))
opt.jo_term_norestore = 1;
- else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "kill", 4) == 0
- && ep != NULL)
+ else if (OPTARG_HAS("kill") && ep != NULL)
{
opt.jo_set2 |= JO2_TERM_KILL;
opt.jo_term_kill = ep + 1;
p = skiptowhite(cmd);
}
- else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0
- && ep != NULL && isdigit(ep[1]))
+ else if (OPTARG_HAS("api"))
+ {
+ opt.jo_set2 |= JO2_TERM_API;
+ if (ep != NULL)
+ {
+ opt.jo_term_api = ep + 1;
+ p = skiptowhite(cmd);
+ }
+ else
+ opt.jo_term_api = NULL;
+ }
+ else if (OPTARG_HAS("rows") && ep != NULL && isdigit(ep[1]))
{
opt.jo_set2 |= JO2_TERM_ROWS;
opt.jo_term_rows = atoi((char *)ep + 1);
p = skiptowhite(cmd);
}
- else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "cols", 4) == 0
- && ep != NULL && isdigit(ep[1]))
+ else if (OPTARG_HAS("cols") && ep != NULL && isdigit(ep[1]))
{
opt.jo_set2 |= JO2_TERM_COLS;
opt.jo_term_cols = atoi((char *)ep + 1);
p = skiptowhite(cmd);
}
- else if ((int)(p - cmd) == 3 && STRNICMP(cmd, "eof", 3) == 0
- && ep != NULL)
+ else if (OPTARG_HAS("eof") && ep != NULL)
{
char_u *buf = NULL;
char_u *keys;
@@ -785,6 +805,7 @@ ex_terminal(exarg_T *eap)
semsg(_("E181: Invalid attribute: %s"), cmd);
goto theend;
}
+# undef OPTARG_HAS
cmd = skipwhite(p);
}
if (*cmd == NUL)
@@ -933,6 +954,7 @@ free_unused_terminals()
free_scrollback(term);
term_free_vterm(term);
+ vim_free(term->tl_api);
vim_free(term->tl_title);
#ifdef FEAT_SESSION
vim_free(term->tl_command);
@@ -3770,6 +3792,15 @@ handle_drop_command(listitem_T *item)
}
/*
+ * Return TRUE if "func" starts with "pat" and "pat" isn't empty.
+ */
+ static int
+is_permitted_term_api(char_u *func, char_u *pat)
+{
+ return pat != NULL && *pat != NUL && STRNICMP(func, pat, STRLEN(pat)) == 0;
+}
+
+/*
* Handles a function call from the job running in a terminal.
* "item" is the function name, "item->li_next" has the arguments.
*/
@@ -3788,9 +3819,9 @@ handle_call_command(term_T *term, channel_T *channel, listitem_T *item)
}
func = tv_get_string(&item->li_tv);
- if (STRNCMP(func, "Tapi_", 5) != 0)
+ if (!is_permitted_term_api(func, term->tl_api))
{
- ch_log(channel, "Invalid function name: %s", func);
+ ch_log(channel, "Unpermitted function: %s", func);
return;
}
@@ -5546,6 +5577,27 @@ f_term_setansicolors(typval_T *argvars, typval_T *rettv UNUSED)
#endif
/*
+ * "term_setapi(buf, api)" function
+ */
+ void
+f_term_setapi(typval_T *argvars, typval_T *rettv UNUSED)
+{
+ buf_T *buf = term_get_buf(argvars, "term_setapi()");
+ term_T *term;
+ char_u *api;
+
+ if (buf == NULL)
+ return;
+ term = buf->b_term;
+ vim_free(term->tl_api);
+ api = tv_get_string_chk(&argvars[1]);
+ if (api != NULL)
+ term->tl_api = vim_strsave(api);
+ else
+ term->tl_api = NULL;
+}
+
+/*
* "term_setrestore(buf, command)" function
*/
void
@@ -5608,7 +5660,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
+ JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
+ JO2_CWD + JO2_ENV + JO2_EOF_CHARS
+ JO2_NORESTORE + JO2_TERM_KILL
- + JO2_ANSI_COLORS + JO2_TTY_TYPE) == FAIL)
+ + JO2_ANSI_COLORS + JO2_TTY_TYPE + JO2_TERM_API) == FAIL)
return;
buf = term_start(&argvars[0], NULL, &opt, 0);
diff --git a/src/testdir/term_util.vim b/src/testdir/term_util.vim
index 1cfac0e9d0..4eaf6d8bee 100644
--- a/src/testdir/term_util.vim
+++ b/src/testdir/term_util.vim
@@ -61,11 +61,16 @@ func RunVimInTerminal(arguments, options)
let cmd = GetVimCommandCleanTerm() .. a:arguments
- let buf = term_start(cmd, {
+ let options = {
\ 'curwin': 1,
\ 'term_rows': rows,
\ 'term_cols': cols,
- \ })
+ \ }
+ " Accept other options whose name starts with 'term_'.
+ call extend(options, filter(copy(a:options), 'v:key =~# "^term_"'))
+
+ let buf = term_start(cmd, options)
+
if &termwinsize == ''
" in the GUI we may end up with a different size, try to set it.
if term_getsize(buf) != [rows, cols]
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 814a989c8e..0041965f30 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -1353,30 +1353,90 @@ endfunc
func Test_terminal_api_call()
CheckRunVimInTerminal
+call ch_logfile('logfile', 'w')
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
call WriteApiCall('Tapi_TryThis')
+
+ " Default
let buf = RunVimInTerminal('-S Xscript', {})
call WaitFor({-> exists('g:called_bufnum')})
call assert_equal(buf, g:called_bufnum)
call assert_equal(['hello', 123], g:called_arg)
+ call StopVimInTerminal(buf)
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ " Enable explicitly
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'Tapi_Try'})
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+ call StopVimInTerminal(buf)
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+ func! ApiCall_TryThis(bufnum, arg)
+ let g:called_bufnum2 = a:bufnum
+ let g:called_arg2 = a:arg
+ endfunc
+
+ call WriteApiCall('ApiCall_TryThis')
+
+ " Use prefix match
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'ApiCall_'})
+ call WaitFor({-> exists('g:called_bufnum2')})
+ call assert_equal(buf, g:called_bufnum2)
+ call assert_equal(['hello', 123], g:called_arg2)
call StopVimInTerminal(buf)
+
+ unlet! g:called_bufnum2
+ unlet! g:called_arg2
+
call delete('Xscript')
- unlet g:called_bufnum
- unlet g:called_arg
+ delfunction! ApiCall_TryThis
+ unlet! g:called_bufnum2
+ unlet! g:called_arg2
endfunc
func Test_terminal_api_call_fails()
CheckRunVimInTerminal
+ func! TryThis(bufnum, arg)
+ let g:called_bufnum3 = a:bufnum
+ let g:called_arg3 = a:arg
+ endfunc
+
call WriteApiCall('TryThis')
+
+ unlet! g:called_bufnum3
+ unlet! g:called_arg3
+
+ " Not permitted
call ch_logfile('Xlog', 'w')
- let buf = RunVimInTerminal('-S Xscript', {})
- call WaitForAssert({-> assert_match('Invalid function name: TryThis', string(readfile('Xlog')))})
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': ''})
+ call WaitForAssert({-> assert_match('Unpermitted function: TryThis', string(readfile('Xlog')))})
+ call assert_false(exists('g:called_bufnum3'))
+ call assert_false(exists('g:called_arg3'))
+ call StopVimInTerminal(buf)
+ " No match
+ call ch_logfile('Xlog', 'w')
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'TryThat'})
+ call WaitFor({-> string(readfile('Xlog')) =~ 'Unpermitted function: TryThis'})
+ call assert_false(exists('g:called_bufnum3'))
+ call assert_false(exists('g:called_arg3'))
call StopVimInTerminal(buf)
+
call delete('Xscript')
- call ch_logfile('', '')
+ call ch_logfile('')
call delete('Xlog')
+ delfunction! TryThis
+ unlet! g:called_bufnum3
+ unlet! g:called_arg3
endfunc
let s:caught_e937 = 0
@@ -2061,3 +2121,34 @@ func Test_terminal_altscreen()
exe buf . "bwipe!"
call delete('Xtext')
endfunc
+
+func Test_terminal_setapi_and_call()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call WriteApiCall('Tapi_TryThis')
+ call ch_logfile('Xlog', 'w')
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 0})
+ call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))})
+ call assert_false(exists('g:called_bufnum'))
+ call assert_false(exists('g:called_arg'))
+
+ call term_setapi(buf, 'Tapi_TryThis')
+ call term_sendkeys(buf, ":set notitle\<CR>")
+ call term_sendkeys(buf, ":source Xscript\<CR>")
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+ call StopVimInTerminal(buf)
+
+ call delete('Xscript')
+ call ch_logfile('')
+ call delete('Xlog')
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+endfunc
diff --git a/src/version.c b/src/version.c
index f7d7ede5ff..44d4d04148 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2080,
+/**/
2079,
/**/
2078,