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 */
diff --git a/src/eval.c b/src/eval.c
index 90fa53a..7679251 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -16889,8 +16889,6 @@
      * not reading the response. */
     channel_set_req_callback(ch_idx,
 	    callback != NULL && *callback == NUL ? NULL : callback);
-    if (callback == NULL)
-	channel_will_block(ch_idx);
 
     if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
 	return ch_idx;
@@ -16907,6 +16905,7 @@
     char_u	*resp;
     typval_T	nrtv;
     typval_T	listtv;
+    typval_T	typetv;
     int		ch_idx;
 
     /* return an empty string by default */
@@ -16932,10 +16931,11 @@
     {
 	/* TODO: read until the whole JSON message is received */
 	/* TODO: only use the message with the right message ID */
+	/* TODO: check sequence number */
 	resp = channel_read_block(ch_idx);
 	if (resp != NULL)
 	{
-	    channel_decode_json(resp, rettv);
+	    channel_decode_json(resp, &typetv, rettv);
 	    vim_free(resp);
 	}
     }
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 7dfd990..24df1d1 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -345,7 +345,6 @@
 static char_u	*find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *compl);
 #endif
 #ifdef FEAT_EX_EXTRA
-static void	ex_normal(exarg_T *eap);
 static void	ex_startinsert(exarg_T *eap);
 static void	ex_stopinsert(exarg_T *eap);
 #else
@@ -9861,11 +9860,11 @@
     update_curswant();
 }
 
-#ifdef FEAT_EX_EXTRA
+#if defined(FEAT_EX_EXTRA) || defined(PROTO)
 /*
  * ":normal[!] {commands}": Execute normal mode commands.
  */
-    static void
+    void
 ex_normal(exarg_T *eap)
 {
     int		save_msg_scroll = msg_scroll;
diff --git a/src/feature.h b/src/feature.h
index dd18394..f1a3a4a 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -256,7 +256,7 @@
 /*
  * +ex_extra		":retab", ":right", ":left", ":center", ":normal".
  */
-#ifdef FEAT_NORMAL
+#if defined(FEAT_NORMAL) || defined(FEAT_CHANNEL)
 # define FEAT_EX_EXTRA
 #endif
 
diff --git a/src/misc2.c b/src/misc2.c
index 05f779f..78a7479 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -6240,6 +6240,10 @@
     /* Process the queued netbeans messages. */
     netbeans_parse_messages();
 # endif
+# ifdef FEAT_CHANNEL
+    /* Process the messages queued on channels. */
+    channel_parse_messages();
+# endif
 # if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
     /* Process the queued clientserver messages. */
     server_parse_messages();
diff --git a/src/proto/channel.pro b/src/proto/channel.pro
index 2d46a49..bf3d93e 100644
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -4,8 +4,7 @@
 void channel_set_json_mode(int idx, int json_mode);
 void channel_set_callback(int idx, char_u *callback);
 void channel_set_req_callback(int idx, char_u *callback);
-void channel_will_block(int idx);
-int channel_decode_json(char_u *msg, typval_T *tv);
+int channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2);
 int channel_is_open(int idx);
 void channel_close(int idx);
 int channel_save(int idx, char_u *buf, int len);
@@ -22,4 +21,5 @@
 int channel_poll_check(int ret_in, void *fds_in);
 int channel_select_setup(int maxfd_in, void *rfds_in);
 int channel_select_check(int ret_in, void *rfds_in);
+void channel_parse_messages(void);
 /* vim: set ft=c : */
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
index 15e5fc5..0ba0fd7 100644
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -49,6 +49,7 @@
 int vim_mkdir_emsg(char_u *name, int prot);
 FILE *open_exfile(char_u *fname, int forceit, char *mode);
 void update_topline_cursor(void);
+void ex_normal(exarg_T *eap);
 void exec_normal_cmd(char_u *cmd, int remap, int silent);
 void exec_normal(int was_typed);
 int find_cmdline_var(char_u *src, int *usedlen);
diff --git a/src/version.c b/src/version.c
index 6207575..d57420b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1217,
+/**/
     1216,
 /**/
     1215,