changeset 19245:5ed8297121fa v8.2.0181

patch 8.2.0181: problems parsing :term arguments Commit: https://github.com/vim/vim/commit/21109272f5b0d32c408dc292561c0b1f2f8ebc53 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jan 30 16:27:20 2020 +0100 patch 8.2.0181: problems parsing :term arguments Problem: Problems parsing :term arguments. Solution: Improve parsing, fix memory leak, add tests. (Ozaki Kiichi, closes #5536)
author Bram Moolenaar <Bram@vim.org>
date Thu, 30 Jan 2020 16:30:04 +0100
parents bb09b0f937ad
children fb4df26177a5
files src/channel.c src/proto/channel.pro src/structs.h src/terminal.c src/testdir/test_terminal.vim src/version.c
diffstat 6 files changed, 133 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -4787,8 +4787,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_OUT_IO))
 		    break;
 		opt->jo_set |= JO_OUT_NAME << (part - PART_OUT);
-		opt->jo_io_name[part] =
-		       tv_get_string_buf_chk(item, opt->jo_io_name_buf[part]);
+		opt->jo_io_name[part] = tv_get_string_buf_chk(item,
+						   opt->jo_io_name_buf[part]);
 	    }
 	    else if (STRCMP(hi->hi_key, "pty") == 0)
 	    {
@@ -4953,7 +4953,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported2 & JO2_TERM_NAME))
 		    break;
 		opt->jo_set2 |= JO2_TERM_NAME;
-		opt->jo_term_name = tv_get_string_chk(item);
+		opt->jo_term_name = tv_get_string_buf_chk(item,
+						       opt->jo_term_name_buf);
 		if (opt->jo_term_name == NULL)
 		{
 		    semsg(_(e_invargval), "term_name");
@@ -4980,7 +4981,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported2 & JO2_TERM_OPENCMD))
 		    break;
 		opt->jo_set2 |= JO2_TERM_OPENCMD;
-		p = opt->jo_term_opencmd = tv_get_string_chk(item);
+		p = opt->jo_term_opencmd = tv_get_string_buf_chk(item,
+						    opt->jo_term_opencmd_buf);
 		if (p != NULL)
 		{
 		    // Must have %d and no other %.
@@ -4997,13 +4999,12 @@ get_job_options(typval_T *tv, jobopt_T *
 	    }
 	    else if (STRCMP(hi->hi_key, "eof_chars") == 0)
 	    {
-		char_u *p;
-
 		if (!(supported2 & JO2_EOF_CHARS))
 		    break;
 		opt->jo_set2 |= JO2_EOF_CHARS;
-		p = opt->jo_eof_chars = tv_get_string_chk(item);
-		if (p == NULL)
+		opt->jo_eof_chars = tv_get_string_buf_chk(item,
+						       opt->jo_eof_chars_buf);
+		if (opt->jo_eof_chars == NULL)
 		{
 		    semsg(_(e_invargval), "eof_chars");
 		    return FAIL;
@@ -5082,7 +5083,13 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported2 & JO2_TERM_KILL))
 		    break;
 		opt->jo_set2 |= JO2_TERM_KILL;
-		opt->jo_term_kill = tv_get_string_chk(item);
+		opt->jo_term_kill = tv_get_string_buf_chk(item,
+						       opt->jo_term_kill_buf);
+		if (opt->jo_term_kill == NULL)
+		{
+		    semsg(_(e_invargval), "term_kill");
+		    return FAIL;
+		}
 	    }
 	    else if (STRCMP(hi->hi_key, "tty_type") == 0)
 	    {
@@ -5157,7 +5164,12 @@ get_job_options(typval_T *tv, jobopt_T *
 		    break;
 		opt->jo_set2 |= JO2_TERM_API;
 		opt->jo_term_api = tv_get_string_buf_chk(item,
-							 opt->jo_term_api_buf);
+							opt->jo_term_api_buf);
+		if (opt->jo_term_api == NULL)
+		{
+		    semsg(_(e_invargval), "term_api");
+		    return FAIL;
+		}
 	    }
 #endif
 	    else if (STRCMP(hi->hi_key, "env") == 0)
