Patch 7.4.1310
Problem:    Jobs don't open a channel.
Solution:   Create pipes and add them to the channel.  Add ch_logfile().
            Only Unix for now.
Files:      src/channel.c, src/eval.c, src/os_unix.c, src/structs.h,
            src/gui_w48.c, src/proto/channel.pro, src/testdir/test_channel.vim,
            src/testdir/test_channel_pipe.py, runtime/doc/eval.txt


*** ../vim-7.4.1309/src/channel.c       2016-02-10 21:07:09.016869231 +0100
--- src/channel.c       2016-02-13 16:51:03.982964196 +0100
***************
*** 14,35 ****
  
  #if defined(FEAT_CHANNEL) || defined(PROTO)
  
- /*
-  * Change the zero to 1 to enable debugging.
-  * This will write a file "channel_debug.log".
-  */
- #if 0
- # define CHERROR(fmt, arg) cherror(fmt, arg)
- # define CHLOG(idx, send, buf) chlog(idx, send, buf)
- # define CHFILE "channel_debug.log"
- 
- static void cherror(char *fmt, char *arg);
- static void chlog(int send, char_u *buf);
- #else
- # define CHERROR(fmt, arg)
- # define CHLOG(idx, send, buf)
- #endif
- 
  /* TRUE when netbeans is running with a GUI. */
  #ifdef FEAT_GUI
  # define CH_HAS_GUI (gui.in_use || gui.starting)
--- 14,19 ----
***************
*** 70,132 ****
  extern HWND s_hwnd;                   /* Gvim's Window handle */
  #endif
  
- struct readqueue
- {
-     char_u            *buffer;
-     struct readqueue  *next;
-     struct readqueue  *prev;
- };
- typedef struct readqueue readq_T;
- 
- struct jsonqueue
- {
-     typval_T          *value;
-     struct jsonqueue  *next;
-     struct jsonqueue  *prev;
- };
- typedef struct jsonqueue jsonq_T;
- 
- struct cbqueue
- {
-     char_u            *callback;
-     int                       seq_nr;
-     struct cbqueue    *next;
-     struct cbqueue    *prev;
- };
- typedef struct cbqueue cbq_T;
- 
- typedef struct {
-     sock_T    ch_fd;  /* the socket, -1 for a closed channel */
-     int             ch_idx;   /* used by channel_poll_setup() */
-     readq_T   ch_head;        /* dummy node, header for circular queue */
- 
-     int             ch_error; /* When TRUE an error was reported.  Avoids 
giving
-                        * pages full of error messages when the other side
-                        * has exited, only mention the first error until the
-                        * connection works again. */
- #ifdef FEAT_GUI_X11
-     XtInputId ch_inputHandler;  /* Cookie for input */
- #endif
- #ifdef FEAT_GUI_GTK
-     gint      ch_inputHandler;        /* Cookie for input */
- #endif
- #ifdef WIN32
-     int       ch_inputHandler;        /* simply ret.value of WSAAsyncSelect() 
*/
- #endif
- 
-     void      (*ch_close_cb)(void); /* callback for when channel is closed */
- 
-     int             ch_block_id;      /* ID that channel_read_json_block() is
-                                  waiting for */
-     char_u    *ch_callback;   /* function to call when a msg is not handled */
-     cbq_T     ch_cb_head;     /* dummy node for pre-request callbacks */
- 
-     ch_mode_T ch_mode;
-     jsonq_T   ch_json_head;   /* dummy node, header for circular queue */
- 
-     int       ch_timeout;     /* request timeout in msec */
- } channel_T;
- 
  /*
   * Information about all channels.
   * There can be gaps for closed channels, they will be reused later.
--- 54,59 ----
***************
*** 134,143 ****
  static channel_T *channels = NULL;
  static int channel_count = 0;
  
! /*
!  * TODO: open debug file when desired.
!  */
! FILE *debugfd = NULL;
  
  #ifdef _WIN32
  # undef PERROR
--- 61,167 ----
  static channel_T *channels = NULL;
  static int channel_count = 0;
  
! /* Log file opened with ch_logfile(). */
! static FILE *log_fd = NULL;
! 
!     void
! ch_logfile(FILE *file)
! {
!     if (log_fd != NULL)
!       fclose(log_fd);
!     log_fd = file;
!     if (log_fd != NULL)
!       fprintf(log_fd, "==== start log session ====\n");
! }
! 
!     static void
! ch_log_lead(char *what, int ch_idx)
! {
!     if (log_fd != NULL)
!     {
!       if (ch_idx >= 0)
!           fprintf(log_fd, "%son %d: ", what, ch_idx);
!       else
!           fprintf(log_fd, "%s: ", what);
!     }
! }
! 
!     static void
! ch_log(int ch_idx, char *msg)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("", ch_idx);
!       fputs(msg, log_fd);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_logn(int ch_idx, char *msg, int nr)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("", ch_idx);
!       fprintf(log_fd, msg, nr);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_logs(int ch_idx, char *msg, char *name)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("", ch_idx);
!       fprintf(log_fd, msg, name);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_logsn(int ch_idx, char *msg, char *name, int nr)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("", ch_idx);
!       fprintf(log_fd, msg, name, nr);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_error(int ch_idx, char *msg)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("ERR ", ch_idx);
!       fputs(msg, log_fd);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_errorn(int ch_idx, char *msg, int nr)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("ERR ", ch_idx);
!       fprintf(log_fd, msg, nr);
!       fflush(log_fd);
!     }
! }
! 
!     static void
! ch_errors(int ch_idx, char *msg, char *arg)
! {
!     if (log_fd != NULL)
!     {
!       ch_log_lead("ERR ", ch_idx);
!       fprintf(log_fd, msg, arg);
!       fflush(log_fd);
!     }
! }
  
  #ifdef _WIN32
  # undef PERROR
***************
*** 181,218 ****
  }
  #endif
  
! /*
!  * Add a new channel slot, return the index.
!  * The channel isn't actually used into ch_fd is set >= 0;
!  * Returns -1 if all channels are in use.
!  */
!     static int
! add_channel(void)
  {
-     int               idx;
      channel_T *ch;
  
!     if (channels != NULL)
!     {
!       for (idx = 0; idx < channel_count; ++idx)
!           if (channels[idx].ch_fd < 0)
!               /* re-use a closed channel slot */
!               return idx;
!       if (channel_count == MAX_OPEN_CHANNELS)
!           return -1;
!     }
!     else
!     {
!       channels = (channel_T *)alloc((int)sizeof(channel_T)
!                                                        * MAX_OPEN_CHANNELS);
!       if (channels == NULL)
!           return -1;
!     }
! 
!     ch = &channels[channel_count];
      (void)vim_memset(ch, 0, sizeof(channel_T));
  
!     ch->ch_fd = (sock_T)-1;
  #ifdef FEAT_GUI_X11
      ch->ch_inputHandler = (XtInputId)NULL;
  #endif
--- 205,224 ----
  }
  #endif
  
!     static void
! init_channel(int ch_idx)
  {
      channel_T *ch;
  
!     ch = &channels[ch_idx];
      (void)vim_memset(ch, 0, sizeof(channel_T));
  
!     ch->ch_sock = (sock_T)-1;
! #ifdef CHANNEL_PIPES
!     ch->ch_in = -1;
!     ch->ch_out = -1;
!     ch->ch_err = -1;
! #endif
  #ifdef FEAT_GUI_X11
      ch->ch_inputHandler = (XtInputId)NULL;
  #endif
***************
*** 231,237 ****
--- 237,276 ----
      ch->ch_json_head.prev = &ch->ch_json_head;
  
      ch->ch_timeout = 2000;
+ }
+ 
+ /*
+  * Add a new channel slot, return the index.
+  * The channel isn't actually used into ch_sock is set >= 0;
+  * Returns -1 if all channels are in use.
+  */
+     int
+ add_channel(void)
+ {
+     int               ch_idx;
  
+     if (channels != NULL)
+     {
+       for (ch_idx = 0; ch_idx < channel_count; ++ch_idx)
+           if (!channel_is_open(ch_idx))
+           {
+               /* re-use a closed channel slot */
+               init_channel(ch_idx);
+               ch_log(ch_idx, "Opening channel (used before)\n");
+               return ch_idx;
+           }
+       if (channel_count == MAX_OPEN_CHANNELS)
+           return -1;
+     }
+     else
+     {
+       channels = (channel_T *)alloc((int)sizeof(channel_T)
+                                                        * MAX_OPEN_CHANNELS);
+       if (channels == NULL)
+           return -1;
+     }
+     init_channel(channel_count);
+     ch_log(channel_count, "Opening new channel\n");
      return channel_count++;
  }
  
***************
*** 245,251 ****
                    int *unused1 UNUSED,
                    XtInputId *unused2 UNUSED)
  {
!     channel_read((int)(long)clientData);
  }
  #endif
  
--- 284,290 ----
                    int *unused1 UNUSED,
                    XtInputId *unused2 UNUSED)
  {
!     channel_read((int)(long)clientData, FALSE, "messageFromNetbeans");
  }
  #endif
  
***************
*** 255,281 ****
                    gint unused1 UNUSED,
                    GdkInputCondition unused2 UNUSED)
  {
!     channel_read((int)(long)clientData);
  }
  #endif
  
      static void
! channel_gui_register(int idx)
  {
!     channel_T *channel = &channels[idx];
  
      if (!CH_HAS_GUI)
        return;
  
  # ifdef FEAT_GUI_X11
      /* tell notifier we are interested in being called
       * when there is input on the editor connection socket
       */
      if (channel->ch_inputHandler == (XtInputId)NULL)
        channel->ch_inputHandler =
!           XtAppAddInput((XtAppContext)app_context, channel->ch_fd,
                         (XtPointer)(XtInputReadMask + XtInputExceptMask),
!                                  messageFromNetbeans, (XtPointer)(long)idx);
  # else
  #  ifdef FEAT_GUI_GTK
      /*
--- 294,321 ----
                    gint unused1 UNUSED,
                    GdkInputCondition unused2 UNUSED)
  {
!     channel_read((int)(long)clientData, FALSE, "messageFromNetbeans");
  }
  #endif
  
      static void
! channel_gui_register(int ch_idx)
  {
!     channel_T *channel = &channels[ch_idx];
  
      if (!CH_HAS_GUI)
        return;
  
+     /* TODO: pipes */
  # ifdef FEAT_GUI_X11
      /* tell notifier we are interested in being called
       * when there is input on the editor connection socket
       */
      if (channel->ch_inputHandler == (XtInputId)NULL)
        channel->ch_inputHandler =
!           XtAppAddInput((XtAppContext)app_context, channel->ch_sock,
                         (XtPointer)(XtInputReadMask + XtInputExceptMask),
!                               messageFromNetbeans, (XtPointer)(long)ch_idx);
  # else
  #  ifdef FEAT_GUI_GTK
      /*
***************
*** 284,292 ****
       */
      if (channel->ch_inputHandler == 0)
        channel->ch_inputHandler =
!           gdk_input_add((gint)channel->ch_fd, (GdkInputCondition)
                             ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
!                                   messageFromNetbeans, (gpointer)(long)idx);
  #  else
  #   ifdef FEAT_GUI_W32
      /*
--- 324,332 ----
       */
      if (channel->ch_inputHandler == 0)
        channel->ch_inputHandler =
!           gdk_input_add((gint)channel->ch_sock, (GdkInputCondition)
                             ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
!                                messageFromNetbeans, (gpointer)(long)ch_idx);
  #  else
  #   ifdef FEAT_GUI_W32
      /*
***************
*** 295,301 ****
       */
      if (channel->ch_inputHandler == -1)
        channel->ch_inputHandler =
!           WSAAsyncSelect(channel->ch_fd, s_hwnd, WM_NETBEANS, FD_READ);
  #   endif
  #  endif
  # endif
--- 335,341 ----
       */
      if (channel->ch_inputHandler == -1)
        channel->ch_inputHandler =
!           WSAAsyncSelect(channel->ch_sock, s_hwnd, WM_NETBEANS, FD_READ);
  #   endif
  #  endif
  # endif
***************
*** 311,325 ****
      int i;
  
      for (i = 0; i < channel_count; ++i)
!       if (channels[i].ch_fd >= 0)
            channel_gui_register(i);
  }
  
      static void
