patch 7.4.1217
Problem:    Execution of command on channel doesn't work yet.
Solution:   Implement the "ex" and "normal" commands.
diff --git a/src/channel.c b/src/channel.c
index 2a75342..416b538 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -99,7 +99,6 @@
 
     char_u    *ch_callback;	/* function to call when a msg is not handled */
     char_u    *ch_req_callback;	/* function to call for current request */
-    int	      ch_will_block;	/* do not use callback right now */
 
     int	      ch_json_mode;
 } channel_T;
@@ -419,21 +418,13 @@
 }
 
 /*
- * Set the flag that the callback for channel "idx" should not be used now.
- */
-    void
-channel_will_block(int idx)
-{
-    channels[idx].ch_will_block = TRUE;
-}
-
-/*
- * Decode JSON "msg", which must have the form "[nr, expr]".
- * Put "expr" in "tv".
+ * Decode JSON "msg", which must have the form "[expr1, expr2]".
+ * Put "expr1" in "tv1".
+ * Put "expr2" in "tv2".
  * Return OK or FAIL.
  */
     int
-channel_decode_json(char_u *msg, typval_T *tv)
+channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2)
 {
     js_read_T	reader;
     typval_T	listtv;
@@ -442,14 +433,14 @@
     reader.js_eof = TRUE;
     reader.js_used = 0;
     json_decode(&reader, &listtv);
-    /* TODO: use the sequence number */
-    if (listtv.v_type == VAR_LIST
-	  && listtv.vval.v_list->lv_len == 2
-	  && listtv.vval.v_list->lv_first->li_tv.v_type == VAR_NUMBER)
+
+    if (listtv.v_type == VAR_LIST && listtv.vval.v_list->lv_len == 2)
     {
 	/* Move the item from the list and then change the type to avoid the
 	 * item being freed. */
-	*tv = listtv.vval.v_list->lv_last->li_tv;
+	*tv1 = listtv.vval.v_list->lv_first->li_tv;
+	listtv.vval.v_list->lv_first->li_tv.v_type = VAR_NUMBER;
+	*tv2 = listtv.vval.v_list->lv_last->li_tv;
 	listtv.vval.v_list->lv_last->li_tv.v_type = VAR_NUMBER;
 	list_unref(listtv.vval.v_list);
 	return OK;
@@ -464,17 +455,79 @@
  * Invoke the "callback" on channel "idx".
  */
     static void
-invoke_callback(int idx, char_u *callback)
+invoke_callback(int idx, char_u *callback, typval_T *argv)
 {
-    typval_T	argv[3];
     typval_T	rettv;
     int		dummy;
-    char_u	*msg;
-    int		ret = OK;
 
     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);
+    /* If an echo command was used the cursor needs to be put back where
+     * it belongs. */
+    setcursor();
+    cursor_on();
+    out_flush();
+}
+
+    static void
+channel_exe_cmd(char_u *cmd, typval_T *arg)
+{
+    if (STRCMP(cmd, "ex") == 0)
+    {
+	if (arg->v_type == VAR_STRING)
+	    do_cmdline_cmd(arg->vval.v_string);
+	else if (p_verbose > 2)
+	    EMSG("E999: received ex command with non-string argument");
+    }
+    else if (STRCMP(cmd, "normal") == 0)
+    {
+	if (arg->v_type == VAR_STRING)
+	{
+	    exarg_T ea;
+
+	    ea.arg = arg->vval.v_string;
+	    ea.addr_count = 0;
+	    ea.forceit = TRUE; /* no mapping */
+	    ex_normal(&ea);
+
+	    update_screen(0);
+	    showruler(FALSE);
+	    setcursor();
+	    out_flush();
+#ifdef FEAT_GUI
+	    if (gui.in_use)
+	    {
+		gui_update_cursor(FALSE, FALSE);
+		gui_mch_flush();
+	    }
+#endif
+	}
+	else if (p_verbose > 2)
+	    EMSG("E999: received normal command with non-string argument");
+    }
+    else if (p_verbose > 2)
+	EMSG2("E999: received unknown command: %s", cmd);
+}
+
+/*
+ * Invoke a callback for channel "idx" if needed.
+ */
+    static void
+may_invoke_callback(int idx)
+{
+    char_u	*msg;
+    typval_T	typetv;
+    typval_T	argv[3];
+    char_u	*cmd = NULL;
+    int		seq_nr = -1;
+    int		ret = OK;
+
+    if (channel_peek(idx) == NULL)
+	return;
+
     /* Concatenate everything into one buffer.
      * TODO: only read what the callback will use.
      * TODO: avoid multiple allocations. */
@@ -483,7 +536,16 @@
     msg = channel_get(idx);
 
     if (channels[idx].ch_json_mode)
-	ret = channel_decode_json(msg, &argv[1]);
+    {
+	ret = channel_decode_json(msg, &typetv, &argv[1]);
+	if (ret == OK)
+	{
+	    if (typetv.v_type == VAR_STRING)
+		cmd = typetv.vval.v_string;
+	    else if (typetv.v_type == VAR_NUMBER)
+		seq_nr = typetv.vval.v_number;
+	}
+    }
     else
     {
 	argv[1].v_type = VAR_STRING;
@@ -492,42 +554,35 @@
 
     if (ret == OK)
     {
-	call_func(callback, (int)STRLEN(callback),
-				 &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
-	/* If an echo command was used the cursor needs to be put back where
-	 * it belongs. */
-	setcursor();
-	cursor_on();
-	out_flush();
+	if (cmd != NULL)
+	{
+	    channel_exe_cmd(cmd, &argv[1]);
+	}
+	else if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
+	{
+	    /* TODO: check the sequence number */
+	    /* invoke the one-time callback */
+	    invoke_callback(idx, channels[idx].ch_req_callback, argv);
+	    channels[idx].ch_req_callback = NULL;
+	}
+	else if (channels[idx].ch_callback != NULL)
+	{
+	    /* invoke the channel callback */
+	    invoke_callback(idx, channels[idx].ch_callback, argv);
+	}
+	/* else: drop the message */
+
+	if (channels[idx].ch_json_mode)
+	{
+	    clear_tv(&typetv);
+	    clear_tv(&argv[1]);
+	}
     }
+
     vim_free(msg);
 }
 
 /*
- * Invoke a callback for channel "idx" if needed.
- */
-    static void
-may_invoke_callback(int idx)
-{
-    if (channels[idx].ch_will_block)
-	return;
-    if (channel_peek(idx) == NULL)
-	return;
-
-    if (channels[idx].ch_req_callback != NULL)
-    {
-	/* invoke the one-time callback */
-	invoke_callback(idx, channels[idx].ch_req_callback);
-	channels[idx].ch_req_callback = NULL;
-	return;
-    }
-
-    if (channels[idx].ch_callback != NULL)
-	/* invoke the channel callback */
-	invoke_callback(idx, channels[idx].ch_callback);
-}
-
-/*
  * Return TRUE when channel "idx" is open.
  * Also returns FALSE or invalid "idx".
  */
@@ -823,8 +878,6 @@
 	}
     }
 
-    may_invoke_callback(idx);
-
 #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
     if (CH_HAS_GUI && gtk_main_level() > 0)
 	gtk_main_quit();
@@ -845,10 +898,7 @@
 	/* Wait for up to 2 seconds.
 	 * TODO: use timeout set on the channel. */
 	if (channel_wait(channels[idx].ch_fd, 2000) == FAIL)
-	{
-	    channels[idx].ch_will_block = FALSE;
 	    return NULL;
-	}
 	channel_read(idx);
     }
 
@@ -857,7 +907,6 @@
     while (channel_collapse(idx) == OK)
 	;
 
-    channels[idx].ch_will_block = FALSE;
     return channel_get(idx);
 }
 
@@ -1009,4 +1058,16 @@
 }
 # endif /* !FEAT_GUI_W32 && HAVE_SELECT */
 
+/*
+ * Invoked from the main loop when it's save to execute received commands.
+ */
+    void
+channel_parse_messages(void)
+{
+    int	    i;
+
+    for (i = 0; i < channel_count; ++i)
+	may_invoke_callback(i);
+}
+
 #endif /* FEAT_CHANNEL */