@@ -5247,7 +5259,7 @@ get_job_options(typval_T *tv, jobopt_T *
 		    break;
 		opt->jo_set |= JO_STOPONEXIT;
 		opt->jo_stoponexit = tv_get_string_buf_chk(item,
-							     opt->jo_soe_buf);
+						      opt->jo_stoponexit_buf);
 		if (opt->jo_stoponexit == NULL)
 		{
 		    semsg(_(e_invargval), "stoponexit");
@@ -5817,7 +5829,7 @@ job_start(
 	typval_T    *argvars,
 	char	    **argv_arg UNUSED,
 	jobopt_T    *opt_arg,
-	int	    is_terminal UNUSED)
+	job_T	    **term_job)
 {
     job_T	*job;
     char_u	*cmd = NULL;
@@ -5968,6 +5980,9 @@ job_start(
     // Save the command used to start the job.
     job->jv_argv = argv;
 
+    if (term_job != NULL)
+	*term_job = job;
+
 #ifdef USE_ARGV
     if (ch_log_active())
     {
@@ -5984,7 +5999,7 @@ job_start(
 	ch_log(NULL, "Starting job: %s", (char *)ga.ga_data);
 	ga_clear(&ga);
     }
-    mch_job_start(argv, job, &opt, is_terminal);
+    mch_job_start(argv, job, &opt, term_job != NULL);
 #else
     ch_log(NULL, "Starting job: %s", (char *)cmd);
     mch_job_start((char *)cmd, job, &opt);
@@ -6600,7 +6615,7 @@ f_job_start(typval_T *argvars, typval_T 
     rettv->v_type = VAR_JOB;
     if (check_restricted() || check_secure())
 	return;
-    rettv->vval.v_job = job_start(argvars, NULL, NULL, FALSE);
+    rettv->vval.v_job = job_start(argvars, NULL, NULL, NULL);
 }
 
 /*
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -51,7 +51,7 @@ void job_set_options(job_T *job, jobopt_
 void job_stop_on_exit(void);
 int has_pending_job(void);
 int job_check_ended(void);
-job_T *job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg, int is_terminal);
+job_T *job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg, job_T **term_job);
 char *job_status(job_T *job);
 int job_stop(job_T *job, typval_T *argvars, char *type);
 void invoke_prompt_callback(void);
--- a/src/structs.h
+++ b/src/structs.h
@@ -2106,7 +2106,7 @@ typedef struct
     int		jo_block_write;	// for testing only
     int		jo_part;
     int		jo_id;
-    char_u	jo_soe_buf[NUMBUFLEN];
+    char_u	jo_stoponexit_buf[NUMBUFLEN];
     char_u	*jo_stoponexit;
     dict_T	*jo_env;	// environment variables
     char_u	jo_cwd_buf[NUMBUFLEN];
@@ -2121,17 +2121,21 @@ typedef struct
     buf_T	*jo_bufnr_buf;
     int		jo_hidden;
     int		jo_term_norestore;
+    char_u	jo_term_name_buf[NUMBUFLEN];
     char_u	*jo_term_name;
+    char_u	jo_term_opencmd_buf[NUMBUFLEN];
     char_u	*jo_term_opencmd;
     int		jo_term_finish;
+    char_u	jo_eof_chars_buf[NUMBUFLEN];
     char_u	*jo_eof_chars;
+    char_u	jo_term_kill_buf[NUMBUFLEN];
     char_u	*jo_term_kill;
 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
     long_u	jo_ansi_colors[16];
 # endif
     int		jo_tty_type;	    // first character of "tty_type"
+    char_u	jo_term_api_buf[NUMBUFLEN];
     char_u	*jo_term_api;
-    char_u	jo_term_api_buf[NUMBUFLEN];
 #endif
 } jobopt_T;
 
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -595,9 +595,7 @@ term_start(
 #if defined(FEAT_SESSION)
     // Remember the command for the session file.
     if (opt->jo_term_norestore || argv != NULL)
-    {
 	term->tl_command = vim_strsave((char_u *)"NONE");
-    }
     else if (argvar->v_type == VAR_STRING)
     {
 	char_u	*cmd = argvar->vval.v_string;
@@ -646,7 +644,11 @@ term_start(
     }
 
     if (opt->jo_term_api != NULL)
-	term->tl_api = vim_strsave(opt->jo_term_api);
+    {
+	char_u *p = skiptowhite(opt->jo_term_api);
+
+	term->tl_api = vim_strnsave(opt->jo_term_api, p - opt->jo_term_api);
+    }
     else
 	term->tl_api = vim_strsave((char_u *)"Tapi_");
 
@@ -778,6 +780,7 @@ ex_terminal(exarg_T *eap)
 	    char_u *buf = NULL;
 	    char_u *keys;
 
+	    vim_free(opt.jo_eof_chars);
 	    p = skiptowhite(cmd);
 	    *p = NUL;
 	    keys = replace_termcodes(ep + 1, &buf,
@@ -6697,7 +6700,7 @@ term_and_job_init(
 #endif
 
     // This may change a string in "argvar".
-    term->tl_job = job_start(argvar, argv, opt, TRUE);
+    term->tl_job = job_start(argvar, argv, opt, &term->tl_job);
     if (term->tl_job != NULL)
 	++term->tl_job->jv_refcount;
 
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -689,53 +689,70 @@ func Test_terminal_noblock()
 endfunc
 
 func Test_terminal_write_stdin()
-  if !executable('wc')
-    throw 'skipped: wc command not available'
-  endif
-  if has('win32')
-    " TODO: enable once writing to stdin works on MS-Windows
-    return
-  endif
-  new
+  " TODO: enable once writing to stdin works on MS-Windows
+  CheckNotMSWindows
+  CheckExecutable wc
+
   call setline(1, ['one', 'two', 'three'])
   %term wc
   call WaitForAssert({-> assert_match('3', getline("$"))})
   let nrs = split(getline('$'))
   call assert_equal(['3', '3', '14'], nrs)
-  bwipe
+  %bwipe!
 
-  new
   call setline(1, ['one', 'two', 'three', 'four'])
   2,3term wc
   call WaitForAssert({-> assert_match('2', getline("$"))})
   let nrs = split(getline('$'))
   call assert_equal(['2', '2', '10'], nrs)
-  bwipe
+  %bwipe!
+endfunc
+
+func Test_terminal_eof_arg()
+  CheckExecutable python
+
+  call setline(1, ['print("hello")'])
+  1term ++eof=exit(123) python
+  " MS-Windows echoes the input, Unix doesn't.
+  if has('win32')
+    call WaitFor({-> getline('$') =~ 'exit(123)'})
+    call assert_equal('hello', getline(line('$') - 1))
+  else
+    call WaitFor({-> getline('$') =~ 'hello'})
+    call assert_equal('hello', getline('$'))
+  endif
+  call assert_equal(123, bufnr()->term_getjob()->job_info().exitval)
+  %bwipe!
+endfunc
+
+func Test_terminal_eof_arg_win32_ctrl_z()
+  CheckMSWindows
+  CheckExecutable python
 
-  if executable('python')
-    new
-    call setline(1, ['print("hello")'])
-    1term ++eof=exit() python
-    " MS-Windows echoes the input, Unix doesn't.
-    call WaitFor('getline("$") =~ "exit" || getline(1) =~ "hello"')
-    if getline(1) =~ 'hello'
-      call assert_equal('hello', getline(1))
-    else
-      call assert_equal('hello', getline(line('$') - 1))
-    endif
-    bwipe
+  call setline(1, ['print("hello")'])
+  1term ++eof=<C-Z> python
+  call WaitForAssert({-> assert_match('\^Z', getline(line('$') - 1))})
+  call assert_match('\^Z', getline(line('$') - 1))
+  %bwipe!
+endfunc
+
+func Test_terminal_duplicate_eof_arg()
+  CheckExecutable python
 
-    if has('win32')
-      new
-      call setline(1, ['print("hello")'])
-      1term ++eof=<C-Z> python
-      call WaitForAssert({-> assert_match('Z', getline("$"))})
-      call assert_equal('hello', getline(line('$') - 1))
-      bwipe
-    endif
+  " Check the last specified ++eof arg is used and should not memory leak.
+  new
+  call setline(1, ['print("hello")'])
+  1term ++eof=<C-Z> ++eof=exit(123) python
+  " MS-Windows echoes the input, Unix doesn't.
+  if has('win32')
+    call WaitFor({-> getline('$') =~ 'exit(123)'})
+    call assert_equal('hello', getline(line('$') - 1))
+  else
+    call WaitFor({-> getline('$') =~ 'hello'})
+    call assert_equal('hello', getline('$'))
   endif
-
-  bwipe!
+  call assert_equal(123, bufnr()->term_getjob()->job_info().exitval)
+  %bwipe!
 endfunc
 
 func Test_terminal_no_cmd()
@@ -2242,9 +2259,7 @@ func Test_terminal_shell_option()
 endfunc
 
 func Test_terminal_setapi_and_call()
-  if !CanRunVimInTerminal()
-    return
-  endif
+  CheckRunVimInTerminal
 
   call WriteApiCall('Tapi_TryThis')
   call ch_logfile('Xlog', 'w')
@@ -2252,17 +2267,18 @@ func Test_terminal_setapi_and_call()
   unlet! g:called_bufnum
   unlet! g:called_arg
 
-  let buf = RunVimInTerminal('-S Xscript', {'term_api': 0})
+  let buf = RunVimInTerminal('-S Xscript', {'term_api': ''})
   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')
+  eval buf->term_setapi('Tapi_')
   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')
@@ -2271,3 +2287,37 @@ func Test_terminal_setapi_and_call()
   unlet! g:called_bufnum
   unlet! g:called_arg
 endfunc
+
+func Test_terminal_api_arg()
+  CheckRunVimInTerminal
+
+  call WriteApiCall('Tapi_TryThis')
+  call ch_logfile('Xlog', 'w')
+
+  unlet! g:called_bufnum
+  unlet! g:called_arg
+
+  execute 'term ++api= ' .. GetVimCommandCleanTerm() .. '-S Xscript'
+  let buf = bufnr('%')
+  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 StopVimInTerminal(buf)
+
+  call ch_logfile('Xlog', 'w')
+
+  execute 'term ++api=Tapi_ ' .. GetVimCommandCleanTerm() .. '-S Xscript'
+  let buf = bufnr('%')
+  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
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    181,
+/**/
     180,
 /**/
     179,