changeset 12389:d0cf7f71b95b v8.0.1074

patch 8.0.1074: ":term NONE" does not work on MS-Windows commit https://github.com/vim/vim/commit/2dc9d26c14e410c09e538cccfa90da19ae344ba4 Author: Bram Moolenaar <Bram@vim.org> 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)
author Christian Brabandt <cb@256bit.org>
date Fri, 08 Sep 2017 14:45:05 +0200
parents 9741eade42c9
children 63350fb880f9
files runtime/doc/eval.txt src/channel.c src/evalfunc.c src/os_unix.c src/structs.h src/terminal.c src/testdir/test_terminal.vim src/version.c
diffstat 8 files changed, 177 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- 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}
 
--- 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);
--- 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},
--- 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();
--- 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 */
--- 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
--- 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'})
--- 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,