mirror of
https://github.com/vim/vim.git
synced 2025-09-29 04:34:16 -04:00
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.
This commit is contained in:
136
src/channel.c
136
src/channel.c
@@ -1549,6 +1549,35 @@ invoke_callback(channel_T *channel, char_u *callback, partial_T *partial,
|
|||||||
channel_need_redraw = TRUE;
|
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.
|
* Return the first buffer from channel "channel"/"part" and remove it.
|
||||||
* The caller must free 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".
|
* Returns the whole buffer contents concatenated for "channel"/"part".
|
||||||
|
* Replaces NUL bytes with NL.
|
||||||
*/
|
*/
|
||||||
static char_u *
|
static char_u *
|
||||||
channel_get_all(channel_T *channel, int part)
|
channel_get_all(channel_T *channel, int part)
|
||||||
@@ -1599,7 +1629,7 @@ channel_get_all(channel_T *channel, int part)
|
|||||||
p = res;
|
p = res;
|
||||||
for (node = head->rq_next; node != NULL; node = node->rq_next)
|
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 += node->rq_buflen;
|
||||||
}
|
}
|
||||||
*p = NUL;
|
*p = NUL;
|
||||||
@@ -1611,9 +1641,32 @@ channel_get_all(channel_T *channel, int part)
|
|||||||
vim_free(p);
|
vim_free(p);
|
||||||
} while (p != NULL);
|
} while (p != NULL);
|
||||||
|
|
||||||
|
/* turn all NUL into NL */
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
--len;
|
||||||
|
if (res[len] == NUL)
|
||||||
|
res[len] = NL;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
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".
|
* Collapses the first and second buffer for "channel"/"part".
|
||||||
* Returns FAIL if that is not possible.
|
* 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;
|
len = node->rq_buflen + last_node->rq_buflen + 1;
|
||||||
if (want_nl)
|
if (want_nl)
|
||||||
while (last_node->rq_next != NULL
|
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;
|
last_node = last_node->rq_next;
|
||||||
len += last_node->rq_buflen;
|
len += last_node->rq_buflen;
|
||||||
@@ -1646,14 +1699,14 @@ channel_collapse(channel_T *channel, int part, int want_nl)
|
|||||||
p = newbuf = alloc(len);
|
p = newbuf = alloc(len);
|
||||||
if (newbuf == NULL)
|
if (newbuf == NULL)
|
||||||
return FAIL; /* out of memory */
|
return FAIL; /* out of memory */
|
||||||
STRCPY(p, node->rq_buffer);
|
mch_memmove(p, node->rq_buffer, node->rq_buflen);
|
||||||
p += node->rq_buflen;
|
p += node->rq_buflen;
|
||||||
vim_free(node->rq_buffer);
|
vim_free(node->rq_buffer);
|
||||||
node->rq_buffer = newbuf;
|
node->rq_buffer = newbuf;
|
||||||
for (n = node; n != last_node; )
|
for (n = node; n != last_node; )
|
||||||
{
|
{
|
||||||
n = n->rq_next;
|
n = n->rq_next;
|
||||||
STRCPY(p, n->rq_buffer);
|
mch_memmove(p, n->rq_buffer, n->rq_buflen);
|
||||||
p += n->rq_buflen;
|
p += n->rq_buflen;
|
||||||
vim_free(n->rq_buffer);
|
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));
|
node = (readq_T *)alloc(sizeof(readq_T));
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return FAIL; /* out of memory */
|
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);
|
node->rq_buffer = alloc(len + 1);
|
||||||
if (node->rq_buffer == NULL)
|
if (node->rq_buffer == NULL)
|
||||||
{
|
{
|
||||||
@@ -2283,6 +2338,7 @@ may_invoke_callback(channel_T *channel, int part)
|
|||||||
char_u *callback = NULL;
|
char_u *callback = NULL;
|
||||||
partial_T *partial = NULL;
|
partial_T *partial = NULL;
|
||||||
buf_T *buffer = NULL;
|
buf_T *buffer = NULL;
|
||||||
|
char_u *p;
|
||||||
|
|
||||||
if (channel->ch_nb_close_cb != NULL)
|
if (channel->ch_nb_close_cb != NULL)
|
||||||
/* this channel is handled elsewhere (netbeans) */
|
/* this channel is handled elsewhere (netbeans) */
|
||||||
@@ -2375,19 +2431,27 @@ may_invoke_callback(channel_T *channel, int part)
|
|||||||
{
|
{
|
||||||
char_u *nl;
|
char_u *nl;
|
||||||
char_u *buf;
|
char_u *buf;
|
||||||
|
readq_T *node;
|
||||||
|
|
||||||
/* See if we have a message ending in NL in the first buffer. If
|
/* See if we have a message ending in NL in the first buffer. If
|
||||||
* not try to concatenate the first and the second buffer. */
|
* not try to concatenate the first and the second buffer. */
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
buf = channel_peek(channel, part);
|
node = channel_peek(channel, part);
|
||||||
nl = vim_strchr(buf, NL);
|
nl = channel_first_nl(node);
|
||||||
if (nl != NULL)
|
if (nl != NULL)
|
||||||
break;
|
break;
|
||||||
if (channel_collapse(channel, part, TRUE) == FAIL)
|
if (channel_collapse(channel, part, TRUE) == FAIL)
|
||||||
return FALSE; /* incomplete message */
|
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 */
|
/* get the whole buffer, drop the NL */
|
||||||
msg = channel_get(channel, part);
|
msg = channel_get(channel, part);
|
||||||
@@ -2395,16 +2459,19 @@ may_invoke_callback(channel_T *channel, int part)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Copy the message into allocated memory and remove it from
|
/* Copy the message into allocated memory (excluding the NL)
|
||||||
* the buffer. */
|
* and remove it from the buffer (including the NL). */
|
||||||
msg = vim_strnsave(buf, (int)(nl - buf));
|
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
|
else
|
||||||
|
{
|
||||||
/* For a raw channel we don't know where the message ends, just
|
/* 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);
|
msg = channel_get_all(channel, part);
|
||||||
|
}
|
||||||
|
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return FALSE; /* out of memory (and avoids Coverity warning) */
|
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;
|
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".
|
* 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;
|
ch_mode_T mode = channel->ch_part[part].ch_mode;
|
||||||
sock_T fd = channel->ch_part[part].ch_fd;
|
sock_T fd = channel->ch_part[part].ch_fd;
|
||||||
char_u *nl;
|
char_u *nl;
|
||||||
|
readq_T *node;
|
||||||
|
|
||||||
ch_logsn(channel, "Blocking %s read, timeout: %d msec",
|
ch_logsn(channel, "Blocking %s read, timeout: %d msec",
|
||||||
mode == MODE_RAW ? "RAW" : "NL", timeout);
|
mode == MODE_RAW ? "RAW" : "NL", timeout);
|
||||||
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
buf = channel_peek(channel, part);
|
node = channel_peek(channel, part);
|
||||||
if (buf != NULL && (mode == MODE_RAW
|
if (node != NULL)
|
||||||
|| (mode == MODE_NL && vim_strchr(buf, NL) != NULL)))
|
{
|
||||||
break;
|
if (mode == MODE_RAW || (mode == MODE_NL
|
||||||
if (buf != NULL && channel_collapse(channel, part, mode == MODE_NL)
|
&& channel_first_nl(node) != NULL))
|
||||||
== OK)
|
/* got a complete message */
|
||||||
continue;
|
break;
|
||||||
|
if (channel_collapse(channel, part, mode == MODE_NL) == OK)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for up to the channel timeout. */
|
/* Wait for up to the channel timeout. */
|
||||||
if (fd == INVALID_FD)
|
if (fd == INVALID_FD)
|
||||||
@@ -3074,8 +3131,17 @@ channel_read_block(channel_T *channel, int part, int timeout)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nl = vim_strchr(buf, NL);
|
char_u *p;
|
||||||
if (nl[1] == NUL)
|
|
||||||
|
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 */
|
/* get the whole buffer */
|
||||||
msg = channel_get(channel, part);
|
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
|
/* Copy the message into allocated memory and remove it from the
|
||||||
* buffer. */
|
* buffer. */
|
||||||
msg = vim_strnsave(buf, (int)(nl - buf));
|
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)
|
if (log_fd != NULL)
|
||||||
|
@@ -382,18 +382,19 @@ handle_key_queue(void)
|
|||||||
void
|
void
|
||||||
netbeans_parse_messages(void)
|
netbeans_parse_messages(void)
|
||||||
{
|
{
|
||||||
|
readq_T *node;
|
||||||
char_u *buffer;
|
char_u *buffer;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
int own_node;
|
int own_node;
|
||||||
|
|
||||||
while (nb_channel != NULL)
|
while (nb_channel != NULL)
|
||||||
{
|
{
|
||||||
buffer = channel_peek(nb_channel, PART_SOCK);
|
node = channel_peek(nb_channel, PART_SOCK);
|
||||||
if (buffer == NULL)
|
if (node == NULL)
|
||||||
break; /* nothing to read */
|
break; /* nothing to read */
|
||||||
|
|
||||||
/* Locate the first line in the first buffer. */
|
/* Locate the first line in the first buffer. */
|
||||||
p = vim_strchr(buffer, '\n');
|
p = channel_first_nl(node);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
{
|
{
|
||||||
/* Command isn't complete. If there is no following buffer,
|
/* Command isn't complete. If there is no following buffer,
|
||||||
@@ -418,14 +419,14 @@ netbeans_parse_messages(void)
|
|||||||
own_node = FALSE;
|
own_node = FALSE;
|
||||||
|
|
||||||
/* now, parse and execute the commands */
|
/* now, parse and execute the commands */
|
||||||
nb_parse_cmd(buffer);
|
nb_parse_cmd(node->rq_buffer);
|
||||||
|
|
||||||
if (own_node)
|
if (own_node)
|
||||||
/* buffer finished, dispose of it */
|
/* buffer finished, dispose of it */
|
||||||
vim_free(buffer);
|
vim_free(node->rq_buffer);
|
||||||
else
|
else
|
||||||
/* more follows, move it to the start */
|
/* more follows, move it to the start */
|
||||||
STRMOVE(buffer, p);
|
channel_consume(nb_channel, PART_SOCK, (int)(p - buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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_buffer_free(buf_T *buf);
|
||||||
void channel_write_any_lines(void);
|
void channel_write_any_lines(void);
|
||||||
void channel_write_new_lines(buf_T *buf);
|
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);
|
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_collapse(channel_T *channel, int part, int want_nl);
|
||||||
int channel_can_write_to(channel_T *channel);
|
int channel_can_write_to(channel_T *channel);
|
||||||
int channel_is_open(channel_T *channel);
|
int channel_is_open(channel_T *channel);
|
||||||
char *channel_status(channel_T *channel);
|
char *channel_status(channel_T *channel);
|
||||||
void channel_info(channel_T *channel, dict_T *dict);
|
void channel_info(channel_T *channel, dict_T *dict);
|
||||||
void channel_close(channel_T *channel, int invoke_close_cb);
|
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_clear(channel_T *channel);
|
||||||
void channel_free_all(void);
|
void channel_free_all(void);
|
||||||
char_u *channel_read_block(channel_T *channel, int part, int timeout);
|
char_u *channel_read_block(channel_T *channel, int part, int timeout);
|
||||||
|
@@ -753,6 +753,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1906,
|
||||||
/**/
|
/**/
|
||||||
1905,
|
1905,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user