]> granicus.if.org Git - vim/commitdiff
patch 8.0.1074: ":term NONE" does not work on MS-Windows v8.0.1074
authorBram Moolenaar <Bram@vim.org>
Fri, 8 Sep 2017 12:39:30 +0000 (14:39 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 8 Sep 2017 12:39:30 +0000 (14:39 +0200)
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)

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

index f9c6ae3b46b9a8d752f63d241db8fa5cc137b1ca..af43bf28fd281a301beb9c29e866f61f53bdd080 100644 (file)
@@ -2401,7 +2401,7 @@ term_getscrolled({buf})           Number  get the scroll count of a terminal
 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_start()*
                                     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}
 
index 30a4304d8a6ae09762e0f97aad8f22b4551d8099..c401df2907420aa4824e92436c9856da7f8fc233 100644 (file)
@@ -969,7 +969,13 @@ ch_close_part(channel_T *channel, ch_part_T part)
            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 fd, int timeout)
            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);
index cf9c8d8ec34decc727e2b1b4e423eb347922da33..a2542e2c5ee4f4d03cd48208cd44a1dd09c9ce8c 100644 (file)
@@ -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},
index 1ec59fcdcfa1602582c9b8fe0abc4b48de33a501..3366efd1da8bc85f78e33d5585455315beb24492 100644 (file)
@@ -5263,7 +5263,11 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
            && (!(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, jobopt_T *options)
     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();
index f19377e36a93c40c5ab9b992cc4d75d18a48a6b3..772b4e706428558d12daf5b4b84bebc2a8f54cb5 100644 (file)
@@ -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 */
index 3ceb360fbccb2044f8b60ab5b9c7557de255ed53..9c4e75948c8267f119b27f80072559fada2b9ce3 100644 (file)
@@ -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_T *rettv)
 {
     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
index 800567920d2b3e702bfd8bb8c62114a2669efa91..efc491d92bccfe28d0667a1aee839e901c63cc3f 100644 (file)
@@ -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'})
index 9ec37eb66787d3ac56b768dae956b601868a0b10..2ab1b17d3bdcb21d128f897d3e22d492ba4441d1 100644 (file)
@@ -769,6 +769,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1074,
 /**/
     1073,
 /**/