# HG changeset patch # User Christian Brabandt # Date 1504874705 -7200 # Node ID d0cf7f71b95bb1bc62d4aff12bf47d2b41e874de # Parent 9741eade42c95a837f92ee05e86bdc317a675dad patch 8.0.1074: ":term NONE" does not work on MS-Windows commit https://github.com/vim/vim/commit/2dc9d26c14e410c09e538cccfa90da19ae344ba4 Author: Bram Moolenaar Date: Fri Sep 8 14:39:30 2017 +0200 patch 8.0.1074: ":term NONE" does not work on MS-Windows Problem: ":term NONE" does not work on MS-Windows. Solution: Make it work. Split "pty" into "pty_in" and "pty_out". (Yasuhiro Matsumoto, closes #2058, closes #2045) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2401,7 +2401,7 @@ term_getscrolled({buf}) Number get the term_getsize({buf}) List get the size of a terminal term_getstatus({buf}) String get the status of a terminal term_gettitle({buf}) String get the title of a terminal -term_gettty({buf}) String get the tty name of a terminal +term_getttty({buf}, [{input}]) String get the tty name of a terminal term_list() List get the list of terminal buffers term_scrape({buf}, {row}) List get row of a terminal screen term_sendkeys({buf}, {keys}) none send keystrokes to a terminal @@ -5245,7 +5245,8 @@ job_info({job}) *job_info()* "status" what |job_status()| returns "channel" what |job_getchannel()| returns "process" process ID - "tty" controlling terminal name, empty when none + "tty_in" terminal input name, empty when none + "tty_out" terminal output name, empty when none "exitval" only valid when "status" is "dead" "exit_cb" function to be called on exit "stoponexit" |job-stoponexit| @@ -8092,10 +8093,13 @@ term_gettitle({buf}) *term_gettitle( string is returned. {only available when compiled with the |+terminal| feature} -term_gettty({buf}) *term_gettty()* +term_gettty({buf} [, {input}]) *term_gettty()* Get the name of the controlling terminal associated with - terminal window {buf}. - {buf} is used as with |term_getsize()|. + terminal window {buf}. {buf} is used as with |term_getsize()|. + + When {input} is omitted or 0, return the name for writing + (stdout). When {input} is 1 return the name for reading + (stdin). On UNIX, both return same name. {only available when compiled with the |+terminal| feature} term_list() *term_list()* @@ -8173,10 +8177,9 @@ term_start({cmd}, {options}) *term_st specified "botright sbuf %d" is used "eof_chars" Text to send after all buffer lines were written to the terminal. When not set - CTRL-D is used. For Python use CTRL-Z or - "exit()". For a shell use "exit". A CR - is always added. - {only on MS-Windows} + CTRL-D is used on MS-Windows. For Python + use CTRL-Z or "exit()". For a shell use + "exit". A CR is always added. {only available when compiled with the |+terminal| feature} diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -969,7 +969,13 @@ ch_close_part(channel_T *channel, ch_par if ((part == PART_IN || channel->CH_IN_FD != *fd) && (part == PART_OUT || channel->CH_OUT_FD != *fd) && (part == PART_ERR || channel->CH_ERR_FD != *fd)) + { +#ifdef WIN32 + if (channel->ch_named_pipe) + DisconnectNamedPipe((HANDLE)fd); +#endif fd_close(*fd); + } } *fd = INVALID_FD; @@ -3086,7 +3092,20 @@ channel_wait(channel_T *channel, sock_T if (r && nread > 0) return CW_READY; if (r == 0) - return CW_ERROR; + { + DWORD err = GetLastError(); + + if (err != ERROR_BAD_PIPE && err != ERROR_BROKEN_PIPE) + return CW_ERROR; + + if (channel->ch_named_pipe) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } + else + return CW_ERROR; + } /* perhaps write some buffer lines */ channel_write_any_lines(); @@ -3670,7 +3689,20 @@ channel_send( if (part == PART_SOCK) res = sock_write(fd, (char *)buf, len); else + { res = fd_write(fd, (char *)buf, len); +#ifdef WIN32 + if (channel->ch_named_pipe) + { + if (res < 0) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } + } +#endif + + } if (res < 0 && (errno == EWOULDBLOCK #ifdef EAGAIN || errno == EAGAIN @@ -4849,7 +4881,8 @@ job_free_contents(job_T *job) } mch_clear_job(job); - vim_free(job->jv_tty_name); + vim_free(job->jv_tty_in); + vim_free(job->jv_tty_out); vim_free(job->jv_stoponexit); free_callback(job->jv_exit_cb, job->jv_exit_partial); } @@ -5503,8 +5536,10 @@ job_info(job_T *job, dict_T *dict) nr = job->jv_proc_info.dwProcessId; #endif dict_add_nr_str(dict, "process", nr, NULL); - dict_add_nr_str(dict, "tty", 0L, - job->jv_tty_name != NULL ? job->jv_tty_name : (char_u *)""); + dict_add_nr_str(dict, "tty_in", 0L, + job->jv_tty_in != NULL ? job->jv_tty_in : (char_u *)""); + dict_add_nr_str(dict, "tty_out", 0L, + job->jv_tty_out != NULL ? job->jv_tty_out : (char_u *)""); dict_add_nr_str(dict, "exitval", job->jv_exitval, NULL); dict_add_nr_str(dict, "exit_cb", 0L, job->jv_exit_cb); diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -843,7 +843,7 @@ static struct fst {"term_getsize", 1, 1, f_term_getsize}, {"term_getstatus", 1, 1, f_term_getstatus}, {"term_gettitle", 1, 1, f_term_gettitle}, - {"term_gettty", 1, 1, f_term_gettty}, + {"term_gettty", 1, 2, f_term_gettty}, {"term_list", 0, 0, f_term_list}, {"term_scrape", 2, 2, f_term_scrape}, {"term_sendkeys", 2, 2, f_term_sendkeys}, diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5263,7 +5263,11 @@ mch_job_start(char **argv, job_T *job, j && (!(use_file_for_in || use_null_for_in) || !(use_file_for_in || use_null_for_out) || !(use_out_for_err || use_file_for_err || use_null_for_err))) - open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name); + { + open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out); + if (job->jv_tty_out != NULL) + job->jv_tty_in = vim_strsave(job->jv_tty_out); + } /* TODO: without the channel feature connect the child to /dev/null? */ /* Open pipes for stdin, stdout, stderr. */ @@ -5687,7 +5691,9 @@ mch_create_pty_channel(job_T *job, jobop int pty_slave_fd = -1; channel_T *channel; - open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name); + open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out); + if (job->jv_tty_out != NULL) + job->jv_tty_in = vim_strsave(job->jv_tty_out); close(pty_slave_fd); channel = add_channel(); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1487,7 +1487,8 @@ struct jobvar_S PROCESS_INFORMATION jv_proc_info; HANDLE jv_job_object; #endif - char_u *jv_tty_name; /* controlling tty, allocated */ + char_u *jv_tty_in; /* controlling tty input, allocated */ + char_u *jv_tty_out; /* controlling tty output, allocated */ jobstatus_T jv_status; char_u *jv_stoponexit; /* allocated */ int jv_exitval; @@ -1652,6 +1653,9 @@ struct channel_S { /* callback for Netbeans when channel is * closed */ +#ifdef WIN32 + int ch_named_pipe; /* using named pipe instead of pty */ +#endif char_u *ch_callback; /* call when any msg is not handled */ partial_T *ch_partial; char_u *ch_close_cb; /* call when channel is closed */ diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -38,8 +38,7 @@ * in tl_scrollback are no longer used. * * TODO: - * - ":term NONE" does not work on MS-Windows. - * https://github.com/vim/vim/pull/2056 + * - patch to use GUI or cterm colors for vterm. Yasuhiro, #2067 * - Redirecting output does not work on MS-Windows. * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. @@ -97,7 +96,8 @@ struct terminal_S { /* used when tl_job is NULL and only a pty was created */ int tl_tty_fd; - char_u *tl_tty_name; + char_u *tl_tty_in; + char_u *tl_tty_out; int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_channel_closed; @@ -2666,14 +2666,32 @@ f_term_gettty(typval_T *argvars, typval_ { buf_T *buf = term_get_buf(argvars); char_u *p; + int num = 0; rettv->v_type = VAR_STRING; if (buf == NULL) return; - if (buf->b_term->tl_job != NULL) - p = buf->b_term->tl_job->jv_tty_name; - else - p = buf->b_term->tl_tty_name; + if (argvars[1].v_type != VAR_UNKNOWN) + num = get_tv_number(&argvars[1]); + + switch (num) + { + case 0: + if (buf->b_term->tl_job != NULL) + p = buf->b_term->tl_job->jv_tty_out; + else + p = buf->b_term->tl_tty_out; + break; + case 1: + if (buf->b_term->tl_job != NULL) + p = buf->b_term->tl_job->jv_tty_in; + else + p = buf->b_term->tl_tty_in; + break; + default: + EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + return; + } if (p != NULL) rettv->vval.v_string = vim_strsave(p); } @@ -3055,7 +3073,6 @@ term_and_job_init( HANDLE child_thread_handle; void *winpty_err; void *spawn_config = NULL; - char buf[MAX_PATH]; garray_T ga; char_u *cmd; @@ -3094,7 +3111,6 @@ term_and_job_init( if (term->tl_winpty == NULL) goto failed; - /* TODO: if the command is "NONE" only create a pty. */ spawn_config = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN, @@ -3162,9 +3178,10 @@ term_and_job_init( job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); job->jv_job_object = jo; job->jv_status = JOB_STARTED; - sprintf(buf, "winpty://%lu", - GetProcessId(winpty_agent_process(term->tl_winpty))); - job->jv_tty_name = vim_strsave((char_u*)buf); + job->jv_tty_in = utf16_to_enc( + (short_u*)winpty_conin_name(term->tl_winpty), NULL); + job->jv_tty_out = utf16_to_enc( + (short_u*)winpty_conout_name(term->tl_winpty), NULL); ++job->jv_refcount; term->tl_job = job; @@ -3205,9 +3222,68 @@ failed: } static int -create_pty_only(term_T *term, jobopt_T *opt) +create_pty_only(term_T *term, jobopt_T *options) { - /* TODO: implement this */ + HANDLE hPipeIn = INVALID_HANDLE_VALUE; + HANDLE hPipeOut = INVALID_HANDLE_VALUE; + char in_name[80], out_name[80]; + channel_T *channel = NULL; + + create_vterm(term, term->tl_rows, term->tl_cols); + + vim_snprintf(in_name, sizeof(in_name), "\\\\.\\pipe\\vim-%d-in-%d", + GetCurrentProcessId(), + curbuf->b_fnum); + hPipeIn = CreateNamedPipe(in_name, PIPE_ACCESS_OUTBOUND, + PIPE_TYPE_MESSAGE | PIPE_NOWAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, NMPWAIT_NOWAIT, NULL); + if (hPipeIn == INVALID_HANDLE_VALUE) + goto failed; + + vim_snprintf(out_name, sizeof(out_name), "\\\\.\\pipe\\vim-%d-out-%d", + GetCurrentProcessId(), + curbuf->b_fnum); + hPipeOut = CreateNamedPipe(out_name, PIPE_ACCESS_INBOUND, + PIPE_TYPE_MESSAGE | PIPE_NOWAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, 0, NULL); + if (hPipeOut == INVALID_HANDLE_VALUE) + goto failed; + + ConnectNamedPipe(hPipeIn, NULL); + ConnectNamedPipe(hPipeOut, NULL); + + term->tl_job = job_alloc(); + if (term->tl_job == NULL) + goto failed; + ++term->tl_job->jv_refcount; + + /* behave like the job is already finished */ + term->tl_job->jv_status = JOB_FINISHED; + + channel = add_channel(); + if (channel == NULL) + goto failed; + term->tl_job->jv_channel = channel; + channel->ch_keep_open = TRUE; + channel->ch_named_pipe = TRUE; + + channel_set_pipes(channel, + (sock_T)hPipeIn, + (sock_T)hPipeOut, + (sock_T)hPipeOut); + channel_set_job(channel, term->tl_job, options); + term->tl_job->jv_tty_in = vim_strsave((char_u*)in_name); + term->tl_job->jv_tty_out = vim_strsave((char_u*)out_name); + + return OK; + +failed: + if (hPipeIn != NULL) + CloseHandle(hPipeIn); + if (hPipeOut != NULL) + CloseHandle(hPipeOut); return FAIL; } @@ -3234,7 +3310,8 @@ term_free_vterm(term_T *term) static void term_report_winsize(term_T *term, int rows, int cols) { - winpty_set_size(term->tl_winpty, cols, rows, NULL); + if (term->tl_winpty) + winpty_set_size(term->tl_winpty, cols, rows, NULL); } int diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -36,11 +36,11 @@ endfunc func Test_terminal_basic() let buf = Run_shell_in_terminal({}) if has("unix") - call assert_match("^/dev/", job_info(g:job).tty) - call assert_match("^/dev/", term_gettty('')) + call assert_match('^/dev/', job_info(g:job).tty_out) + call assert_match('^/dev/', term_gettty('')) else - call assert_match("^winpty://", job_info(g:job).tty) - call assert_match("^winpty://", term_gettty('')) + call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out) + call assert_match('^\\\\.\\pipe\\', term_gettty('')) endif call assert_equal('t', mode()) call assert_match('%aR[^\n]*running]', execute('ls')) @@ -539,10 +539,6 @@ func Test_terminal_write_stdin() endfunc func Test_terminal_no_cmd() - " Todo: make this work on all systems. - if !has('unix') - return - endif " Todo: make this work in the GUI if !has('gui_running') return @@ -550,11 +546,20 @@ func Test_terminal_no_cmd() let buf = term_start('NONE', {}) call assert_notequal(0, buf) - let pty = job_info(term_getjob(buf))['tty'] + let pty = job_info(term_getjob(buf))['tty_out'] call assert_notequal('', pty) - call system('echo "look here" > ' . pty) + if has('win32') + silent exe '!cmd /c "echo look here > ' . pty . '"' + else + call system('echo "look here" > ' . pty) + endif call term_wait(buf) - call assert_equal('look here', term_getline(buf, 1)) + + let result = term_getline(buf, 1) + if has('win32') + let result = substitute(result, '\s\+$', '', '') + endif + call assert_equal('look here', result) bwipe! endfunc @@ -600,6 +605,7 @@ func Test_terminal_redir_file() call WaitFor('len(readfile("Xfile")) > 0') call assert_match('123', readfile('Xfile')[0]) call delete('Xfile') + bwipe endif if has('unix') @@ -608,6 +614,7 @@ func Test_terminal_redir_file() call WaitFor('len(readfile("Xfile")) > 0') call assert_match('executing job failed', readfile('Xfile')[0]) call delete('Xfile') + bwipe call writefile(['one line'], 'Xfile') let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xfile'}) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -770,6 +770,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1074, +/**/ 1073, /**/ 1072,