diff src/channel.c @ 15454:1d2b5c016f17 v8.1.0735

patch 8.1.0735: cannot handle binary data commit https://github.com/vim/vim/commit/6e5ea8d2a995b32bbc5972edc4f827b959f2702f Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 12 22:47:31 2019 +0100 patch 8.1.0735: cannot handle binary data Problem: Cannot handle binary data. Solution: Add the Blob type. (Yasuhiro Matsumoto, closes https://github.com/vim/vim/issues/3638)
author Bram Moolenaar <Bram@vim.org>
date Sat, 12 Jan 2019 23:00:06 +0100
parents c73f1fdfb27a
children 55ccc2d353bd
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -1665,7 +1665,7 @@ channel_first_nl(readq_T *node)
  * Returns NULL if there is nothing.
  */
     char_u *
-channel_get(channel_T *channel, ch_part_T part)
+channel_get(channel_T *channel, ch_part_T part, int *outlen)
 {
     readq_T *head = &channel->ch_part[part].ch_head;
     readq_T *node = head->rq_next;
@@ -1673,6 +1673,8 @@ channel_get(channel_T *channel, ch_part_
 
     if (node == NULL)
 	return NULL;
+    if (outlen != NULL)
+	*outlen += node->rq_buflen;
     /* dispose of the node but keep the buffer */
     p = node->rq_buffer;
     head->rq_next = node->rq_next;
@@ -1689,7 +1691,7 @@ channel_get(channel_T *channel, ch_part_
  * Replaces NUL bytes with NL.
  */
     static char_u *
-channel_get_all(channel_T *channel, ch_part_T part)
+channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
 {
     readq_T *head = &channel->ch_part[part].ch_head;
     readq_T *node = head->rq_next;
@@ -1699,7 +1701,7 @@ channel_get_all(channel_T *channel, ch_p
 
     /* If there is only one buffer just get that one. */
     if (head->rq_next == NULL || head->rq_next->rq_next == NULL)
-	return channel_get(channel, part);
+	return channel_get(channel, part, outlen);
 
     /* Concatenate everything into one buffer. */
     for (node = head->rq_next; node != NULL; node = node->rq_next)
@@ -1718,10 +1720,16 @@ channel_get_all(channel_T *channel, ch_p
     /* Free all buffers */
     do
     {
-	p = channel_get(channel, part);
+	p = channel_get(channel, part, NULL);
 	vim_free(p);
     } while (p != NULL);
 
+    if (outlen != NULL)
+    {
+	*outlen += len;
+	return res;
+    }
+
     /* turn all NUL into NL */
     while (len > 0)
     {
@@ -1893,7 +1901,7 @@ channel_fill(js_read_T *reader)
 {
     channel_T	*channel = (channel_T *)reader->js_cookie;
     ch_part_T	part = reader->js_cookie_arg;
-    char_u	*next = channel_get(channel, part);
+    char_u	*next = channel_get(channel, part, NULL);
     int		keeplen;
     int		addlen;
     char_u	*p;
@@ -1942,7 +1950,7 @@ channel_parse_json(channel_T *channel, c
     if (channel_peek(channel, part) == NULL)
 	return FALSE;
 
-    reader.js_buf = channel_get(channel, part);
+    reader.js_buf = channel_get(channel, part, NULL);
     reader.js_used = 0;
     reader.js_fill = channel_fill;
     reader.js_cookie = channel;
@@ -2475,7 +2483,7 @@ drop_messages(channel_T *channel, ch_par
 {
     char_u *msg;
 
-    while ((msg = channel_get(channel, part)) != NULL)
+    while ((msg = channel_get(channel, part, NULL)) != NULL)
     {
 	ch_log(channel, "Dropping message '%s'", (char *)msg);
 	vim_free(msg);
@@ -2639,7 +2647,7 @@ may_invoke_callback(channel_T *channel, 
 	    if (nl + 1 == buf + node->rq_buflen)
 	    {
 		/* get the whole buffer, drop the NL */
-		msg = channel_get(channel, part);
+		msg = channel_get(channel, part, NULL);
 		*nl = NUL;
 	    }
 	    else
@@ -2655,7 +2663,7 @@ may_invoke_callback(channel_T *channel, 
 	    /* For a raw channel we don't know where the message ends, just
 	     * get everything we have.
 	     * Convert NUL to NL, the internal representation. */
-	    msg = channel_get_all(channel, part);
+	    msg = channel_get_all(channel, part, NULL);
 	}
 
 	if (msg == NULL)
@@ -3007,7 +3015,7 @@ channel_clear_one(channel_T *channel, ch
     cbq_T   *cb_head = &ch_part->ch_cb_head;
 
     while (channel_peek(channel, part) != NULL)
-	vim_free(channel_get(channel, part));
+	vim_free(channel_get(channel, part, NULL));
 
     while (cb_head->cq_next != NULL)
     {
@@ -3381,7 +3389,8 @@ channel_read(channel_T *channel, ch_part
  * Returns NULL in case of error or timeout.
  */
     static char_u *
-channel_read_block(channel_T *channel, ch_part_T part, int timeout, int raw)
+channel_read_block(
+	channel_T *channel, ch_part_T part, int timeout, int raw, int *outlen)
 {
     char_u	*buf;
     char_u	*msg;
@@ -3422,9 +3431,9 @@ channel_read_block(channel_T *channel, c
     }
 
     /* We have a complete message now. */
-    if (mode == MODE_RAW)
+    if (mode == MODE_RAW || outlen != NULL)
     {
-	msg = channel_get_all(channel, part);
+	msg = channel_get_all(channel, part, outlen);
     }
     else
     {
@@ -3441,12 +3450,12 @@ channel_read_block(channel_T *channel, c
 	if (nl == NULL)
 	{
 	    /* must be a closed channel with missing NL */
-	    msg = channel_get(channel, part);
+	    msg = channel_get(channel, part, NULL);
 	}
 	else if (nl + 1 == buf + node->rq_buflen)
 	{
 	    /* get the whole buffer */
-	    msg = channel_get(channel, part);
+	    msg = channel_get(channel, part, NULL);
 	    *nl = NUL;
 	}
 	else
@@ -3554,7 +3563,7 @@ channel_read_json_block(
  * Common for ch_read() and ch_readraw().
  */
     void
-common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
+common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob)
 {
     channel_T	*channel;
     ch_part_T	part = PART_COUNT;
@@ -3585,9 +3594,32 @@ common_channel_read(typval_T *argvars, t
 	if (opt.jo_set & JO_TIMEOUT)
 	    timeout = opt.jo_timeout;
 
-	if (raw || mode == MODE_RAW || mode == MODE_NL)
+	if (blob)
+	{
+	    int	    outlen = 0;
+	    char_u  *p = channel_read_block(channel, part,
+						       timeout, TRUE, &outlen);
+	    if (p != NULL)
+	    {
+		blob_T	*b = blob_alloc();
+
+		if (b != NULL)
+		{
+		    b->bv_ga.ga_len = outlen;
+		    if (ga_grow(&b->bv_ga, outlen) == FAIL)
+			blob_free(b);
+		    else
+		    {
+			memcpy(b->bv_ga.ga_data, p, outlen);
+			rettv_blob_set(rettv, b);
+		    }
+		}
+		vim_free(p);
+	    }
+	}
+	else if (raw || mode == MODE_RAW || mode == MODE_NL)
 	    rettv->vval.v_string = channel_read_block(channel, part,
-								 timeout, raw);
+							 timeout, raw, NULL);
 	else
 	{
 	    if (opt.jo_set & JO_ID)
@@ -3905,6 +3937,7 @@ channel_send(
 send_common(
 	typval_T    *argvars,
 	char_u	    *text,
+	int	    len,
 	int	    id,
 	int	    eval,
 	jobopt_T    *opt,
@@ -3938,7 +3971,7 @@ send_common(
 				       opt->jo_callback, opt->jo_partial, id);
     }
 
-    if (channel_send(channel, part_send, text, (int)STRLEN(text), fun) == OK
+    if (channel_send(channel, part_send, text, len, fun) == OK
 						  && opt->jo_callback == NULL)
 	return channel;
     return NULL;
@@ -3982,7 +4015,7 @@ ch_expr_common(typval_T *argvars, typval
     if (text == NULL)
 	return;
 
-    channel = send_common(argvars, text, id, eval, &opt,
+    channel = send_common(argvars, text, (int)STRLEN(text), id, eval, &opt,
 			    eval ? "ch_evalexpr" : "ch_sendexpr", &part_read);
     vim_free(text);
     if (channel != NULL && eval)
@@ -4014,6 +4047,7 @@ ch_raw_common(typval_T *argvars, typval_
 {
     char_u	buf[NUMBUFLEN];
     char_u	*text;
+    int		len;
     channel_T	*channel;
     ch_part_T	part_read;
     jobopt_T    opt;
@@ -4023,8 +4057,17 @@ ch_raw_common(typval_T *argvars, typval_
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
 
-    text = tv_get_string_buf(&argvars[1], buf);
-    channel = send_common(argvars, text, 0, eval, &opt,
+    if (argvars[1].v_type == VAR_BLOB)
+    {
+	text = argvars[1].vval.v_blob->bv_ga.ga_data;
+	len = argvars[1].vval.v_blob->bv_ga.ga_len;
+    }
+    else
+    {
+	text = tv_get_string_buf(&argvars[1], buf);
+	len = STRLEN(text);
+    }
+    channel = send_common(argvars, text, len, 0, eval, &opt,
 			      eval ? "ch_evalraw" : "ch_sendraw", &part_read);
     if (channel != NULL && eval)
     {
@@ -4033,7 +4076,7 @@ ch_raw_common(typval_T *argvars, typval_
 	else
 	    timeout = channel_get_timeout(channel, part_read);
 	rettv->vval.v_string = channel_read_block(channel, part_read,
-								timeout, TRUE);
+							timeout, TRUE, NULL);
     }
     free_job_options(&opt);
 }