! channel_gui_unregister(int idx)
  {
!     channel_T *channel = &channels[idx];
  
  # ifdef FEAT_GUI_X11
      if (channel->ch_inputHandler != (XtInputId)NULL)
      {
--- 351,367 ----
      int i;
  
      for (i = 0; i < channel_count; ++i)
!       /* TODO: pipes */
!       if (channels[i].ch_sock >= 0)
            channel_gui_register(i);
  }
  
      static void
! channel_gui_unregister(int ch_idx)
  {
!     channel_T *channel = &channels[ch_idx];
  
+     /* TODO: pipes */
  # ifdef FEAT_GUI_X11
      if (channel->ch_inputHandler != (XtInputId)NULL)
      {
***************
*** 337,343 ****
  #   ifdef FEAT_GUI_W32
      if (channel->ch_inputHandler == 0)
      {
!       WSAAsyncSelect(channel->ch_fd, s_hwnd, 0, 0);
        channel->ch_inputHandler = -1;
      }
  #   endif
--- 379,385 ----
  #   ifdef FEAT_GUI_W32
      if (channel->ch_inputHandler == 0)
      {
!       WSAAsyncSelect(channel->ch_sock, s_hwnd, 0, 0);
        channel->ch_inputHandler = -1;
      }
  #   endif
***************
*** 348,354 ****
  #endif
  
  /*
!  * Open a channel to "hostname":"port".
   * Returns the channel number for success.
   * Returns a negative number for failure.
   */
--- 390,396 ----
  #endif
  
  /*
!  * Open a socket channel to "hostname":"port".
   * Returns the channel number for success.
   * Returns a negative number for failure.
   */
***************
*** 364,387 ****
  #else
      int                       port = port_in;
  #endif
!     int                       idx;
      int                       ret;
  
  #ifdef WIN32
      channel_init_winsock();
  #endif
  
!     idx = add_channel();
!     if (idx < 0)
      {
!       CHERROR("All channels are in use\n", "");
        EMSG(_("E897: All channels are in use"));
        return -1;
      }
  
      if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
      {
!       CHERROR("error in socket() in channel_open()\n", "");
        PERROR("E898: socket() in channel_open()");
        return -1;
      }
--- 406,429 ----
  #else
      int                       port = port_in;
  #endif
!     int                       ch_idx;
      int                       ret;
  
  #ifdef WIN32
      channel_init_winsock();
  #endif
  
!     ch_idx = add_channel();
!     if (ch_idx < 0)
      {
!       ch_error(-1, "All channels are in use.\n");
        EMSG(_("E897: All channels are in use"));
        return -1;
      }
  
      if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
      {
!       ch_error(-1, "in socket() in channel_open().\n");
        PERROR("E898: socket() in channel_open()");
        return -1;
      }
***************
*** 393,399 ****
      server.sin_port = htons(port);
      if ((host = gethostbyname(hostname)) == NULL)
      {
!       CHERROR("error in gethostbyname() in channel_open()\n", "");
        PERROR("E901: gethostbyname() in channel_open()");
        sock_close(sd);
        return -1;
--- 435,441 ----
      server.sin_port = htons(port);
      if ((host = gethostbyname(hostname)) == NULL)
      {
!       ch_error(-1, "in gethostbyname() in channel_open()\n");
        PERROR("E901: gethostbyname() in channel_open()");
        sock_close(sd);
        return -1;
***************
*** 412,432 ****
           )
        {
            SOCK_ERRNO;
!           CHERROR("channel_open: Connect failed with errno %d\n", errno);
            sock_close(sd);
            return -1;
        }
      }
  
      /* Try connecting to the server. */
      ret = connect(sd, (struct sockaddr *)&server, sizeof(server));
      SOCK_ERRNO;
      if (ret < 0)
      {
!       if (errno != EWOULDBLOCK && errno != EINPROGRESS)
        {
!           CHERROR("channel_open: Connect failed with errno %d\n", errno);
!           CHERROR("Cannot connect to port\n", "");
            PERROR(_("E902: Cannot connect to port"));
            sock_close(sd);
            return -1;
--- 454,480 ----
           )
        {
            SOCK_ERRNO;
!           ch_errorn(-1, "channel_open: Connect failed with errno %d\n",
!                                                                      errno);
            sock_close(sd);
            return -1;
        }
      }
  
      /* Try connecting to the server. */
+     ch_logsn(-1, "Connecting to %s port %d", hostname, port);
      ret = connect(sd, (struct sockaddr *)&server, sizeof(server));
      SOCK_ERRNO;
      if (ret < 0)
      {
!       if (errno != EWOULDBLOCK
! #ifdef EINPROGRESS
!                   && errno != EINPROGRESS
! #endif
!               )
        {
!           ch_errorn(-1, "channel_open: Connect failed with errno %d\n",
!                                                                      errno);
            PERROR(_("E902: Cannot connect to port"));
            sock_close(sd);
            return -1;
***************
*** 446,453 ****
        if (ret < 0)
        {
            SOCK_ERRNO;
!           CHERROR("channel_open: Connect failed with errno %d\n", errno);
!           CHERROR("Cannot connect to port\n", "");
            PERROR(_("E902: Cannot connect to port"));
            sock_close(sd);
            return -1;
--- 494,501 ----
        if (ret < 0)
        {
            SOCK_ERRNO;
!           ch_errorn(-1, "channel_open: Connect failed with errno %d\n",
!                                                                      errno);
            PERROR(_("E902: Cannot connect to port"));
            sock_close(sd);
            return -1;
***************
*** 477,483 ****
        if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
        {
            SOCK_ERRNO;
!           CHERROR("socket() retry in channel_open()\n", "");
            PERROR("E900: socket() retry in channel_open()");
            return -1;
        }
--- 525,531 ----
        if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
        {
            SOCK_ERRNO;
!           ch_log(-1, "socket() retry in channel_open()\n");
            PERROR("E900: socket() retry in channel_open()");
            return -1;
        }
***************
*** 490,496 ****
            while (retries-- && ((errno == ECONNREFUSED)
                                                     || (errno == EINTR)))
            {
!               CHERROR("retrying...\n", "");
                mch_delay(3000L, TRUE);
                ui_breakcheck();
                if (got_int)
--- 538,544 ----
            while (retries-- && ((errno == ECONNREFUSED)
                                                     || (errno == EINTR)))
            {
!               ch_log(-1, "retrying...\n");
                mch_delay(3000L, TRUE);
                ui_breakcheck();
                if (got_int)
***************
*** 509,515 ****
            if (!success)
            {
                /* Get here when the server can't be found. */
!               CHERROR("Cannot connect to port after retry\n", "");
                PERROR(_("E899: Cannot connect to port after retry2"));
                sock_close(sd);
                return -1;
--- 557,563 ----
            if (!success)
            {
                /* Get here when the server can't be found. */
!               ch_error(-1, "Cannot connect to port after retry\n");
                PERROR(_("E899: Cannot connect to port after retry2"));
                sock_close(sd);
                return -1;
***************
*** 517,567 ****
        }
      }
  
!     channels[idx].ch_fd = sd;
!     channels[idx].ch_close_cb = close_cb;
  
  #ifdef FEAT_GUI
!     channel_gui_register(idx);
  #endif
  
!     return idx;
  }
  
  /*
!  * Set the json mode of channel "idx" to "ch_mode".
   */
      void
! channel_set_json_mode(int idx, ch_mode_T ch_mode)
  {
!     channels[idx].ch_mode = ch_mode;
  }
  
  /*
!  * Set the read timeout of channel "idx".
   */
      void
! channel_set_timeout(int idx, int timeout)
  {
!     channels[idx].ch_timeout = timeout;
  }
  
  /*
!  * Set the callback for channel "idx".
   */
      void
! channel_set_callback(int idx, char_u *callback)
  {
!     vim_free(channels[idx].ch_callback);
!     channels[idx].ch_callback = vim_strsave(callback);
  }
  
  /*
!  * Set the callback for channel "idx" for the response with "id".
   */
      void
! channel_set_req_callback(int idx, char_u *callback, int id)
  {
!     cbq_T *cbhead = &channels[idx].ch_cb_head;
      cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T));
  
      if (item != NULL)
--- 565,633 ----
        }
      }
  
!     channels[ch_idx].ch_sock = sd;
!     channels[ch_idx].ch_close_cb = close_cb;
  
  #ifdef FEAT_GUI
!     channel_gui_register(ch_idx);
  #endif
  
!     return ch_idx;
! }
! 
! #if defined(CHANNEL_PIPES) || defined(PROTO)
!     void
! channel_set_pipes(int ch_idx, int in, int out, int err)
! {
!     channel_T *channel = &channels[ch_idx];
! 
!     channel->ch_in = in;
!     channel->ch_out = out;
!     channel->ch_err = err;
! }
! #endif
! 
!     void
! channel_set_job(int ch_idx, job_T *job)
! {
!     channels[ch_idx].ch_job = job;
  }
  
  /*
!  * Set the json mode of channel "ch_idx" to "ch_mode".
   */
      void
! channel_set_json_mode(int ch_idx, ch_mode_T ch_mode)
  {
!     channels[ch_idx].ch_mode = ch_mode;
  }
  
  /*
!  * Set the read timeout of channel "ch_idx".
   */
      void
! channel_set_timeout(int ch_idx, int timeout)
  {
!     channels[ch_idx].ch_timeout = timeout;
  }
  
  /*
!  * Set the callback for channel "ch_idx".
   */
      void
! channel_set_callback(int ch_idx, char_u *callback)
  {
!     vim_free(channels[ch_idx].ch_callback);
!     channels[ch_idx].ch_callback = vim_strsave(callback);
  }
  
  /*
!  * Set the callback for channel "ch_idx" for the response with "id".
   */
      void
! channel_set_req_callback(int ch_idx, char_u *callback, int id)
  {
!     cbq_T *cbhead = &channels[ch_idx].ch_cb_head;
      cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T));
  
      if (item != NULL)
***************
*** 576,591 ****
  }
  
  /*
!  * Invoke the "callback" on channel "idx".
   */
      static void
! invoke_callback(int idx, char_u *callback, typval_T *argv)
  {
      typval_T  rettv;
      int               dummy;
  
      argv[0].v_type = VAR_NUMBER;
!     argv[0].vval.v_number = idx;
  
      call_func(callback, (int)STRLEN(callback),
                             &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
--- 642,657 ----
  }
  
  /*
!  * Invoke the "callback" on channel "ch_idx".
   */
      static void
! invoke_callback(int ch_idx, char_u *callback, typval_T *argv)
  {
      typval_T  rettv;
      int               dummy;
  
      argv[0].v_type = VAR_NUMBER;
!     argv[0].vval.v_number = ch_idx;
  
      call_func(callback, (int)STRLEN(callback),
                             &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
***************
*** 602,610 ****
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_get(int idx)
  {
!     readq_T *head = &channels[idx].ch_head;
      readq_T *node;
      char_u *p;
  
--- 668,676 ----
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_get(int ch_idx)
  {
!     readq_T *head = &channels[ch_idx].ch_head;
      readq_T *node;
      char_u *p;
  
***************
*** 623,645 ****
   * Returns the whole buffer contents concatenated.
   */
      static char_u *
! channel_get_all(int idx)
  {
      /* Concatenate everything into one buffer.
       * TODO: avoid multiple allocations. */
!     while (channel_collapse(idx) == OK)
        ;
!     return channel_get(idx);
  }
  
  /*
!  * Collapses the first and second buffer in the channel "idx".
   * Returns FAIL if that is not possible.
   */
      int
! channel_collapse(int idx)
  {
!     readq_T *head = &channels[idx].ch_head;
      readq_T *node = head->next;
      char_u  *p;
  
--- 689,711 ----
   * Returns the whole buffer contents concatenated.
   */
      static char_u *
! channel_get_all(int ch_idx)
  {
      /* Concatenate everything into one buffer.
       * TODO: avoid multiple allocations. */
!     while (channel_collapse(ch_idx) == OK)
        ;
!     return channel_get(ch_idx);
  }
  
  /*
!  * Collapses the first and second buffer in the channel "ch_idx".
   * Returns FAIL if that is not possible.
   */
      int
! channel_collapse(int ch_idx)
  {
!     readq_T *head = &channels[ch_idx].ch_head;
      readq_T *node = head->next;
      char_u  *p;
  
***************
*** 799,810 ****
  }
  
  /*
!  * Execute a command received over channel "idx".
   * "cmd" is the command string, "arg2" the second argument.
   * "arg3" is the third argument, NULL if missing.
   */
      static void
! channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
  {
      char_u *arg;
  
--- 865,876 ----
  }
  
  /*
!  * Execute a command received over channel "ch_idx".
   * "cmd" is the command string, "arg2" the second argument.
   * "arg3" is the third argument, NULL if missing.
   */
      static void
! channel_exe_cmd(int ch_idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
  {
      char_u *arg;
  
***************
*** 862,868 ****
            typval_T    *tv;
            typval_T    err_tv;
            char_u      *json = NULL;
!           channel_T   *channel = &channels[idx];
            int         options = channel->ch_mode == MODE_JS ? JSON_JS : 0;
  
            /* Don't pollute the display with errors. */
--- 928,934 ----
            typval_T    *tv;
            typval_T    err_tv;
            char_u      *json = NULL;
!           channel_T   *channel = &channels[ch_idx];
            int         options = channel->ch_mode == MODE_JS ? JSON_JS : 0;
  
            /* Don't pollute the display with errors. */
***************
*** 885,891 ****
                }
                if (json != NULL)
                {
!                   channel_send(idx, json, "eval");
                    vim_free(json);
                }
            }
--- 951,957 ----
                }
                if (json != NULL)
                {
!                   channel_send(ch_idx, json, "eval");
                    vim_free(json);
                }
            }
***************
*** 899,909 ****
  }
  
  /*
!  * Invoke a callback for channel "idx" if needed.
   * Return OK when a message was handled, there might be another one.
   */
      static int
! may_invoke_callback(int idx)
  {
      char_u    *msg = NULL;
      typval_T  *listtv = NULL;
--- 965,975 ----
  }
  
  /*
!  * Invoke a callback for channel "ch_idx" if needed.
   * Return OK when a message was handled, there might be another one.
   */
      static int
! may_invoke_callback(int ch_idx)
  {
      char_u    *msg = NULL;
      typval_T  *listtv = NULL;
***************
*** 911,917 ****
      typval_T  *typetv;
      typval_T  argv[3];
      int               seq_nr = -1;
!     channel_T *channel = &channels[idx];
      ch_mode_T ch_mode = channel->ch_mode;
  
      if (channel->ch_close_cb != NULL)
--- 977,983 ----
      typval_T  *typetv;
      typval_T  argv[3];
      int               seq_nr = -1;
!     channel_T *channel = &channels[ch_idx];
      ch_mode_T ch_mode = channel->ch_mode;
  
      if (channel->ch_close_cb != NULL)
***************
*** 921,931 ****
      if (ch_mode != MODE_RAW)
      {
        /* Get any json message in the queue. */
!       if (channel_get_json(idx, -1, &listtv) == FAIL)
        {
            /* Parse readahead, return when there is still no message. */
!           channel_parse_json(idx);
!           if (channel_get_json(idx, -1, &listtv) == FAIL)
                return FALSE;
        }
  
--- 987,997 ----
      if (ch_mode != MODE_RAW)
      {
        /* Get any json message in the queue. */
!       if (channel_get_json(ch_idx, -1, &listtv) == FAIL)
        {
            /* Parse readahead, return when there is still no message. */
!           channel_parse_json(ch_idx);
!           if (channel_get_json(ch_idx, -1, &listtv) == FAIL)
                return FALSE;
        }
  
***************
*** 940,995 ****
            /* ["cmd", arg] or ["cmd", arg, arg] */
            if (list->lv_len == 3)
                arg3 = &list->lv_last->li_tv;
!           channel_exe_cmd(idx, cmd, &argv[1], arg3);
            clear_tv(listtv);
            return TRUE;
        }
  
        if (typetv->v_type != VAR_NUMBER)
        {
!           /* TODO: give error */
            clear_tv(listtv);
            return FALSE;
        }
        seq_nr = typetv->vval.v_number;
      }
!     else if (channel_peek(idx) == NULL)
      {
        /* nothing to read on raw channel */
        return FALSE;
      }
      else
      {
        /* For a raw channel we don't know where the message ends, just get
         * everything. */
!       msg = channel_get_all(idx);
        argv[1].v_type = VAR_STRING;
        argv[1].vval.v_string = msg;
      }
  
      if (seq_nr > 0)
      {
!       cbq_T *cbhead = &channel->ch_cb_head;
!       cbq_T *cbitem = cbhead->next;
  
        /* invoke the one-time callback with the matching nr */
        while (cbitem != cbhead)
        {
            if (cbitem->seq_nr == seq_nr)
            {
!               invoke_callback(idx, cbitem->callback, argv);
                remove_cb_node(cbitem);
                break;
            }
            cbitem = cbitem->next;
        }
      }
      else if (channel->ch_callback != NULL)
      {
        /* invoke the channel callback */
!       invoke_callback(idx, channel->ch_callback, argv);
      }
!     /* else: drop the message TODO: give error */
  
      if (listtv != NULL)
        clear_tv(listtv);
--- 1006,1074 ----
            /* ["cmd", arg] or ["cmd", arg, arg] */
            if (list->lv_len == 3)
                arg3 = &list->lv_last->li_tv;
!           ch_logs(ch_idx, "Executing %s command", (char *)cmd);
!           channel_exe_cmd(ch_idx, cmd, &argv[1], arg3);
            clear_tv(listtv);
            return TRUE;
        }
  
        if (typetv->v_type != VAR_NUMBER)
        {
!           ch_error(ch_idx,
!                     "Dropping message with invalid sequence number type\n");
            clear_tv(listtv);
            return FALSE;
        }
        seq_nr = typetv->vval.v_number;
      }
!     else if (channel_peek(ch_idx) == NULL)
      {
        /* nothing to read on raw channel */
        return FALSE;
      }
      else
      {
+       /* If there is no callback, don't do anything. */
+       if (channel->ch_callback == NULL)
+           return FALSE;
+ 
        /* For a raw channel we don't know where the message ends, just get
         * everything. */
!       msg = channel_get_all(ch_idx);
        argv[1].v_type = VAR_STRING;
        argv[1].vval.v_string = msg;
      }
  
      if (seq_nr > 0)
      {
!       cbq_T   *cbhead = &channel->ch_cb_head;
!       cbq_T   *cbitem = cbhead->next;
!       int     done = FALSE;
  
        /* invoke the one-time callback with the matching nr */
        while (cbitem != cbhead)
        {
            if (cbitem->seq_nr == seq_nr)
            {
!               ch_log(ch_idx, "Invoking one-time callback\n");
!               invoke_callback(ch_idx, cbitem->callback, argv);
                remove_cb_node(cbitem);
+               done = TRUE;
                break;
            }
            cbitem = cbitem->next;
        }
+       if (!done)
+           ch_log(ch_idx, "Dropping message without callback\n");
      }
      else if (channel->ch_callback != NULL)
      {
        /* invoke the channel callback */
!       ch_log(ch_idx, "Invoking channel callback\n");
!       invoke_callback(ch_idx, channel->ch_callback, argv);
      }
!     else
!       ch_log(ch_idx, "Dropping message\n");
  
      if (listtv != NULL)
        clear_tv(listtv);
***************
*** 999,1038 ****
  }
  
  /*
!  * Return TRUE when channel "idx" is open.
!  * Also returns FALSE or invalid "idx".
   */
      int
! channel_is_open(int idx)
  {
!     return idx >= 0 && idx < channel_count && channels[idx].ch_fd >= 0;
  }
  
  /*
!  * Close channel "idx".
   * This does not trigger the close callback.
   */
      void
! channel_close(int idx)
  {
!     channel_T *channel = &channels[idx];
      jsonq_T   *jhead;
      cbq_T     *cbhead;
  
!     if (channel->ch_fd >= 0)
      {
!       sock_close(channel->ch_fd);
!       channel->ch_fd = -1;
        channel->ch_close_cb = NULL;
  #ifdef FEAT_GUI
!       channel_gui_unregister(idx);
  #endif
        vim_free(channel->ch_callback);
        channel->ch_callback = NULL;
        channel->ch_timeout = 2000;
  
!       while (channel_peek(idx) != NULL)
!           vim_free(channel_get(idx));
  
        cbhead = &channel->ch_cb_head;
        while (cbhead->next != cbhead)
--- 1078,1139 ----
  }
  
  /*
!  * Return TRUE when channel "ch_idx" is open for writing to.
!  * Also returns FALSE or invalid "ch_idx".
   */
      int
! channel_can_write_to(int ch_idx)
  {
!     return ch_idx >= 0 && ch_idx < channel_count
!                 && (channels[ch_idx].ch_sock >= 0
! #ifdef CHANNEL_PIPES
!                         || channels[ch_idx].ch_in >= 0
! #endif
!                         );
  }
  
  /*
!  * Return TRUE when channel "ch_idx" is open for reading or writing.
!  * Also returns FALSE or invalid "ch_idx".
!  */
!     int
! channel_is_open(int ch_idx)
! {
!     return ch_idx >= 0 && ch_idx < channel_count
!                 && (channels[ch_idx].ch_sock >= 0
! #ifdef CHANNEL_PIPES
!                         || channels[ch_idx].ch_in >= 0
!                         || channels[ch_idx].ch_out >= 0
!                         || channels[ch_idx].ch_err >= 0
! #endif
!                         );
! }
! 
! /*
!  * Close channel "ch_idx".
   * This does not trigger the close callback.
   */
      void
! channel_close(int ch_idx)
  {
!     channel_T *channel = &channels[ch_idx];
      jsonq_T   *jhead;
      cbq_T     *cbhead;
  
!     if (channel->ch_sock >= 0)
      {
!       sock_close(channel->ch_sock);
!       channel->ch_sock = -1;
        channel->ch_close_cb = NULL;
  #ifdef FEAT_GUI
!       channel_gui_unregister(ch_idx);
  #endif
        vim_free(channel->ch_callback);
        channel->ch_callback = NULL;
        channel->ch_timeout = 2000;
  
!       while (channel_peek(ch_idx) != NULL)
!           vim_free(channel_get(ch_idx));
  
        cbhead = &channel->ch_cb_head;
        while (cbhead->next != cbhead)
***************
*** 1045,1061 ****
            remove_json_node(jhead->next);
        }
      }
  }
  
  /*
!  * Store "buf[len]" on channel "idx".
   * Returns OK or FAIL.
   */
      int
! channel_save(int idx, char_u *buf, int len)
  {
      readq_T *node;
!     readq_T *head = &channels[idx].ch_head;
  
      node = (readq_T *)alloc(sizeof(readq_T));
      if (node == NULL)
--- 1146,1179 ----
            remove_json_node(jhead->next);
        }
      }
+ #if defined(CHANNEL_PIPES)
+     if (channel->ch_in >= 0)
+     {
+       close(channel->ch_in);
+       channel->ch_in = -1;
+     }
+     if (channel->ch_out >= 0)
+     {
+       close(channel->ch_out);
+       channel->ch_out = -1;
+     }
+     if (channel->ch_err >= 0)
+     {
+       close(channel->ch_err);
+       channel->ch_err = -1;
+     }
+ #endif
  }
  
  /*
!  * Store "buf[len]" on channel "ch_idx".
   * Returns OK or FAIL.
   */
      int
! channel_save(int ch_idx, char_u *buf, int len)
  {
      readq_T *node;
!     readq_T *head = &channels[ch_idx].ch_head;
  
      node = (readq_T *)alloc(sizeof(readq_T));
      if (node == NULL)
***************
*** 1075,1086 ****
      head->prev->next = node;
      head->prev = node;
  
!     if (debugfd != NULL)
      {
!       fprintf(debugfd, "RECV on %d: ", idx);
!       if (fwrite(buf, len, 1, debugfd) != 1)
            return FAIL;
!       fprintf(debugfd, "\n");
      }
      return OK;
  }
--- 1193,1205 ----
      head->prev->next = node;
      head->prev = node;
  
!     if (log_fd != NULL)
      {
!       ch_log_lead("RECV ", ch_idx);
!       fprintf(log_fd, "'");
!       if (fwrite(buf, len, 1, log_fd) != 1)
            return FAIL;
!       fprintf(log_fd, "'\n");
      }
      return OK;
  }
