Mercurial > vim
diff src/channel.c @ 9246:6ee88fa405b3 v7.4.1906
commit https://github.com/vim/vim/commit/5f1032d2a55b9417a0a6fa225e35089c98a5a419
Author: Bram Moolenaar <Bram@vim.org>
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.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Tue, 07 Jun 2016 22:30:06 +0200 |
parents | 7d13d180a6ae |
children | 9f97a6290c63 |
line wrap: on
line diff
--- 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)