]> granicus.if.org Git - vim/commitdiff
patch 8.0.1801: MS-Windows: redirecting terminal output does not work v8.0.1801
authorBram Moolenaar <Bram@vim.org>
Sun, 6 May 2018 19:49:32 +0000 (21:49 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 6 May 2018 19:49:32 +0000 (21:49 +0200)
Problem:    MS-Windows: redirecting terminal output does not work.
Solution:   Intercept the text written to the terminal and write it to the
            file.

src/terminal.c
src/testdir/test_terminal.vim
src/version.c

index 01919675f1f537f21b6b05dd8a744d7cabe4ed2b..47f7f201d581dc79a7fafc6cc69607f92aa3726f 100644 (file)
  * in tl_scrollback are no longer used.
  *
  * TODO:
- * - Win32: Make terminal used for :!cmd in the GUI work better.  Allow for
- *   redirection.  Probably in call to channel_set_pipes().
- * - Win32: Redirecting output does not work, Test_terminal_redir_file()
+ * - Win32: Redirecting input does not work, half of Test_terminal_redir_file()
  *   is disabled.
+ * - Win32: Redirecting output works but includes escape sequences.
+ * - Win32: Make terminal used for :!cmd in the GUI work better.  Allow for
+ *   redirection.
  * - Copy text in the vterm to the Vim buffer once in a while, so that
  *   completion works.
  * - When the job only outputs lines, we could handle resizing the terminal
  *   better: store lines separated by line breaks, instead of screen lines,
  *   then when the window is resized redraw those lines.
- * - Redrawing is slow with Athena and Motif.  Also other GUI? (Ramel Eshed)
+ * - Redrawing is slow with Athena and Motif. (Ramel Eshed)
  * - For the GUI fill termios with default values, perhaps like pangoterm:
  *   http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
  * - When 'encoding' is not utf-8, or the job is using another encoding, setup
@@ -99,11 +100,6 @@ struct terminal_S {
     /* Set when setting the size of a vterm, reset after redrawing. */
     int                tl_vterm_size_changed;
 
-    /* used when tl_job is NULL and only a pty was created */
-    int                tl_tty_fd;
-    char_u     *tl_tty_in;
-    char_u     *tl_tty_out;
-
     int                tl_normal_mode; /* TRUE: Terminal-Normal mode */
     int                tl_channel_closed;
     int                tl_finish;
@@ -117,6 +113,8 @@ struct terminal_S {
 #ifdef WIN3264
     void       *tl_winpty_config;
     void       *tl_winpty;
+
+    FILE       *tl_out_fd;
 #endif
 #if defined(FEAT_SESSION)
     char_u     *tl_command;
@@ -169,7 +167,7 @@ static term_T *in_terminal_loop = NULL;
 /*
  * Functions with separate implementation for MS-Windows and Unix-like systems.
  */
-static int term_and_job_init(term_T *term, typval_T *argvar, char **argv, jobopt_T *opt);
+static int term_and_job_init(term_T *term, typval_T *argvar, char **argv, jobopt_T *opt, jobopt_T *orig_opt);
 static int create_pty_only(term_T *term, jobopt_T *opt);
 static void term_report_winsize(term_T *term, int rows, int cols);
 static void term_free_vterm(term_T *term);
@@ -283,7 +281,11 @@ init_job_options(jobopt_T *opt)
     static void
 setup_job_options(jobopt_T *opt, int rows, int cols)
 {
+#ifndef WIN3264
+    /* Win32: Redirecting the job output won't work, thus always connect stdout
+     * here. */
     if (!(opt->jo_set & JO_OUT_IO))
+#endif
     {
        /* Connect stdout to the terminal. */
        opt->jo_io[PART_OUT] = JIO_BUFFER;
@@ -292,7 +294,11 @@ setup_job_options(jobopt_T *opt, int rows, int cols)
        opt->jo_set |= JO_OUT_IO + JO_OUT_BUF + JO_OUT_MODIFIABLE;
     }
 
+#ifndef WIN3264
+    /* Win32: Redirecting the job output won't work, thus always connect stderr
+     * here. */
     if (!(opt->jo_set & JO_ERR_IO))
+#endif
     {
        /* Connect stderr to the terminal. */
        opt->jo_io[PART_ERR] = JIO_BUFFER;
@@ -350,6 +356,7 @@ term_start(
     int                res;
     buf_T      *newbuf;
     int                vertical = opt->jo_vertical || (cmdmod.split & WSP_VERT);
+    jobopt_T   orig_opt;  // only partly filled
 
     if (check_restricted() || check_secure())
        return NULL;
@@ -517,6 +524,9 @@ term_start(
     curbuf->b_p_ma = FALSE;
 
     set_term_and_win_size(term);
+#ifdef WIN3264
+    mch_memmove(orig_opt.jo_io, opt->jo_io, sizeof(orig_opt.jo_io));
+#endif
     setup_job_options(opt, term->tl_rows, term->tl_cols);
 
     if (flags & TERM_START_NOJOB)
@@ -582,7 +592,7 @@ term_start(
            && STRCMP(argvar->vval.v_string, "NONE") == 0)
        res = create_pty_only(term, opt);
     else
-       res = term_and_job_init(term, argvar, argv, opt);
+       res = term_and_job_init(term, argvar, argv, opt, &orig_opt);
 
     newbuf = curbuf;
     if (res == OK)
@@ -823,6 +833,10 @@ free_terminal(buf_T *buf)
     vim_free(term->tl_status_text);
     vim_free(term->tl_opencmd);
     vim_free(term->tl_eof_chars);
+#ifdef WIN3264
+    if (term->tl_out_fd != NULL)
+       fclose(term->tl_out_fd);
+#endif
     if (desired_cursor_color == term->tl_cursor_color)
        desired_cursor_color = (char_u *)"";
     vim_free(term->tl_cursor_color);
@@ -918,6 +932,17 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
     size_t     len = STRLEN(msg);
     term_T     *term = buffer->b_term;
 
+#ifdef WIN3264
+    /* Win32: Cannot redirect output of the job, intercept it here and write to
+     * the file. */
+    if (term->tl_out_fd != NULL)
+    {
+       ch_log(channel, "Writing %d bytes to output file", (int)len);
+       fwrite(msg, len, 1, term->tl_out_fd);
+       return;
+    }
+#endif
+
     if (term->tl_vterm == NULL)
     {
        ch_log(channel, "NOT writing %d bytes to terminal", (int)len);
@@ -4740,14 +4765,10 @@ f_term_gettty(typval_T *argvars, typval_T *rettv)
        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]));
@@ -5239,7 +5260,8 @@ term_and_job_init(
        term_T      *term,
        typval_T    *argvar,
        char        **argv UNUSED,
-       jobopt_T    *opt)
+       jobopt_T    *opt,
+       jobopt_T    *orig_opt)
 {
     WCHAR          *cmd_wchar = NULL;
     WCHAR          *cwd_wchar = NULL;
@@ -5393,6 +5415,19 @@ term_and_job_init(
     ++job->jv_refcount;
     term->tl_job = job;
 
+    /* Redirecting stdout and stderr doesn't work at the job level.  Instead
+     * open the file here and handle it in.  opt->jo_io was changed in
+     * setup_job_options(), use the original flags here. */
+    if (orig_opt->jo_io[PART_OUT] == JIO_FILE)
+    {
+       char_u *fname = opt->jo_io_name[PART_OUT];
+
+       ch_log(channel, "Opening output file %s", fname);
+       term->tl_out_fd = mch_fopen((char *)fname, WRITEBIN);
+       if (term->tl_out_fd == NULL)
+           EMSG2(_(e_notopen), fname);
+    }
+
     return OK;
 
 failed:
@@ -5546,7 +5581,8 @@ term_and_job_init(
        term_T      *term,
        typval_T    *argvar,
        char        **argv,
-       jobopt_T    *opt)
+       jobopt_T    *opt,
+       jobopt_T    *orig_opt UNUSED)
 {
     create_vterm(term, term->tl_rows, term->tl_cols);
 
index 8b63dee4eec891cededf162cbfb6a3519161df68..5dfe0275d4b25f13cee02ea9a2919f3399c7fe9e 100644 (file)
@@ -675,18 +675,15 @@ func Test_terminal_wrong_options()
 endfunc
 
 func Test_terminal_redir_file()
-  " TODO: this should work on MS-Window
-  if has('unix')
-    let cmd = Get_cat_123_cmd()
-    let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xfile'})
-    call term_wait(buf)
-    call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
-    call assert_match('123', readfile('Xfile')[0])
-    let g:job = term_getjob(buf)
-    call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
-    call delete('Xfile')
-    bwipe
-  endif
+  let cmd = Get_cat_123_cmd()
+  let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xfile'})
+  call term_wait(buf)
+  call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
+  call assert_match('123', readfile('Xfile')[0])
+  let g:job = term_getjob(buf)
+  call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
+  call delete('Xfile')
+  bwipe
 
   if has('unix')
     call writefile(['one line'], 'Xfile')
index 0e1cdff0d2a071043f7d6a7a698c97fe315e811c..70e4bcdca3821b83dced18af00609a385fd70ffd 100644 (file)
@@ -761,6 +761,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1801,
 /**/
     1800,
 /**/