***************
*** 1090,1098 ****
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_peek(int idx)
  {
!     readq_T *head = &channels[idx].ch_head;
  
      if (head->next == head || head->next == NULL)
        return NULL;
--- 1209,1217 ----
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_peek(int ch_idx)
  {
!     readq_T *head = &channels[ch_idx].ch_head;
  
      if (head->next == head || head->next == NULL)
        return NULL;
***************
*** 1100,1111 ****
  }
  
  /*
!  * Clear the read buffer on channel "idx".
   */
      void
! channel_clear(int idx)
  {
!     readq_T *head = &channels[idx].ch_head;
      readq_T *node = head->next;
      readq_T *next;
  
--- 1219,1230 ----
  }
  
  /*
!  * Clear the read buffer on channel "ch_idx".
   */
      void
! channel_clear(int ch_idx)
  {
!     readq_T *head = &channels[ch_idx].ch_head;
      readq_T *node = head->next;
      readq_T *next;
  
***************
*** 1136,1148 ****
   * Always returns OK for FEAT_GUI_W32.
   */
      static int
! channel_wait(int fd, int timeout)
  {
  #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32)
      struct timeval    tval;
      fd_set            rfds;
      int                       ret;
  
      FD_ZERO(&rfds);
      FD_SET(fd, &rfds);
      tval.tv_sec = timeout / 1000;
--- 1255,1269 ----
   * Always returns OK for FEAT_GUI_W32.
   */
      static int
! channel_wait(int ch_idx, int fd, int timeout)
  {
  #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32)
      struct timeval    tval;
      fd_set            rfds;
      int                       ret;
  
+     if (timeout > 0)
+       ch_logn(ch_idx, "Waiting for %d msec\n", timeout);
      FD_ZERO(&rfds);
      FD_SET(fd, &rfds);
      tval.tv_sec = timeout / 1000;
***************
*** 1155,1171 ****
--- 1276,1300 ----
            continue;
  # endif
        if (ret <= 0)
+       {
+           ch_log(ch_idx, "Nothing to read\n");
            return FAIL;
+       }
        break;
      }
  #else
  # ifdef HAVE_POLL
      struct pollfd     fds;
  
+     if (timeout > 0)
+       ch_logn(ch_idx, "Waiting for %d msec\n", timeout);
      fds.fd = fd;
      fds.events = POLLIN;
      if (poll(&fds, 1, timeout) <= 0)
+     {
+       ch_log(ch_idx, "Nothing to read\n");
        return FAIL;
+     }
  # endif
  #endif
      return OK;
***************
*** 1183,1204 ****
  }
  
  /*
!  * Read from channel "idx" for as long as there is something to read.
   * The data is put in the read queue.
   */
      void
! channel_read(int idx)
  {
      static char_u     *buf = NULL;
      int                       len = 0;
      int                       readlen = 0;
!     channel_T         *channel = &channels[idx];
  
!     if (channel->ch_fd < 0)
!     {
!       CHLOG(idx, FALSE, "channel_read() called while socket is closed\n");
        return;
!     }
  
      /* Allocate a buffer to read into. */
      if (buf == NULL)
--- 1312,1354 ----
  }
  
  /*
!  * Get the file descriptor to read from, either the socket or stdout.
!  */
!     static int
! get_read_fd(int ch_idx, int use_stderr)
! {
!     channel_T         *channel = &channels[ch_idx];
! 
!     if (channel->ch_sock >= 0)
!       return channel->ch_sock;
! #if defined(CHANNEL_PIPES)
!     if (!use_stderr && channel->ch_out >= 0)
!       return channel->ch_out;
!     if (use_stderr && channel->ch_err >= 0)
!       return channel->ch_err;
! #endif
!     ch_error(ch_idx, "channel_read() called while socket is closed\n");
!     return -1;
! }
! 
! /*
!  * Read from channel "ch_idx" for as long as there is something to read.
   * The data is put in the read queue.
   */
      void
! channel_read(int ch_idx, int use_stderr, char *func)
  {
+     channel_T         *channel = &channels[ch_idx];
      static char_u     *buf = NULL;
      int                       len = 0;
      int                       readlen = 0;
!     int                       fd;
!     int                       use_socket = FALSE;
  
!     fd = get_read_fd(ch_idx, use_stderr);
!     if (fd < 0)
        return;
!     use_socket = channel->ch_sock >= 0;
  
      /* Allocate a buffer to read into. */
      if (buf == NULL)
***************
*** 1213,1232 ****
       * MAXMSGSIZE long. */
      for (;;)
      {
!       if (channel_wait(channel->ch_fd, 0) == FAIL)
            break;
!       len = sock_read(channel->ch_fd, buf, MAXMSGSIZE);
        if (len <= 0)
            break;      /* error or nothing more to read */
  
        /* Store the read message in the queue. */
!       channel_save(idx, buf, len);
        readlen += len;
        if (len < MAXMSGSIZE)
            break;      /* did read everything that's available */
      }
  #ifdef FEAT_GUI_W32
!     if (len == SOCKET_ERROR)
      {
        /* For Win32 GUI channel_wait() always returns OK and we handle the
         * situation that there is nothing to read here.
--- 1363,1385 ----
       * MAXMSGSIZE long. */
      for (;;)
      {
!       if (channel_wait(ch_idx, fd, 0) == FAIL)
            break;
!       if (use_socket)
!           len = sock_read(fd, buf, MAXMSGSIZE);
!       else
!           len = read(fd, buf, MAXMSGSIZE);
        if (len <= 0)
            break;      /* error or nothing more to read */
  
        /* Store the read message in the queue. */
!       channel_save(ch_idx, buf, len);
        readlen += len;
        if (len < MAXMSGSIZE)
            break;      /* did read everything that's available */
      }
  #ifdef FEAT_GUI_W32
!     if (use_socket && len == SOCKET_ERROR)
      {
        /* For Win32 GUI channel_wait() always returns OK and we handle the
         * situation that there is nothing to read here.
***************
*** 1249,1292 ****
         *                  -> gui event loop or select loop
         *                      -> channel_read()
         */
!       channel_save(idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG));
  
!       channel_close(idx);
!       if (channel->ch_close_cb != NULL)
!           (*channel->ch_close_cb)();
  
        if (len < 0)
        {
!           /* Todo: which channel? */
!           CHERROR("%s(): cannot from channel\n", "channel_read");
            PERROR(_("E896: read from channel"));
        }
      }
  
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
      if (CH_HAS_GUI && gtk_main_level() > 0)
        gtk_main_quit();
  #endif
  }
  
  /*
!  * Read from raw channel "idx".  Blocks until there is something to read or
   * the timeout expires.
   * Returns what was read in allocated memory.
   * Returns NULL in case of error or timeout.
   */
      char_u *
! channel_read_block(int idx)
  {
!     if (channel_peek(idx) == NULL)
      {
        /* Wait for up to the channel timeout. */
!       if (channel_wait(channels[idx].ch_fd, channels[idx].ch_timeout) == FAIL)
            return NULL;
!       channel_read(idx);
      }
  
!     return channel_get_all(idx);
  }
  
  /*
--- 1402,1463 ----
         *                  -> gui event loop or select loop
         *                      -> channel_read()
         */
!       ch_errors(ch_idx, "%s(): Cannot read\n", func);
!       channel_save(ch_idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG));
  
!       if (use_socket)
!       {
!           channel_close(ch_idx);
!           if (channel->ch_close_cb != NULL)
!               (*channel->ch_close_cb)();
!       }
! #if defined(CHANNEL_PIPES)
!       else
!       {
!           close(fd);
!           channel->ch_out = -1;
!       }
! #endif
  
        if (len < 0)
        {
!           ch_error(ch_idx, "channel_read(): cannot read from channel\n");
            PERROR(_("E896: read from channel"));
        }
      }
  
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
+     /* signal the main loop that there is something to read */
      if (CH_HAS_GUI && gtk_main_level() > 0)
        gtk_main_quit();
  #endif
  }
  
  /*
!  * Read from raw channel "ch_idx".  Blocks until there is something to read or
   * the timeout expires.
   * Returns what was read in allocated memory.
   * Returns NULL in case of error or timeout.
   */
      char_u *
