To: vim_dev@googlegroups.com Subject: Patch 8.2.0181 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0181 Problem: Problems parsing :term arguments. Solution: Improve parsing, fix memory leak, add tests. (Ozaki Kiichi, closes #5536) Files: src/channel.c, src/proto/channel.pro, src/structs.h, src/terminal.c, src/testdir/test_terminal.vim *** ../vim-8.2.0180/src/channel.c 2020-01-29 21:27:17.578406709 +0100 --- src/channel.c 2020-01-30 16:21:12.089824065 +0100 *************** *** 4787,4794 **** 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]); } else if (STRCMP(hi->hi_key, "pty") == 0) { --- 4787,4794 ---- 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]); } else if (STRCMP(hi->hi_key, "pty") == 0) { *************** *** 4953,4959 **** if (!(supported2 & JO2_TERM_NAME)) break; opt->jo_set2 |= JO2_TERM_NAME; ! opt->jo_term_name = tv_get_string_chk(item); if (opt->jo_term_name == NULL) { semsg(_(e_invargval), "term_name"); --- 4953,4960 ---- if (!(supported2 & JO2_TERM_NAME)) break; opt->jo_set2 |= JO2_TERM_NAME; ! 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,4986 **** if (!(supported2 & JO2_TERM_OPENCMD)) break; opt->jo_set2 |= JO2_TERM_OPENCMD; ! p = opt->jo_term_opencmd = tv_get_string_chk(item); if (p != NULL) { // Must have %d and no other %. --- 4981,4988 ---- if (!(supported2 & JO2_TERM_OPENCMD)) break; opt->jo_set2 |= JO2_TERM_OPENCMD; ! 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,5009 **** } 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) { semsg(_(e_invargval), "eof_chars"); return FAIL; --- 4999,5010 ---- } else if (STRCMP(hi->hi_key, "eof_chars") == 0) { if (!(supported2 & JO2_EOF_CHARS)) break; opt->jo_set2 |= JO2_EOF_CHARS; ! 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,5088 **** if (!(supported2 & JO2_TERM_KILL)) break; opt->jo_set2 |= JO2_TERM_KILL; ! opt->jo_term_kill = tv_get_string_chk(item); } else if (STRCMP(hi->hi_key, "tty_type") == 0) { --- 5083,5095 ---- if (!(supported2 & JO2_TERM_KILL)) break; opt->jo_set2 |= JO2_TERM_KILL; ! 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,5163 **** 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) --- 5164,5175 ---- break; opt->jo_set2 |= JO2_TERM_API; opt->jo_term_api = tv_get_string_buf_chk(item, ! 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,5253 **** break; opt->jo_set |= JO_STOPONEXIT; opt->jo_stoponexit = tv_get_string_buf_chk(item, ! opt->jo_soe_buf); if (opt->jo_stoponexit == NULL) { semsg(_(e_invargval), "stoponexit"); --- 5259,5265 ---- break; opt->jo_set |= JO_STOPONEXIT; opt->jo_stoponexit = tv_get_string_buf_chk(item, ! opt->jo_stoponexit_buf); if (opt->jo_stoponexit == NULL) { semsg(_(e_invargval), "stoponexit"); *************** *** 5817,5823 **** typval_T *argvars, char **argv_arg UNUSED, jobopt_T *opt_arg, ! int is_terminal UNUSED) { job_T *job; char_u *cmd = NULL; --- 5829,5835 ---- typval_T *argvars, char **argv_arg UNUSED, jobopt_T *opt_arg, ! job_T **term_job) { job_T *job; char_u *cmd = NULL; *************** *** 5968,5973 **** --- 5980,5988 ---- // 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,5990 **** ch_log(NULL, "Starting job: %s", (char *)ga.ga_data); ga_clear(&ga); } ! mch_job_start(argv, job, &opt, is_terminal); #else ch_log(NULL, "Starting job: %s", (char *)cmd); mch_job_start((char *)cmd, job, &opt); --- 5999,6005 ---- ch_log(NULL, "Starting job: %s", (char *)ga.ga_data); ga_clear(&ga); } ! 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,6606 **** rettv->v_type = VAR_JOB; if (check_restricted() || check_secure()) return; ! rettv->vval.v_job = job_start(argvars, NULL, NULL, FALSE); } /* --- 6615,6621 ---- rettv->v_type = VAR_JOB; if (check_restricted() || check_secure()) return; ! rettv->vval.v_job = job_start(argvars, NULL, NULL, NULL); } /* *** ../vim-8.2.0180/src/proto/channel.pro 2019-12-12 12:55:38.000000000 +0100 --- src/proto/channel.pro 2020-01-30 16:21:12.089824065 +0100 *************** *** 51,57 **** 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); char *job_status(job_T *job); int job_stop(job_T *job, typval_T *argvars, char *type); void invoke_prompt_callback(void); --- 51,57 ---- 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, 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); *** ../vim-8.2.0180/src/structs.h 2020-01-29 21:27:17.574406732 +0100 --- src/structs.h 2020-01-30 16:21:12.093824049 +0100 *************** *** 2106,2112 **** int jo_block_write; // for testing only int jo_part; int jo_id; ! char_u jo_soe_buf[NUMBUFLEN]; char_u *jo_stoponexit; dict_T *jo_env; // environment variables char_u jo_cwd_buf[NUMBUFLEN]; --- 2106,2112 ---- int jo_block_write; // for testing only int jo_part; int jo_id; ! char_u jo_stoponexit_buf[NUMBUFLEN]; char_u *jo_stoponexit; dict_T *jo_env; // environment variables char_u jo_cwd_buf[NUMBUFLEN]; *************** *** 2121,2137 **** buf_T *jo_bufnr_buf; int jo_hidden; int jo_term_norestore; char_u *jo_term_name; char_u *jo_term_opencmd; int jo_term_finish; char_u *jo_eof_chars; 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; char_u jo_term_api_buf[NUMBUFLEN]; #endif } jobopt_T; --- 2121,2141 ---- 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; #endif } jobopt_T; *** ../vim-8.2.0180/src/terminal.c 2020-01-30 14:55:29.006670423 +0100 --- src/terminal.c 2020-01-30 16:21:12.093824049 +0100 *************** *** 595,603 **** #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; --- 595,601 ---- *************** *** 646,652 **** } if (opt->jo_term_api != NULL) ! term->tl_api = vim_strsave(opt->jo_term_api); else term->tl_api = vim_strsave((char_u *)"Tapi_"); --- 644,654 ---- } if (opt->jo_term_api != NULL) ! { ! 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,783 **** --- 780,786 ---- 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,6703 **** #endif // This may change a string in "argvar". ! term->tl_job = job_start(argvar, argv, opt, TRUE); if (term->tl_job != NULL) ++term->tl_job->jv_refcount; --- 6700,6706 ---- #endif // This may change a string in "argvar". ! term->tl_job = job_start(argvar, argv, opt, &term->tl_job); if (term->tl_job != NULL) ++term->tl_job->jv_refcount; *** ../vim-8.2.0180/src/testdir/test_terminal.vim 2019-11-30 15:33:12.000000000 +0100 --- src/testdir/test_terminal.vim 2020-01-30 16:21:12.093824049 +0100 *************** *** 689,741 **** 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 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 - 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 ! 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 ! if has('win32') ! new ! call setline(1, ['print("hello")']) ! 1term ++eof= python ! call WaitForAssert({-> assert_match('Z', getline("$"))}) ! call assert_equal('hello', getline(line('$') - 1)) ! bwipe ! endif endif ! bwipe! endfunc func Test_terminal_no_cmd() --- 689,758 ---- endfunc func Test_terminal_write_stdin() ! " 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! 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! ! 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 ! ! call setline(1, ['print("hello")']) ! 1term ++eof= 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 ! ! " Check the last specified ++eof arg is used and should not memory leak. ! new ! call setline(1, ['print("hello")']) ! 1term ++eof= ++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_no_cmd() *************** *** 2242,2250 **** endfunc func Test_terminal_setapi_and_call() ! if !CanRunVimInTerminal() ! return ! endif call WriteApiCall('Tapi_TryThis') call ch_logfile('Xlog', 'w') --- 2259,2265 ---- endfunc func Test_terminal_setapi_and_call() ! CheckRunVimInTerminal call WriteApiCall('Tapi_TryThis') call ch_logfile('Xlog', 'w') *************** *** 2252,2268 **** 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\") call term_sendkeys(buf, ":source 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) call delete('Xscript') --- 2267,2318 ---- unlet! g:called_bufnum unlet! g:called_arg ! 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')) ! eval buf->term_setapi('Tapi_') call term_sendkeys(buf, ":set notitle\") call term_sendkeys(buf, ":source 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) + + call delete('Xscript') + call ch_logfile('') + call delete('Xlog') + 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') *** ../vim-8.2.0180/src/version.c 2020-01-30 16:09:18.460743760 +0100 --- src/version.c 2020-01-30 16:24:48.273047512 +0100 *************** *** 744,745 **** --- 744,747 ---- { /* Add new patch number below this line */ + /**/ + 181, /**/ -- hundred-and-one symptoms of being an internet addict: 20. When looking at a pageful of someone else's links, you notice all of them are already highlighted in purple. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///