To: vim_dev@googlegroups.com Subject: Patch 7.4.1906 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/channel.c, src/netbeans.c, src/proto/channel.pro *** ../vim-7.4.1905/src/channel.c 2016-06-05 16:10:49.537012299 +0200 --- src/channel.c 2016-06-07 22:09:11.450339521 +0200 *************** *** 1550,1555 **** --- 1550,1584 ---- } /* + * 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,1581 **** --- 1605,1611 ---- /* * 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,1605 **** p = res; for (node = head->rq_next; node != NULL; node = node->rq_next) { ! STRCPY(p, node->rq_buffer); p += node->rq_buflen; } *p = NUL; --- 1629,1635 ---- p = res; for (node = head->rq_next; node != NULL; node = node->rq_next) { ! mch_memmove(p, node->rq_buffer, node->rq_buflen); p += node->rq_buflen; } *p = NUL; *************** *** 1611,1620 **** --- 1641,1673 ---- 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,1643 **** 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) { last_node = last_node->rq_next; len += last_node->rq_buflen; --- 1690,1696 ---- len = node->rq_buflen + last_node->rq_buflen + 1; if (want_nl) while (last_node->rq_next != NULL ! && channel_first_nl(last_node) == NULL) { last_node = last_node->rq_next; len += last_node->rq_buflen; *************** *** 1646,1659 **** p = newbuf = alloc(len); if (newbuf == NULL) return FAIL; /* out of memory */ ! STRCPY(p, node->rq_buffer); 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); p += n->rq_buflen; vim_free(n->rq_buffer); } --- 1699,1712 ---- p = newbuf = alloc(len); if (newbuf == NULL) return FAIL; /* out of memory */ ! 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; ! mch_memmove(p, n->rq_buffer, n->rq_buflen); p += n->rq_buflen; vim_free(n->rq_buffer); } *************** *** 1691,1696 **** --- 1744,1751 ---- 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,2288 **** --- 2338,2344 ---- 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,2393 **** { char_u *nl; char_u *buf; /* 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); if (nl != NULL) break; if (channel_collapse(channel, part, TRUE) == FAIL) return FALSE; /* incomplete message */ } ! if (nl[1] == NUL) { /* get the whole buffer, drop the NL */ msg = channel_get(channel, part); --- 2431,2457 ---- { 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) { ! 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 */ } ! 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,2410 **** } else { ! /* 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); } } else /* For a raw channel we don't know where the message ends, just ! * get everything we have. */ msg = channel_get_all(channel, part); if (msg == NULL) return FALSE; /* out of memory (and avoids Coverity warning) */ --- 2459,2477 ---- } else { ! /* 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)); ! 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. ! * 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,2687 **** } /* - * 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 --- 2735,2740 ---- *************** *** 3043,3061 **** ch_mode_T mode = channel->ch_part[part].ch_mode; sock_T fd = channel->ch_part[part].ch_fd; char_u *nl; 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; /* Wait for up to the channel timeout. */ if (fd == INVALID_FD) --- 3096,3118 ---- 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) { ! 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,3081 **** } else { ! nl = vim_strchr(buf, NL); ! if (nl[1] == NUL) { /* get the whole buffer */ msg = channel_get(channel, part); --- 3131,3147 ---- } else { ! 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,3092 **** /* 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); } } if (log_fd != NULL) --- 3152,3158 ---- /* Copy the message into allocated memory and remove it from the * buffer. */ msg = vim_strnsave(buf, (int)(nl - buf)); ! channel_consume(channel, part, (int)(nl - buf) + 1); } } if (log_fd != NULL) *** ../vim-7.4.1905/src/netbeans.c 2016-06-04 17:17:07.042146086 +0200 --- src/netbeans.c 2016-06-07 21:11:33.302387091 +0200 *************** *** 382,399 **** void netbeans_parse_messages(void) { char_u *buffer; char_u *p; int own_node; while (nb_channel != NULL) { ! buffer = channel_peek(nb_channel, PART_SOCK); ! if (buffer == NULL) break; /* nothing to read */ /* Locate the first line in the first buffer. */ ! p = vim_strchr(buffer, '\n'); if (p == NULL) { /* Command isn't complete. If there is no following buffer, --- 382,400 ---- void netbeans_parse_messages(void) { + readq_T *node; char_u *buffer; char_u *p; int own_node; while (nb_channel != NULL) { ! node = channel_peek(nb_channel, PART_SOCK); ! if (node == NULL) break; /* nothing to read */ /* Locate the first line in the first buffer. */ ! p = channel_first_nl(node); if (p == NULL) { /* Command isn't complete. If there is no following buffer, *************** *** 418,431 **** own_node = FALSE; /* now, parse and execute the commands */ ! nb_parse_cmd(buffer); if (own_node) /* buffer finished, dispose of it */ ! vim_free(buffer); else /* more follows, move it to the start */ ! STRMOVE(buffer, p); } } } --- 419,432 ---- own_node = FALSE; /* now, parse and execute the commands */ ! nb_parse_cmd(node->rq_buffer); if (own_node) /* buffer finished, dispose of it */ ! vim_free(node->rq_buffer); else /* more follows, move it to the start */ ! channel_consume(nb_channel, PART_SOCK, (int)(p - buffer)); } } } *** ../vim-7.4.1905/src/proto/channel.pro 2016-06-04 17:17:07.042146086 +0200 --- src/proto/channel.pro 2016-06-07 21:11:54.838386794 +0200 *************** *** 17,30 **** void channel_buffer_free(buf_T *buf); void channel_write_any_lines(void); void channel_write_new_lines(buf_T *buf); char_u *channel_get(channel_T *channel, int part); 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); --- 17,32 ---- 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); void channel_clear(channel_T *channel); void channel_free_all(void); char_u *channel_read_block(channel_T *channel, int part, int timeout); *** ../vim-7.4.1905/src/version.c 2016-06-06 21:38:39.159553227 +0200 --- src/version.c 2016-06-07 22:10:43.882338249 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 1906, /**/ -- Q: Should I clean my house or work on Vim? A: Whatever contains more bugs. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///