! channel_read_block(int ch_idx)
  {
!     ch_log(ch_idx, "Reading raw\n");
!     if (channel_peek(ch_idx) == NULL)
      {
+       int fd = get_read_fd(ch_idx, FALSE);
+ 
+       ch_log(ch_idx, "No readahead\n");
        /* Wait for up to the channel timeout. */
!       if (fd < 0 || channel_wait(ch_idx, fd,
!                                        channels[ch_idx].ch_timeout) == FAIL)
            return NULL;
!       channel_read(ch_idx, FALSE, "channel_read_block");
      }
  
!     /* TODO: only get the first message */
!     ch_log(ch_idx, "Returning readahead\n");
!     return channel_get_all(ch_idx);
  }
  
  /*
***************
*** 1299,1305 ****
--- 1470,1478 ----
  {
      int               more;
      channel_T *channel = &channels[ch_idx];
+     int               fd;
  
+     ch_log(ch_idx, "Reading JSON\n");
      channel->ch_block_id = id;
      for (;;)
      {
***************
*** 1320,1329 ****
                continue;
  
            /* Wait for up to the channel timeout. */
!           if (channel->ch_fd < 0 || channel_wait(channel->ch_fd,
!                                                channel->ch_timeout) == FAIL)
                break;
!           channel_read(ch_idx);
        }
      }
      channel->ch_block_id = 0;
