# HG changeset patch # User Christian Brabandt # Date 1465331406 -7200 # Node ID 6ee88fa405b323377fe8615237d5bf74a8dfcc36 # Parent fe03dc56fba7704ccc2e4591314b834779040814 commit https://github.com/vim/vim/commit/5f1032d2a55b9417a0a6fa225e35089c98a5a419 Author: Bram Moolenaar Date: Tue Jun 7 22:16:36 2016 +0200 patch 7.4.1906 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. diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -1550,6 +1550,35 @@ invoke_callback(channel_T *channel, char } /* + * 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. * Returns NULL if there is nothing. @@ -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 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,10 +1641,33 @@ channel_get_all(channel_T *channel, int 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. * When "want_nl" is TRUE collapse more buffers until a NL is found. @@ -1637,7 +1690,7 @@ channel_collapse(channel_T *channel, int 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 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 par 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, 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, { 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, } 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) */ @@ -2668,20 +2735,6 @@ channel_close(channel_T *channel, int in } /* - * 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". */ static void @@ -3043,19 +3096,23 @@ channel_read_block(channel_T *channel, i 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, i } 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, i /* 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) diff --git a/src/netbeans.c b/src/netbeans.c --- a/src/netbeans.c +++ b/src/netbeans.c @@ -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)); } } } diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -17,14 +17,16 @@ void channel_set_req_callback(channel_T 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); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1906, +/**/ 1905, /**/ 1904,