patch 7.4.1906 v7.4.1906
authorBram Moolenaar <Bram@vim.org>
Tue, 7 Jun 2016 20:16:36 +0000 (22:16 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 7 Jun 2016 20:16:36 +0000 (22:16 +0200)
Problem:    Collapsing channel buffers and searching for NL does not work
            properly. (Xavier de Gary, Ramel Eshed)
Solution:   Do not assume the buffer contains a NUL or not.  Change NUL bytes
            to NL to avoid the string is truncated.

src/channel.c
src/netbeans.c
src/proto/channel.pro
src/version.c

index e60d49e4cf1afcbfcb5818acafe12f506ad119cf..d6ab030de69fec0dc88171bf27496261e88df367 100644 (file)
@@ -1549,6 +1549,35 @@ invoke_callback(channel_T *channel, char_u *callback, partial_T *partial,
     channel_need_redraw = TRUE;
 }
 
+/*
+ * Return the first node from "channel"/"part" without removing it.
+ * Returns NULL if there is nothing.
+ */
+    readq_T *
+channel_peek(channel_T *channel, int part)
+{
+    readq_T *head = &channel->ch_part[part].ch_head;
+
+    return head->rq_next;
+}
+
+/*
+ * Return a pointer to the first NL in "node".
+ * Skips over NUL characters.
+ * Returns NULL if there is no NL.
+ */
+    char_u *
+channel_first_nl(readq_T *node)
+{
+    char_u  *buffer = node->rq_buffer;
+    long_u  i;
+
+    for (i = 0; i < node->rq_buflen; ++i)
+       if (buffer[i] == NL)
+           return buffer + i;
+    return NULL;
+}
+
 /*
  * Return the first buffer from channel "channel"/"part" and remove it.
  * The caller must free it.
@@ -1576,6 +1605,7 @@ channel_get(channel_T *channel, int part)
 
 /*
  * Returns the whole buffer contents concatenated for "channel"/"part".
+ * Replaces NUL bytes with NL.
  */
     static char_u *
 channel_get_all(channel_T *channel, int part)
@@ -1599,7 +1629,7 @@ channel_get_all(channel_T *channel, int part)
     p = res;
     for (node = head->rq_next; node != NULL; node = node->rq_next)
     {
-       STRCPY(p, node->rq_buffer);
+       mch_memmove(p, node->rq_buffer, node->rq_buflen);
        p += node->rq_buflen;
     }
     *p = NUL;
@@ -1611,9 +1641,32 @@ channel_get_all(channel_T *channel, int part)
        vim_free(p);
     } while (p != NULL);
 
+    /* turn all NUL into NL */
+    while (len > 0)
+    {
+       --len;
+       if (res[len] == NUL)
+           res[len] = NL;
+    }
+
     return res;
 }
 
+/*
+ * Consume "len" bytes from the head of "channel"/"part".
+ * Caller must check these bytes are available.
+ */
+    void
+channel_consume(channel_T *channel, int part, int len)
+{
+    readq_T *head = &channel->ch_part[part].ch_head;
+    readq_T *node = head->rq_next;
+    char_u *buf = node->rq_buffer;
+
+    mch_memmove(buf, buf + len, node->rq_buflen - len);
+    node->rq_buflen -= len;
+}
+
 /*
  * Collapses the first and second buffer for "channel"/"part".
  * Returns FAIL if that is not possible.
@@ -1637,7 +1690,7 @@ channel_collapse(channel_T *channel, int part, int want_nl)
     len = node->rq_buflen + last_node->rq_buflen + 1;
     if (want_nl)
        while (last_node->rq_next != NULL
-               && vim_strchr(last_node->rq_buffer, NL) == NULL)
+               && channel_first_nl(last_node) == NULL)
        {
            last_node = last_node->rq_next;
            len += last_node->rq_buflen;
@@ -1646,14 +1699,14 @@ channel_collapse(channel_T *channel, int part, int want_nl)
     p = newbuf = alloc(len);
     if (newbuf == NULL)
        return FAIL;        /* out of memory */
-    STRCPY(p, node->rq_buffer);
+    mch_memmove(p, node->rq_buffer, node->rq_buflen);
     p += node->rq_buflen;
     vim_free(node->rq_buffer);
     node->rq_buffer = newbuf;
     for (n = node; n != last_node; )
     {
        n = n->rq_next;
-       STRCPY(p, n->rq_buffer);
+       mch_memmove(p, n->rq_buffer, n->rq_buflen);
        p += n->rq_buflen;
        vim_free(n->rq_buffer);
     }
@@ -1691,6 +1744,8 @@ channel_save(channel_T *channel, int part, char_u *buf, int len,
     node = (readq_T *)alloc(sizeof(readq_T));
     if (node == NULL)
        return FAIL;        /* out of memory */
+    /* A NUL is added at the end, because netbeans code expects that.
+     * Otherwise a NUL may appear inside the text. */
     node->rq_buffer = alloc(len + 1);
     if (node->rq_buffer == NULL)
     {
@@ -2283,6 +2338,7 @@ may_invoke_callback(channel_T *channel, int part)
     char_u     *callback = NULL;
     partial_T  *partial = NULL;
     buf_T      *buffer = NULL;
+    char_u     *p;
 
     if (channel->ch_nb_close_cb != NULL)
        /* this channel is handled elsewhere (netbeans) */
@@ -2375,19 +2431,27 @@ may_invoke_callback(channel_T *channel, int part)
        {
            char_u  *nl;
            char_u  *buf;
+           readq_T *node;
 
            /* See if we have a message ending in NL in the first buffer.  If
             * not try to concatenate the first and the second buffer. */
            while (TRUE)
            {
-               buf = channel_peek(channel, part);
-               nl = vim_strchr(buf, NL);
+               node = channel_peek(channel, part);
+               nl = channel_first_nl(node);
                if (nl != NULL)
                    break;
                if (channel_collapse(channel, part, TRUE) == FAIL)
                    return FALSE; /* incomplete message */
            }
-           if (nl[1] == NUL)
+           buf = node->rq_buffer;
+
+           /* Convert NUL to NL, the internal representation. */
+           for (p = buf; p < nl && p < buf + node->rq_buflen; ++p)
+               if (*p == NUL)
+                   *p = NL;
+
+           if (nl + 1 == buf + node->rq_buflen)
            {
                /* get the whole buffer, drop the NL */
                msg = channel_get(channel, part);
@@ -2395,16 +2459,19 @@ may_invoke_callback(channel_T *channel, int part)
            }
            else
            {
-               /* Copy the message into allocated memory and remove it from
-                * the buffer. */
+               /* Copy the message into allocated memory (excluding the NL)
+                * and remove it from the buffer (including the NL). */
                msg = vim_strnsave(buf, (int)(nl - buf));
-               mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1);
+               channel_consume(channel, part, (int)(nl - buf) + 1);
            }
        }
        else
+       {
            /* For a raw channel we don't know where the message ends, just
-            * get everything we have. */
+            * get everything we have.
+            * Convert NUL to NL, the internal representation. */
            msg = channel_get_all(channel, part);
+       }
 
        if (msg == NULL)
            return FALSE; /* out of memory (and avoids Coverity warning) */
@@ -2667,20 +2734,6 @@ channel_close(channel_T *channel, int invoke_close_cb)
     channel->ch_nb_close_cb = NULL;
 }
 
-/*
- * Return the first buffer from "channel"/"part" without removing it.
- * Returns NULL if there is nothing.
- */
-    char_u *
-channel_peek(channel_T *channel, int part)
-{
-    readq_T *head = &channel->ch_part[part].ch_head;
-
-    if (head->rq_next == NULL)
-       return NULL;
-    return head->rq_next->rq_buffer;
-}
-
 /*
  * Clear the read buffer on "channel"/"part".
  */
@@ -3043,19 +3096,23 @@ channel_read_block(channel_T *channel, int part, int timeout)
     ch_mode_T  mode = channel->ch_part[part].ch_mode;
     sock_T     fd = channel->ch_part[part].ch_fd;
     char_u     *nl;
+    readq_T    *node;
 
     ch_logsn(channel, "Blocking %s read, timeout: %d msec",
                                    mode == MODE_RAW ? "RAW" : "NL", timeout);
 
     while (TRUE)
     {
-       buf = channel_peek(channel, part);
-       if (buf != NULL && (mode == MODE_RAW
-                        || (mode == MODE_NL && vim_strchr(buf, NL) != NULL)))
-           break;
-       if (buf != NULL && channel_collapse(channel, part, mode == MODE_NL)
-                                                                       == OK)
-           continue;
+       node = channel_peek(channel, part);
+       if (node != NULL)
+       {
+           if (mode == MODE_RAW || (mode == MODE_NL
+                                          && channel_first_nl(node) != NULL))
+               /* got a complete message */
+               break;
+           if (channel_collapse(channel, part, mode == MODE_NL) == OK)
+               continue;
+       }
 
        /* Wait for up to the channel timeout. */
        if (fd == INVALID_FD)
@@ -3074,8 +3131,17 @@ channel_read_block(channel_T *channel, int part, int timeout)
     }
     else
     {
-       nl = vim_strchr(buf, NL);
-       if (nl[1] == NUL)
+       char_u *p;
+
+       buf = node->rq_buffer;
+       nl = channel_first_nl(node);
+
+       /* Convert NUL to NL, the internal representation. */
+       for (p = buf; p < nl && p < buf + node->rq_buflen; ++p)
+           if (*p == NUL)
+               *p = NL;
+
+       if (nl + 1 == buf + node->rq_buflen)
        {
            /* get the whole buffer */
            msg = channel_get(channel, part);
@@ -3086,7 +3152,7 @@ channel_read_block(channel_T *channel, int part, int timeout)
            /* Copy the message into allocated memory and remove it from the
             * buffer. */
            msg = vim_strnsave(buf, (int)(nl - buf));
-           mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1);
+           channel_consume(channel, part, (int)(nl - buf) + 1);
        }
     }
     if (log_fd != NULL)
index 3eaf2a2b81629e9efaf614f7b4abaf832d7edd0b..9a9181c0ac42771d53760a33794bb722f884c05b 100644 (file)
@@ -382,18 +382,19 @@ handle_key_queue(void)
     void
 netbeans_parse_messages(void)
 {
+    readq_T    *node;
     char_u     *buffer;
     char_u     *p;
     int                own_node;
 
     while (nb_channel != NULL)
     {
-       buffer = channel_peek(nb_channel, PART_SOCK);
-       if (buffer == NULL)
+       node = channel_peek(nb_channel, PART_SOCK);
+       if (node == NULL)
            break;      /* nothing to read */
 
        /* Locate the first line in the first buffer. */
-       p = vim_strchr(buffer, '\n');
+       p = channel_first_nl(node);
        if (p == NULL)
        {
            /* Command isn't complete.  If there is no following buffer,
@@ -418,14 +419,14 @@ netbeans_parse_messages(void)
                own_node = FALSE;
 
            /* now, parse and execute the commands */
-           nb_parse_cmd(buffer);
+           nb_parse_cmd(node->rq_buffer);
 
            if (own_node)
                /* buffer finished, dispose of it */
-               vim_free(buffer);
+               vim_free(node->rq_buffer);
            else
                /* more follows, move it to the start */
-               STRMOVE(buffer, p);
+               channel_consume(nb_channel, PART_SOCK, (int)(p - buffer));
        }
     }
 }
index 52bced3b93b2106d67b8978fd2651149ecdc7328..8a059a3a9dd205bdcbfd2cdc42433df6bc4287fb 100644 (file)
@@ -17,14 +17,16 @@ void channel_set_req_callback(channel_T *channel, int part, char_u *callback, pa
 void channel_buffer_free(buf_T *buf);
 void channel_write_any_lines(void);
 void channel_write_new_lines(buf_T *buf);
+readq_T *channel_peek(channel_T *channel, int part);
+char_u *channel_first_nl(readq_T *node);
 char_u *channel_get(channel_T *channel, int part);
+void channel_consume(channel_T *channel, int part, int len);
 int channel_collapse(channel_T *channel, int part, int want_nl);
 int channel_can_write_to(channel_T *channel);
 int channel_is_open(channel_T *channel);
 char *channel_status(channel_T *channel);
 void channel_info(channel_T *channel, dict_T *dict);
 void channel_close(channel_T *channel, int invoke_close_cb);
-char_u *channel_peek(channel_T *channel, int part);
 void channel_clear(channel_T *channel);
 void channel_free_all(void);
 char_u *channel_read_block(channel_T *channel, int part, int timeout);
index 7e7dd48bb86230f2fc3150d4bd28f2f3eea23c9c..547f30add633eb2df9876aec91aedebfd24487ee 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1906,
 /**/
     1905,
 /**/