--- 1493,1502 ----
                continue;
  
            /* Wait for up to the channel timeout. */
!           fd = get_read_fd(ch_idx, FALSE);
!           if (fd < 0 || channel_wait(ch_idx, fd, channel->ch_timeout) == FAIL)
                break;
!           channel_read(ch_idx, FALSE, "channel_read_json_block");
        }
      }
      channel->ch_block_id = 0;
***************
*** 1336,1380 ****
   * Returns -1 when the socket isn't found.
   */
      int
! channel_socket2idx(sock_T fd)
  {
      int i;
  
      if (fd >= 0)
        for (i = 0; i < channel_count; ++i)
!           if (channels[i].ch_fd == fd)
                return i;
      return -1;
  }
  # endif
  
  /*
!  * Write "buf" (NUL terminated string) to channel "idx".
   * When "fun" is not NULL an error message might be given.
   * Return FAIL or OK.
   */
      int
! channel_send(int idx, char_u *buf, char *fun)
  {
!     channel_T *channel = &channels[idx];
      int               len = (int)STRLEN(buf);
  
!     if (channel->ch_fd < 0)
      {
        if (!channel->ch_error && fun != NULL)
        {
!           CHERROR("    %s(): write while not connected\n", fun);
            EMSG2("E630: %s(): write while not connected", fun);
        }
        channel->ch_error = TRUE;
        return FAIL;
      }
  
!     if (sock_write(channel->ch_fd, buf, len) != len)
      {
        if (!channel->ch_error && fun != NULL)
        {
!           CHERROR("    %s(): write failed\n", fun);
            EMSG2("E631: %s(): write failed", fun);
        }
        channel->ch_error = TRUE;
--- 1509,1583 ----
   * Returns -1 when the socket isn't found.
   */
      int
! channel_fd2idx(sock_T fd)
  {
      int i;
  
      if (fd >= 0)
        for (i = 0; i < channel_count; ++i)
!           if (channels[i].ch_sock == fd
! #  if defined(CHANNEL_PIPES)
!                   || channels[i].ch_out == fd
!                   || channels[i].ch_err == fd
! #  endif
!                   )
                return i;
      return -1;
  }
  # endif
  
  /*
!  * Write "buf" (NUL terminated string) to channel "ch_idx".
   * When "fun" is not NULL an error message might be given.
   * Return FAIL or OK.
   */
      int
! channel_send(int ch_idx, char_u *buf, char *fun)
  {
!     channel_T *channel = &channels[ch_idx];
      int               len = (int)STRLEN(buf);
+     int               res;
+     int               fd;
+     int               use_socket = FALSE;
  
!     if (channel->ch_sock >= 0)
!     {
!       fd = channel->ch_sock;
!       use_socket = TRUE;
!     }
! #if defined(CHANNEL_PIPES)
!     else if (channel->ch_in >= 0)
!       fd = channel->ch_in;
! #endif
!     if (fd < 0)
      {
        if (!channel->ch_error && fun != NULL)
        {
!           ch_errors(ch_idx, "%s(): write while not connected\n", fun);
            EMSG2("E630: %s(): write while not connected", fun);
        }
        channel->ch_error = TRUE;
        return FAIL;
      }
  
!     if (log_fd != NULL)
!     {
!       ch_log_lead("SEND ", ch_idx);
!       fprintf(log_fd, "'");
!       ignored = fwrite(buf, len, 1, log_fd);
!       fprintf(log_fd, "'\n");
!       fflush(log_fd);
!     }
! 
!     if (use_socket)
!       res = sock_write(fd, buf, len);
!     else
!       res = write(fd, buf, len);
!     if (res != len)
      {
        if (!channel->ch_error && fun != NULL)
        {
!           ch_errors(ch_idx, "%s(): write failed\n", fun);
            EMSG2("E631: %s(): write failed", fun);
        }
        channel->ch_error = TRUE;
***************
*** 1399,1413 ****
      struct pollfd *fds = fds_in;
  
      for (i = 0; i < channel_count; ++i)
!       if (channels[i].ch_fd >= 0)
        {
!           channels[i].ch_idx = nfd;
!           fds[nfd].fd = channels[i].ch_fd;
            fds[nfd].events = POLLIN;
            nfd++;
        }
        else
!           channels[i].ch_idx = -1;
  
      return nfd;
  }
--- 1602,1640 ----
      struct pollfd *fds = fds_in;
  
      for (i = 0; i < channel_count; ++i)
!     {
!       if (channels[i].ch_sock >= 0)
!       {
!           channels[i].ch_sock_idx = nfd;
!           fds[nfd].fd = channels[i].ch_sock;
!           fds[nfd].events = POLLIN;
!           nfd++;
!       }
!       else
!           channels[i].ch_sock_idx = -1;
! 
! #  ifdef CHANNEL_PIPES
!       if (channels[i].ch_out >= 0)
!       {
!           channels[i].ch_out_idx = nfd;
!           fds[nfd].fd = channels[i].ch_out;
!           fds[nfd].events = POLLIN;
!           nfd++;
!       }
!       else
!           channels[i].ch_out_idx = -1;
! 
!       if (channels[i].ch_err >= 0)
        {
!           channels[i].ch_err_idx = nfd;
!           fds[nfd].fd = channels[i].ch_err;
            fds[nfd].events = POLLIN;
            nfd++;
        }
        else
!           channels[i].ch_err_idx = -1;
! #  endif
!     }
  
      return nfd;
  }
***************
*** 1423,1434 ****
      struct pollfd *fds = fds_in;
  
      for (i = 0; i < channel_count; ++i)
!       if (ret > 0 && channels[i].ch_idx != -1
!                                && fds[channels[i].ch_idx].revents & POLLIN)
        {
!           channel_read(i);
            --ret;
        }
  
      return ret;
  }
--- 1650,1677 ----
      struct pollfd *fds = fds_in;
  
      for (i = 0; i < channel_count; ++i)
!     {
!       if (ret > 0 && channels[i].ch_sock_idx != -1
!                            && fds[channels[i].ch_sock_idx].revents & POLLIN)
!       {
!           channel_read(i, FALSE, "channel_poll_check");
!           --ret;
!       }
! #  ifdef CHANNEL_PIPES
!       if (ret > 0 && channels[i].ch_out_idx != -1
!                              && fds[channels[i].ch_out_idx].revents & POLLIN)
        {
!           channel_read(i, FALSE, "channel_poll_check");
            --ret;
        }
+       if (ret > 0 && channels[i].ch_err_idx != -1
+                              && fds[channels[i].ch_err_idx].revents & POLLIN)
+       {
+           channel_read(i, TRUE, "channel_poll_check");
+           --ret;
+       }
+ #  endif
+     }
  
      return ret;
  }
***************
*** 1446,1457 ****
      fd_set  *rfds = rfds_in;
  
      for (i = 0; i < channel_count; ++i)
!       if (channels[i].ch_fd >= 0)
        {
!           FD_SET(channels[i].ch_fd, rfds);
!           if (maxfd < channels[i].ch_fd)
!               maxfd = channels[i].ch_fd;
        }
  
      return maxfd;
  }
--- 1689,1716 ----
      fd_set  *rfds = rfds_in;
  
      for (i = 0; i < channel_count; ++i)
!     {
!       if (channels[i].ch_sock >= 0)
!       {
!           FD_SET(channels[i].ch_sock, rfds);
!           if (maxfd < channels[i].ch_sock)
!               maxfd = channels[i].ch_sock;
!       }
! #  ifdef CHANNEL_PIPES
!       if (channels[i].ch_out >= 0)
!       {
!           FD_SET(channels[i].ch_out, rfds);
!           if (maxfd < channels[i].ch_out)
!               maxfd = channels[i].ch_out;
!       }
!       if (channels[i].ch_err >= 0)
        {
!           FD_SET(channels[i].ch_err, rfds);
!           if (maxfd < channels[i].ch_err)
!               maxfd = channels[i].ch_err;
        }
+ #  endif
+     }
  
      return maxfd;
  }
***************
*** 1467,1478 ****
      fd_set  *rfds = rfds_in;
  
      for (i = 0; i < channel_count; ++i)
!       if (ret > 0 && channels[i].ch_fd >= 0
!                                      && FD_ISSET(channels[i].ch_fd, rfds))
        {
!           channel_read(i);
            --ret;
        }
  
      return ret;
  }
--- 1726,1753 ----
      fd_set  *rfds = rfds_in;
  
      for (i = 0; i < channel_count; ++i)
!     {
!       if (ret > 0 && channels[i].ch_sock >= 0
!                                      && FD_ISSET(channels[i].ch_sock, rfds))
!       {
!           channel_read(i, FALSE, "channel_select_check");
!           --ret;
!       }
! #  ifdef CHANNEL_PIPES
!       if (ret > 0 && channels[i].ch_out >= 0
!                                      && FD_ISSET(channels[i].ch_out, rfds))
        {
!           channel_read(i, FALSE, "channel_select_check");
            --ret;
        }
+       if (ret > 0 && channels[i].ch_err >= 0
+                                      && FD_ISSET(channels[i].ch_err, rfds))
+       {
+           channel_read(i, TRUE, "channel_select_check");
+           --ret;
+       }
+ #  endif
+     }
  
      return ret;
  }
***************
*** 1528,1542 ****
  }
  
  /*
!  * Return the mode of channel "idx".
!  * If "idx" is invalid returns MODE_JSON.
   */
      ch_mode_T
! channel_get_mode(int idx)
  {
!     if (idx < 0 || idx >= channel_count)
        return MODE_JSON;
!     return channels[idx].ch_mode;
  }
  
  #endif /* FEAT_CHANNEL */
--- 1803,1817 ----
  }
  
  /*
!  * Return the mode of channel "ch_idx".
!  * If "ch_idx" is invalid returns MODE_JSON.
   */
      ch_mode_T
! channel_get_mode(int ch_idx)
  {
!     if (ch_idx < 0 || ch_idx >= channel_count)
        return MODE_JSON;
!     return channels[ch_idx].ch_mode;
  }
  
  #endif /* FEAT_CHANNEL */
*** ../vim-7.4.1309/src/eval.c  2016-02-12 19:30:20.349885799 +0100
--- src/eval.c  2016-02-13 15:38:01.864656937 +0100
***************
*** 503,510 ****
  static void f_ceil(typval_T *argvars, typval_T *rettv);
  #endif
  #ifdef FEAT_CHANNEL
- static void f_ch_open(typval_T *argvars, typval_T *rettv);
  static void f_ch_close(typval_T *argvars, typval_T *rettv);
  static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
  static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
  #endif
--- 503,512 ----
  static void f_ceil(typval_T *argvars, typval_T *rettv);
  #endif
  #ifdef FEAT_CHANNEL
  static void f_ch_close(typval_T *argvars, typval_T *rettv);
+ static void f_ch_logfile(typval_T *argvars, typval_T *rettv);
+ static void f_ch_open(typval_T *argvars, typval_T *rettv);
+ static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
  static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
  static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
  #endif
***************
*** 624,629 ****
--- 626,632 ----
  static void f_islocked(typval_T *argvars, typval_T *rettv);
  static void f_items(typval_T *argvars, typval_T *rettv);
  #ifdef FEAT_JOB
+ static void f_job_getchannel(typval_T *argvars, typval_T *rettv);
  static void f_job_start(typval_T *argvars, typval_T *rettv);
  static void f_job_stop(typval_T *argvars, typval_T *rettv);
  static void f_job_status(typval_T *argvars, typval_T *rettv);
***************
*** 7720,7725 ****
--- 7723,7730 ----
      static void
  job_free(job_T *job)
  {
+     if (job->jv_channel >= 0)
+       channel_close(job->jv_channel);
      mch_clear_job(job);
      vim_free(job);
  }
***************
*** 8083,8089 ****
--- 8088,8096 ----
  #endif
  #ifdef FEAT_CHANNEL
      {"ch_close",      1, 1, f_ch_close},
+     {"ch_logfile",    1, 2, f_ch_logfile},
      {"ch_open",               1, 2, f_ch_open},
+     {"ch_readraw",    1, 2, f_ch_readraw},
      {"ch_sendexpr",   2, 3, f_ch_sendexpr},
      {"ch_sendraw",    2, 3, f_ch_sendraw},
  #endif
***************
*** 8207,8212 ****
--- 8214,8220 ----
      {"islocked",      1, 1, f_islocked},
      {"items",         1, 1, f_items},
  #ifdef FEAT_JOB
+     {"job_getchannel",        1, 1, f_job_getchannel},
      {"job_start",     1, 2, f_job_start},
      {"job_status",    1, 1, f_job_status},
      {"job_stop",      1, 2, f_job_stop},
***************
*** 9788,9794 ****
      }
      ch_idx = tv->vval.v_number;
  
!     if (!channel_is_open(ch_idx))
      {
        EMSGN(_("E906: not an open channel"), ch_idx);
        return -1;
--- 9796,9802 ----
      }
      ch_idx = tv->vval.v_number;
  
!     if (!channel_can_write_to(ch_idx))
      {
        EMSGN(_("E906: not an open channel"), ch_idx);
        return -1;
***************
*** 9825,9830 ****
--- 9833,9864 ----
  }
  
  /*
+  * "ch_logfile()" function
+  */
+     static void
+ f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
+ {
+     char_u *fname;
+     char_u *opt = (char_u *)"";
+     char_u buf[NUMBUFLEN];
+     FILE   *file = NULL;
+ 
+     fname = get_tv_string(&argvars[0]);
+     if (argvars[1].v_type == VAR_STRING)
+       opt = get_tv_string_buf(&argvars[1], buf);
+     if (*fname != NUL)
+     {
+       file = fopen((char *)fname, *opt == 'w' ? "w" : "a");
+       if (file == NULL)
+       {
+           EMSG2(_(e_notopen), fname);
+           return;
+       }
+     }
+     ch_logfile(file);
+ }
+ 
+ /*
   * "ch_open()" function
   */
      static void
***************
*** 9914,9919 ****
--- 9948,9974 ----
  }
  
  /*
+  * "ch_readraw()" function
+  */
+     static void
+ f_ch_readraw(typval_T *argvars, typval_T *rettv)
+ {
+     int               ch_idx;
+ 
+     /* return an empty string by default */
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = NULL;
+ 
+     ch_idx = get_channel_arg(&argvars[0]);
+     if (ch_idx < 0)
+     {
+       EMSG(_(e_invarg));
+       return;
+     }
+     rettv->vval.v_string = channel_read_block(ch_idx);
+ }
+ 
+ /*
   * common for "sendexpr()" and "sendraw()"
   * Returns the channel index if the caller should read the response.
   * Otherwise returns -1.
***************
*** 14300,14305 ****
--- 14355,14377 ----
  
  #ifdef FEAT_JOB
  /*
+  * "job_getchannel()" function
+  */
+     static void
+ f_job_getchannel(typval_T *argvars, typval_T *rettv)
+ {
+     if (argvars[0].v_type != VAR_JOB)
+       EMSG(_(e_invarg));
+     else
+     {
+       job_T *job = argvars[0].vval.v_job;
+ 
+       rettv->v_type = VAR_NUMBER;
+       rettv->vval.v_number = job->jv_channel;
+     }
+ }
+ 
+ /*
   * "job_start()" function
   */
      static void
***************
*** 14401,14407 ****
   * "job_status()" function
   */
      static void
! f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
      char *result;
  
--- 14473,14479 ----
   * "job_status()" function
   */
      static void
! f_job_status(typval_T *argvars, typval_T *rettv)
  {
      char *result;
  
*** ../vim-7.4.1309/src/os_unix.c       2016-02-12 19:30:20.353885756 +0100
--- src/os_unix.c       2016-02-13 14:09:10.128543973 +0100
***************
*** 3984,3989 ****
--- 3984,4025 ----
  }
  #endif
  
+ #if !defined(USE_SYSTEM) || defined(FEAT_JOB)
+     static void
+ set_child_environment(void)
+ {
+ # ifdef HAVE_SETENV
+     char      envbuf[50];
+ # else
+     static char       envbuf_Rows[20];
+     static char       envbuf_Columns[20];
+ # endif
+ 
+     /* Simulate to have a dumb terminal (for now) */
+ # ifdef HAVE_SETENV
+     setenv("TERM", "dumb", 1);
+     sprintf((char *)envbuf, "%ld", Rows);
+     setenv("ROWS", (char *)envbuf, 1);
+     sprintf((char *)envbuf, "%ld", Rows);
+     setenv("LINES", (char *)envbuf, 1);
+     sprintf((char *)envbuf, "%ld", Columns);
+     setenv("COLUMNS", (char *)envbuf, 1);
+ # else
+     /*
+      * Putenv does not copy the string, it has to remain valid.
+      * Use a static array to avoid losing allocated memory.
+      */
+     putenv("TERM=dumb");
+     sprintf(envbuf_Rows, "ROWS=%ld", Rows);
+     putenv(envbuf_Rows);
+     sprintf(envbuf_Rows, "LINES=%ld", Rows);
+     putenv(envbuf_Rows);
+     sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
+     putenv(envbuf_Columns);
+ # endif
+ }
+ #endif
+ 
      int
  mch_call_shell(
      char_u    *cmd,
***************
*** 4134,4145 ****
      int               fd_toshell[2];          /* for pipes */
      int               fd_fromshell[2];
      int               pipe_error = FALSE;
- # ifdef HAVE_SETENV
-     char      envbuf[50];
- # else
-     static char       envbuf_Rows[20];
-     static char       envbuf_Columns[20];
- # endif
      int               did_settmode = FALSE;   /* settmode(TMODE_RAW) called */
  
      newcmd = vim_strsave(p_sh);
--- 4170,4175 ----
***************
*** 4349,4376 ****
  #  endif
                }
  # endif
!               /* Simulate to have a dumb terminal (for now) */
! # ifdef HAVE_SETENV
!               setenv("TERM", "dumb", 1);
!               sprintf((char *)envbuf, "%ld", Rows);
!               setenv("ROWS", (char *)envbuf, 1);
!               sprintf((char *)envbuf, "%ld", Rows);
!               setenv("LINES", (char *)envbuf, 1);
!               sprintf((char *)envbuf, "%ld", Columns);
!               setenv("COLUMNS", (char *)envbuf, 1);
! # else
!               /*
!                * Putenv does not copy the string, it has to remain valid.
!                * Use a static array to avoid losing allocated memory.
!                */
!               putenv("TERM=dumb");
!               sprintf(envbuf_Rows, "ROWS=%ld", Rows);
!               putenv(envbuf_Rows);
!               sprintf(envbuf_Rows, "LINES=%ld", Rows);
!               putenv(envbuf_Rows);
!               sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
!               putenv(envbuf_Columns);
! # endif
  
                /*
                 * stderr is only redirected when using the GUI, so that a
--- 4379,4385 ----
  #  endif
                }
  # endif
!               set_child_environment();
  
                /*
                 * stderr is only redirected when using the GUI, so that a
***************
*** 5030,5042 ****
      void
  mch_start_job(char **argv, job_T *job)
  {
!     pid_t pid = fork();
  
!     if (pid  == -1)   /* maybe we should use vfork() */
      {
!       job->jv_status = JOB_FAILED;
      }
!     else if (pid == 0)
      {
        /* child */
        reset_signals();                /* handle signals normally */
--- 5039,5072 ----
      void
  mch_start_job(char **argv, job_T *job)
  {
!     pid_t     pid;
!     int               fd_in[2];       /* for stdin */
!     int               fd_out[2];      /* for stdout */
!     int               fd_err[2];      /* for stderr */
!     int               ch_idx;
! 
!     /* default is to fail */
!     job->jv_status = JOB_FAILED;
!     fd_in[0] = -1;
!     fd_out[0] = -1;
!     fd_err[0] = -1;
! 
!     /* Open pipes for stdin, stdout, stderr. */
!     if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0))
!       goto failed;
! 
!     ch_idx = add_channel();
!     if (ch_idx < 0)
!       goto failed;
  
!     pid = fork();     /* maybe we should use vfork() */
!     if (pid  == -1)
      {
!       /* failed to fork */
!       goto failed;
      }
! 
!     if (pid == 0)
      {
        /* child */
        reset_signals();                /* handle signals normally */
***************
*** 5048,5064 ****
        (void)setsid();
  # endif
  
        /* See above for type of argv. */
        execvp(argv[0], argv);
  
        perror("executing job failed");
        _exit(EXEC_FAILED);         /* exec failed, return failure code */
      }
!     else
      {
!       /* parent */
!       job->jv_pid = pid;
!       job->jv_status = JOB_STARTED;
      }
  }
  
--- 5078,5139 ----
        (void)setsid();
  # endif
  
+       set_child_environment();
+ 
+       /* set up stdin for the child */
+       close(fd_in[1]);
+       close(0);
+       ignored = dup(fd_in[0]);
+       close(fd_in[0]);
+ 
+       /* set up stdout for the child */
+       close(fd_out[0]);
+       close(1);
+       ignored = dup(fd_out[1]);
+       close(fd_out[1]);
+ 
+       /* set up stderr for the child */
+       close(fd_err[0]);
+       close(2);
+       ignored = dup(fd_err[1]);
+       close(fd_err[1]);
+ 
        /* See above for type of argv. */
        execvp(argv[0], argv);
  
        perror("executing job failed");
        _exit(EXEC_FAILED);         /* exec failed, return failure code */
      }
! 
!     /* parent */
!     job->jv_pid = pid;
!     job->jv_status = JOB_STARTED;
!     job->jv_channel = ch_idx;
! 
!     /* child stdin, stdout and stderr */
!     close(fd_in[0]);
!     close(fd_out[1]);
!     close(fd_err[1]);
!     channel_set_pipes(ch_idx, fd_in[1], fd_out[0], fd_err[0]);
!     channel_set_job(ch_idx, job);
! 
!     return;
! 
! failed:
!     if (fd_in[0] >= 0)
!     {
!       close(fd_in[0]);
!       close(fd_in[1]);
!     }
!     if (fd_out[0] >= 0)
!     {
!       close(fd_out[0]);
!       close(fd_out[1]);
!     }
!     if (fd_err[0] >= 0)
      {
!       close(fd_err[0]);
!       close(fd_err[1]);
      }
  }
  
***************
*** 5104,5111 ****
      int
  mch_stop_job(job_T *job, char_u *how)
  {
!     int sig = -1;
!     pid_t job_pid;
  
      if (STRCMP(how, "hup") == 0)
        sig = SIGHUP;
--- 5179,5186 ----
      int
  mch_stop_job(job_T *job, char_u *how)
  {
!     int           sig = -1;
!     pid_t   job_pid;
  
      if (STRCMP(how, "hup") == 0)
        sig = SIGHUP;
*** ../vim-7.4.1309/src/structs.h       2016-02-12 19:30:20.353885756 +0100
--- src/structs.h       2016-02-13 13:31:52.556086121 +0100
***************
*** 1110,1116 ****
--- 1110,1121 ----
  
  typedef struct listvar_S list_T;
  typedef struct dictvar_S dict_T;
+ 
  typedef struct jobvar_S job_T;
+ typedef struct readq_S readq_T;
+ typedef struct jsonq_S jsonq_T;
+ typedef struct cbq_S cbq_T;
+ typedef struct channel_S channel_T;
  
  typedef enum
  {
***************
*** 1255,1262 ****
--- 1260,1351 ----
      jobstatus_T       jv_status;
  
      int               jv_refcount;    /* reference count */
+     int               jv_channel;     /* channel for I/O */
+ };
+ 
+ /*
+  * Structures to hold info about a Channel.
+  */
+ struct readq_S
+ {
+     char_u    *buffer;
+     readq_T   *next;
+     readq_T   *prev;
+ };
+ 
+ struct jsonq_S
+ {
+     typval_T  *value;
+     jsonq_T   *next;
+     jsonq_T   *prev;
+ };
+ 
+ struct cbq_S
+ {
+     char_u    *callback;
+     int               seq_nr;
+     cbq_T     *next;
+     cbq_T     *prev;
+ };
+ 
+ /* mode for a channel */
+ typedef enum
+ {
+     MODE_RAW = 0,
+     MODE_JSON,
+     MODE_JS
+ } ch_mode_T;
+ 
+ struct channel_S {
+     sock_T    ch_sock;        /* the socket, -1 for a closed channel */
+ 
+ #ifdef UNIX
+ # define CHANNEL_PIPES
+     int               ch_in;          /* stdin of the job, -1 if not used */
+     int               ch_out;         /* stdout of the job, -1 if not used */
+     int               ch_err;         /* stderr of the job, -1 if not used */
+ 
+ # if defined(UNIX) && !defined(HAVE_SELECT)
+     int               ch_sock_idx;    /* used by channel_poll_setup() */
+     int               ch_in_idx;      /* used by channel_poll_setup() */
+     int               ch_out_idx;     /* used by channel_poll_setup() */
+     int               ch_err_idx;     /* used by channel_poll_setup() */
+ # endif
+ #endif
+ 
+     readq_T   ch_head;        /* dummy node, header for circular queue */
+ 
+     int               ch_error;       /* When TRUE an error was reported.  
Avoids
+                                * giving pages full of error messages when
+                                * the other side has exited, only mention the
+                                * first error until the connection works
+                                * again. */
+ #ifdef FEAT_GUI_X11
+     XtInputId ch_inputHandler; /* Cookie for input */
+ #endif
+ #ifdef FEAT_GUI_GTK
+     gint      ch_inputHandler; /* Cookie for input */
+ #endif
+ #ifdef WIN32
+     int               ch_inputHandler; /* simply ret.value of 
WSAAsyncSelect() */
+ #endif
+ 
+     void      (*ch_close_cb)(void); /* callback for when channel is closed */
+ 
+     int               ch_block_id;    /* ID that channel_read_json_block() is
+                                  waiting for */
+     char_u    *ch_callback;   /* function to call when a msg is not handled */
+     cbq_T     ch_cb_head;     /* dummy node for pre-request callbacks */
+ 
+     ch_mode_T ch_mode;
+     jsonq_T   ch_json_head;   /* dummy node, header for circular queue */
+ 
+     int               ch_timeout;     /* request timeout in msec */
+ 
+     job_T     *ch_job;        /* job that uses this channel */
  };
  
+ 
  /* structure used for explicit stack while garbage collecting hash tables */
  typedef struct ht_stack_S
  {
***************
*** 2729,2739 ****
      void      *js_cookie;     /* can be used by js_fill */
  };
  typedef struct js_reader js_read_T;
- 
- /* mode for a channel */
- typedef enum
- {
-     MODE_RAW = 0,
-     MODE_JSON,
-     MODE_JS
- } ch_mode_T;
--- 2818,2820 ----
*** ../vim-7.4.1309/src/gui_w48.c       2016-01-30 17:24:01.794502490 +0100
--- src/gui_w48.c       2016-02-13 15:50:48.476651452 +0100
***************
*** 1780,1789 ****
  #ifdef FEAT_CHANNEL
      if (msg.message == WM_NETBEANS)
      {
!       int channel_idx = channel_socket2idx((sock_T)msg.wParam);
  
        if (channel_idx >= 0)
!           channel_read(channel_idx);
        return;
      }
  #endif
--- 1780,1789 ----
  #ifdef FEAT_CHANNEL
      if (msg.message == WM_NETBEANS)
      {
!       int channel_idx = channel_fd2idx((sock_T)msg.wParam);
  
        if (channel_idx >= 0)
!           channel_read(channel_idx, FALSE, "process_message");
        return;
      }
  #endif
*** ../vim-7.4.1309/src/proto/channel.pro       2016-02-07 21:59:17.539916046 
+0100
--- src/proto/channel.pro       2016-02-13 15:50:59.792533243 +0100
***************
*** 1,22 ****
  /* channel.c */
  void channel_gui_register_all(void);
  int channel_open(char *hostname, int port_in, int waittime, void 
(*close_cb)(void));
  void channel_set_json_mode(int idx, ch_mode_T ch_mode);
  void channel_set_timeout(int idx, int timeout);
  void channel_set_callback(int idx, char_u *callback);
  void channel_set_req_callback(int idx, char_u *callback, int id);
  char_u *channel_get(int idx);
  int channel_collapse(int idx);
  int channel_is_open(int idx);
  void channel_close(int idx);
  int channel_save(int idx, char_u *buf, int len);
  char_u *channel_peek(int idx);
  void channel_clear(int idx);
  int channel_get_id(void);
! void channel_read(int idx);
  char_u *channel_read_block(int idx);
  int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
! int channel_socket2idx(sock_T fd);
  int channel_send(int idx, char_u *buf, char *fun);
  int channel_poll_setup(int nfd_in, void *fds_in);
  int channel_poll_check(int ret_in, void *fds_in);
--- 1,27 ----
  /* channel.c */
+ void ch_logfile(FILE *file);
+ int add_channel(void);
  void channel_gui_register_all(void);
  int channel_open(char *hostname, int port_in, int waittime, void 
(*close_cb)(void));
+ void channel_set_pipes(int idx, int in, int out, int err);
+ void channel_set_job(int idx, job_T *job);
  void channel_set_json_mode(int idx, ch_mode_T ch_mode);
  void channel_set_timeout(int idx, int timeout);
  void channel_set_callback(int idx, char_u *callback);
  void channel_set_req_callback(int idx, char_u *callback, int id);
  char_u *channel_get(int idx);
  int channel_collapse(int idx);
+ int channel_can_write_to(int idx);
  int channel_is_open(int idx);
  void channel_close(int idx);
  int channel_save(int idx, char_u *buf, int len);
  char_u *channel_peek(int idx);
  void channel_clear(int idx);
  int channel_get_id(void);
! void channel_read(int idx, int use_stderr, char *func);
  char_u *channel_read_block(int idx);
  int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
! int channel_fd2idx(sock_T fd);
  int channel_send(int idx, char_u *buf, char *fun);
  int channel_poll_setup(int nfd_in, void *fds_in);
  int channel_poll_check(int ret_in, void *fds_in);
*** ../vim-7.4.1309/src/testdir/test_channel.vim        2016-02-12 
22:35:47.519872593 +0100
--- src/testdir/test_channel.vim        2016-02-13 16:43:35.355640338 +0100
***************
*** 273,275 ****
--- 273,292 ----
      call assert_true(reltimefloat(elapsed) < (has('unix') ? 1.0 : 3.0))
    endif
  endfunc
+ 
+ func Test_pipe()
+   if !has('job') || !has('unix')
+     return
+   endif
+   let job = job_start("python test_channel_pipe.py")
+   call assert_equal("run", job_status(job))
+   try
+     let handle = job_getchannel(job)
+     call ch_sendraw(handle, "echo something\n", 0)
+     call assert_equal("something\n", ch_readraw(handle))
+     let reply = ch_sendraw(handle, "quit\n")
+     call assert_equal("Goodbye!\n", reply)
+   finally
+     call job_stop(job)
+   endtry
+ endfunc
*** ../vim-7.4.1309/src/testdir/test_channel_pipe.py    2016-02-13 
16:57:13.367130042 +0100
--- src/testdir/test_channel_pipe.py    2016-02-13 16:22:31.128801099 +0100
***************
*** 0 ****
--- 1,24 ----
+ #!/usr/bin/python
+ #
+ # Server that will communicate over stdin/stderr
+ #
+ # This requires Python 2.6 or later.
+ 
+ from __future__ import print_function
+ import sys
+ 
+ if __name__ == "__main__":
+ 
+     if len(sys.argv) > 1:
+         print(sys.argv[1])
+ 
+     while True:
+         typed = sys.stdin.readline()
+         if typed.startswith("quit"):
+             print("Goodbye!")
+             sys.stdout.flush()
+             break
+         if typed.startswith("echo"):
+             print(typed[5:-1])
+             sys.stdout.flush()
+ 
*** ../vim-7.4.1309/runtime/doc/eval.txt        2016-02-11 21:08:27.544531244 
+0100
--- runtime/doc/eval.txt        2016-02-13 15:36:56.061343826 +0100
***************
*** 1806,1812 ****
--- 1816,1824 ----
                                any     call {func} with arguments {arglist}
  ceil( {expr})                 Float   round {expr} up
  ch_close( {handle})           none    close a channel
+ ch_logfile( {fname} [, {mode}])       none    start logging channel activity
  ch_open( {address} [, {argdict})] Number open a channel to {address}
+ ch_readraw( {handle})         String  read from channel {handle}
  ch_sendexpr( {handle}, {expr} [, {callback}])
                                any     send {expr} over JSON channel {handle}
  ch_sendraw( {handle}, {string} [, {callback}])
***************
*** 2663,2670 ****
                don't fit, a vertical layout is used anyway.  For some systems
                the horizontal layout is always used.
  
! ch_close({handle})                                    *ch_close()*
                Close channel {handle}.  See |channel|.
  
  ch_open({address} [, {argdict}])                              *ch_open()*
                Open a channel to {address}.  See |channel|.
--- 2678,2692 ----
                don't fit, a vertical layout is used anyway.  For some systems
                the horizontal layout is always used.
  
! ch_close({handle})                                            *ch_close()*
                Close channel {handle}.  See |channel|.
+               {only available when compiled with the |+channel| feature}
+ 
+ ch_logfile( {fname} [, {mode}])                                       
*ch_logfile()*
+               Start logging channel activity to {fname}.
+               When {mode} is omitted or "a" append to the file.
+               When {mode} is "w" start with an empty file.
+               When {fname} is an empty string: stop logging.
  
  ch_open({address} [, {argdict}])                              *ch_open()*
                Open a channel to {address}.  See |channel|.
***************
*** 2689,2695 ****
                                    Default: 2000.
                {only available when compiled with the |+channel| feature}
  
! ch_sendexpr({handle}, {expr} [, {callback}])          *ch_sendexpr()*
                Send {expr} over channel {handle}.  The {expr} is encoded
                according to the type of channel.  The function cannot be used
                with a raw channel.  See |channel-use|.  *E912*
--- 2711,2723 ----
                                    Default: 2000.
                {only available when compiled with the |+channel| feature}
  
! ch_readraw({handle})                                          *ch_readraw()*
!               Read from channel {handle} and return the received message.
!               This uses the channel timeout.  When there is nothing to read
!               within that time an empty string is returned.
!               TODO: depends on channel mode.
! 
! ch_sendexpr({handle}, {expr} [, {callback}])                  *ch_sendexpr()*
                Send {expr} over channel {handle}.  The {expr} is encoded
                according to the type of channel.  The function cannot be used
                with a raw channel.  See |channel-use|.  *E912*
***************
*** 4252,4273 ****
                Start a job and return a Job object.  Unlike |system()| and
                |:!cmd| this does not wait for the job to finish.
  
!               {command} can be a string.  This works best on MS-Windows.  On
                Unix it is split up in white-separated parts to be passed to
                execvp().  Arguments in double quotes can contain white space.
  
!               {command} can be a list, where the first item is the executable
                and further items are the arguments.  All items are converted
                to String.  This works best on Unix.
  
                The command is executed directly, not through a shell, the
                'shell' option is not used.  To use the shell: >
        let job = job_start(["/bin/sh", "-c", "echo hello"])
  <             Or: >
        let job = job_start('/bin/sh -c "echo hello"')
! <             However, the status of the job will now be the status of the
!               shell, and stopping the job means stopping the shell and the
!               command may continue to run.
  
                On Unix $PATH is used to search for the executable only when
                the command does not contain a slash.
--- 4338,4362 ----
                Start a job and return a Job object.  Unlike |system()| and
                |:!cmd| this does not wait for the job to finish.
  
!               {command} can be a String.  This works best on MS-Windows.  On
                Unix it is split up in white-separated parts to be passed to
                execvp().  Arguments in double quotes can contain white space.
  
!               {command} can be a List, where the first item is the executable
                and further items are the arguments.  All items are converted
                to String.  This works best on Unix.
  
+               On MS-Windows, job_start() makes a GUI application hidden. If
+               want to show it, Use |:!start| instead.
+ 
                The command is executed directly, not through a shell, the
                'shell' option is not used.  To use the shell: >
        let job = job_start(["/bin/sh", "-c", "echo hello"])
  <             Or: >
        let job = job_start('/bin/sh -c "echo hello"')
! <             Note that this will start two processes, the shell and the
!               command it executes.  If you don't want this use the "exec"
!               shell command.
  
                On Unix $PATH is used to search for the executable only when
                the command does not contain a slash.
***************
*** 4280,4291 ****
                The returned Job object can be used to get the status with
                |job_status()| and stop the job with |job_stop()|.
  
!               {options} must be a Dictionary.  It can contain these optional
!               items:
!                       killonexit      When non-zero kill the job when Vim
!                                       exits. (default: 0, don't kill)
  
!               {only available when compiled with the |+channel| feature}
  
  job_status({job})                                             *job_status()*
                Returns a String with the status of {job}:
--- 4369,4378 ----
                The returned Job object can be used to get the status with
                |job_status()| and stop the job with |job_stop()|.
  
!               {options} must be a Dictionary.  It can contain many optional
!               items, see |job-options|.
  
!               {only available when compiled with the |+job| feature}
  
  job_status({job})                                             *job_status()*
                Returns a String with the status of {job}:
***************
*** 4293,4319 ****
                        "fail"  job failed to start
                        "dead"  job died or was stopped after running
  
!               {only available when compiled with the |+channel| feature}
  
  job_stop({job} [, {how}])                                     *job_stop()*
                Stop the {job}.  This can also be used to signal the job.
  
                When {how} is omitted or is "term" the job will be terminated
!               normally.  For Unix SIGTERM is sent.
!               Other values:
                        "hup"   Unix: SIGHUP
                        "quit"  Unix: SIGQUIT
                        "kill"  Unix: SIGKILL (strongest way to stop)
                        number  Unix: signal with that number
  
                The result is a Number: 1 if the operation could be executed,
                0 if "how" is not supported on the system.
                Note that even when the operation was executed, whether the
                job was actually stopped needs to be checked with
                job_status().
!               The operation will even be done when the job wasn't running.
  
!               {only available when compiled with the |+channel| feature}
  
  join({list} [, {sep}])                                        *join()*
                Join the items in {list} together into one String.
--- 4380,4419 ----
                        "fail"  job failed to start
                        "dead"  job died or was stopped after running
  
!               {only available when compiled with the |+job| feature}
  
  job_stop({job} [, {how}])                                     *job_stop()*
                Stop the {job}.  This can also be used to signal the job.
  
                When {how} is omitted or is "term" the job will be terminated
!               normally.  For Unix SIGTERM is sent.  For MS-Windows
!               CTRL_BREAK will be sent.  This goes to the process group, thus
!               children may also be affected.
! 
!               Other values for Unix:
                        "hup"   Unix: SIGHUP
                        "quit"  Unix: SIGQUIT
                        "kill"  Unix: SIGKILL (strongest way to stop)
                        number  Unix: signal with that number
  
+               Other values for MS-Windows:
+                       "int"   Windows: CTRL_C
+                       "kill"  Windows: terminate process forcedly
+                       Others  Windows: CTRL_BREAK
+ 
+               On Unix the signal is sent to the process group.  This means
+               that when the job is "sh -c command" it affects both the shell
+               and the command.
+ 
                The result is a Number: 1 if the operation could be executed,
                0 if "how" is not supported on the system.
                Note that even when the operation was executed, whether the
                job was actually stopped needs to be checked with
                job_status().
!               The status of the job isn't checked, the operation will even
!               be done when Vim thinks the job isn't running.
  
!               {only available when compiled with the |+job| feature}
  
  join({list} [, {sep}])                                        *join()*
                Join the items in {list} together into one String.
*** ../vim-7.4.1309/src/version.c       2016-02-13 14:06:10.002442660 +0100
--- src/version.c       2016-02-13 16:57:23.163028341 +0100
***************
*** 749,750 ****
--- 749,752 ----
  {   /* Add new patch number below this line */
+ /**/
+     1310,
  /**/

-- 
Married is a three ring circus:
First comes the engagement ring.
Then comes the wedding ring.
Then comes the suffering.

 /// Bram Moolenaar -- b...@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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui