Patch 7.4.1231
Problem: JSON messages are not parsed properly.
Solution: Queue received messages.
Files: src/eval,c src/channel.c, src/json.c, src/proto/eval.pro,
src/proto/channel.pro, src/proto/json.pro, src/structs.h
*** ../vim-7.4.1230/src/channel.c 2016-01-31 20:24:09.962066926 +0100
--- src/channel.c 2016-02-01 02:02:48.158391509 +0100
***************
*** 74,85 ****
struct readqueue *next;
struct readqueue *prev;
};
! typedef struct readqueue queue_T;
typedef struct {
sock_T ch_fd; /* the socket, -1 for a closed channel */
int ch_idx; /* used by channel_poll_setup() */
! queue_T ch_head; /* dummy node, header for circular queue */
int ch_error; /* When TRUE an error was reported. Avoids
giving
* pages full of error messages when the other side
--- 74,93 ----
struct readqueue *next;
struct readqueue *prev;
};
! typedef struct readqueue readq_T;
!
! struct jsonqueue
! {
! typval_T *value;
! struct jsonqueue *next;
! struct jsonqueue *prev;
! };
! typedef struct jsonqueue jsonq_T;
typedef struct {
sock_T ch_fd; /* the socket, -1 for a closed channel */
int ch_idx; /* used by channel_poll_setup() */
! readq_T ch_head; /* dummy node, header for circular queue */
int ch_error; /* When TRUE an error was reported. Avoids
giving
* pages full of error messages when the other side
***************
*** 100,106 ****
char_u *ch_callback; /* function to call when a msg is not handled */
char_u *ch_req_callback; /* function to call for current request
*/
! int ch_json_mode;
} channel_T;
/*
--- 108,115 ----
char_u *ch_callback; /* function to call when a msg is not handled */
char_u *ch_req_callback; /* function to call for current request
*/
! int ch_json_mode; /* TRUE for a json channel */
! jsonq_T ch_json_head; /* dummy node, header for circular queue */
} channel_T;
/*
***************
*** 125,130 ****
--- 134,140 ----
{
int idx;
channel_T *new_channels;
+ channel_T *ch;
if (channels != NULL)
for (idx = 0; idx < channel_count; ++idx)
***************
*** 139,156 ****
if (channels != NULL)
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
channels = new_channels;
! (void)vim_memset(&channels[channel_count], 0, sizeof(channel_T));
! channels[channel_count].ch_fd = (sock_T)-1;
#ifdef FEAT_GUI_X11
! channels[channel_count].ch_inputHandler = (XtInputId)NULL;
#endif
#ifdef FEAT_GUI_GTK
! channels[channel_count].ch_inputHandler = 0;
#endif
#ifdef FEAT_GUI_W32
! channels[channel_count].ch_inputHandler = -1;
#endif
return channel_count++;
}
--- 149,172 ----
if (channels != NULL)
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
channels = new_channels;
! ch = &channels[channel_count];
! (void)vim_memset(ch, 0, sizeof(channel_T));
! ch->ch_fd = (sock_T)-1;
#ifdef FEAT_GUI_X11
! ch->ch_inputHandler = (XtInputId)NULL;
#endif
#ifdef FEAT_GUI_GTK
! ch->ch_inputHandler = 0;
#endif
#ifdef FEAT_GUI_W32
! ch->ch_inputHandler = -1;
#endif
+ /* initialize circular queues */
+ ch->ch_head.next = &ch->ch_head;
+ ch->ch_head.prev = &ch->ch_head;
+ ch->ch_json_head.next = &ch->ch_json_head;
+ ch->ch_json_head.prev = &ch->ch_json_head;
return channel_count++;
}
***************
*** 412,492 ****
void
channel_set_req_callback(int idx, char_u *callback)
{
vim_free(channels[idx].ch_req_callback);
channels[idx].ch_req_callback = callback == NULL
? NULL : vim_strsave(callback);
}
/*
! * Decode JSON "msg", which must have the form "[expr1, expr2, expr3]".
! * Put "expr1" in "tv1".
! * Put "expr2" in "tv2".
! * Put "expr3" in "tv3". If "tv3" is NULL there is no "expr3".
! *
! * Return OK or FAIL.
*/
int
! channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2, typval_T *tv3)
{
js_read_T reader;
typval_T listtv;
! reader.js_buf = msg;
reader.js_eof = TRUE;
reader.js_used = 0;
! json_decode(&reader, &listtv);
!
! if (listtv.v_type == VAR_LIST)
! {
! list_T *list = listtv.vval.v_list;
!
! if (list->lv_len == 2 || (tv3 != NULL && list->lv_len == 3))
{
! /* Move the item from the list and then change the type to avoid the
! * item being freed. */
! *tv1 = list->lv_first->li_tv;
! list->lv_first->li_tv.v_type = VAR_NUMBER;
! *tv2 = list->lv_first->li_next->li_tv;
! list->lv_first->li_next->li_tv.v_type = VAR_NUMBER;
! if (tv3 != NULL)
{
! if (list->lv_len == 3)
! {
! *tv3 = list->lv_last->li_tv;
! list->lv_last->li_tv.v_type = VAR_NUMBER;
! }
! else
! tv3->v_type = VAR_UNKNOWN;
}
- list_unref(list);
- return OK;
}
}
-
- /* give error message? */
- clear_tv(&listtv);
- return FAIL;
}
/*
! * Invoke the "callback" on channel "idx".
*/
static void
! invoke_callback(int idx, char_u *callback, typval_T *argv)
{
! typval_T rettv;
! int dummy;
! argv[0].v_type = VAR_NUMBER;
! argv[0].vval.v_number = idx;
! call_func(callback, (int)STRLEN(callback),
! &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
! /* If an echo command was used the cursor needs to be put back where
! * it belongs. */
! setcursor();
! cursor_on();
! out_flush();
}
/*
--- 428,615 ----
void
channel_set_req_callback(int idx, char_u *callback)
{
+ /* TODO: make a list of callbacks */
vim_free(channels[idx].ch_req_callback);
channels[idx].ch_req_callback = callback == NULL
? NULL : vim_strsave(callback);
}
/*
! * Invoke the "callback" on channel "idx".
! */
! static void
! invoke_callback(int idx, char_u *callback, typval_T *argv)
! {
! typval_T rettv;
! int dummy;
!
! argv[0].v_type = VAR_NUMBER;
! argv[0].vval.v_number = idx;
!
! call_func(callback, (int)STRLEN(callback),
! &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
! /* If an echo command was used the cursor needs to be put back where
! * it belongs. */
! setcursor();
! cursor_on();
! out_flush();
! }
!
! /*
! * Return the first buffer from the channel and remove it.
! * The caller must free it.
! * Returns NULL if there is nothing.
! */
! char_u *
! channel_get(int idx)
! {
! readq_T *head = &channels[idx].ch_head;
! readq_T *node;
! char_u *p;
!
! if (head->next == head || head->next == NULL)
! return NULL;
! node = head->next;
! /* dispose of the node but keep the buffer */
! p = node->buffer;
! head->next = node->next;
! node->next->prev = node->prev;
! vim_free(node);
! return p;
! }
!
! /*
! * Returns the whole buffer contents concatenated.
! */
! static char_u *
! channel_get_all(int idx)
! {
! /* Concatenate everything into one buffer.
! * TODO: avoid multiple allocations. */
! while (channel_collapse(idx) == OK)
! ;
! return channel_get(idx);
! }
!
! /*
! * Collapses the first and second buffer in the channel "idx".
! * Returns FAIL if that is not possible.
*/
int
! channel_collapse(int idx)
! {
! readq_T *head = &channels[idx].ch_head;
! readq_T *node = head->next;
! char_u *p;
!
! if (node == head || node == NULL || node->next == head)
! return FAIL;
!
! p = alloc((unsigned)(STRLEN(node->buffer)
! + STRLEN(node->next->buffer) + 1));
! if (p == NULL)
! return FAIL; /* out of memory */
! STRCPY(p, node->buffer);
! STRCAT(p, node->next->buffer);
! vim_free(node->next->buffer);
! node->next->buffer = p;
!
! /* dispose of the node and buffer */
! head->next = node->next;
! node->next->prev = node->prev;
! vim_free(node->buffer);
! vim_free(node);
! return OK;
! }
!
! /*
! * Use the read buffer of channel "ch_idx" and parse JSON messages that are
! * complete. The messages are added to the queue.
! */
! void
! channel_read_json(int ch_idx)
{
js_read_T reader;
typval_T listtv;
+ jsonq_T *item;
+ jsonq_T *head = &channels[ch_idx].ch_json_head;
! if (channel_peek(ch_idx) == NULL)
! return;
!
! /* TODO: make reader work properly */
! /* reader.js_buf = channel_peek(ch_idx); */
! reader.js_buf = channel_get_all(ch_idx);
reader.js_eof = TRUE;
+ /* reader.js_eof = FALSE; */
reader.js_used = 0;
! /* reader.js_fill = channel_fill; */
! reader.js_cookie = &ch_idx;
! if (json_decode(&reader, &listtv) == OK)
! {
! item = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T));
! if (item == NULL)
! clear_tv(&listtv);
! else
{
! item->value = alloc_tv();
! if (item->value == NULL)
{
! vim_free(item);
! clear_tv(&listtv);
! }
! else
! {
! *item->value = listtv;
! item->prev = head->prev;
! head->prev = item;
! item->next = head;
! item->prev->next = item;
}
}
}
}
/*
! * Remove "node" from the queue that it is in and free it.
! * Caller should have freed or used node->value.
*/
static void
! remove_json_node(jsonq_T *node)
{
! node->prev->next = node->next;
! node->next->prev = node->prev;
! vim_free(node);
! }
! /*
! * Get a message from the JSON queue for channel "ch_idx".
! * When "id" is positive it must match the first number in the list.
! * When "id" is zero or negative jut get the first message.
! * Return OK when found and return the value in "rettv".
! * Return FAIL otherwise.
! */
! static int
! channel_get_json(int ch_idx, int id, typval_T **rettv)
! {
! jsonq_T *head = &channels[ch_idx].ch_json_head;
! jsonq_T *item = head->next;
! while (item != head)
! {
! list_T *l = item->value->vval.v_list;
! typval_T *tv = &l->lv_first->li_tv;
!
! if ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
! || id <= 0)
! {
! *rettv = item->value;
! remove_json_node(item);
! return OK;
! }
! item = item->next;
! }
! return FAIL;
}
/*
***************
*** 524,530 ****
{
exarg_T ea;
! ea.forceit = *arg != NUL;
ex_redraw(&ea);
showruler(FALSE);
setcursor();
--- 647,653 ----
{
exarg_T ea;
! ea.forceit = arg != NULL && *arg != NUL;
ex_redraw(&ea);
showruler(FALSE);
setcursor();
***************
*** 577,646 ****
static void
may_invoke_callback(int idx)
{
! char_u *msg;
! typval_T typetv;
typval_T argv[3];
- typval_T arg3;
- char_u *cmd = NULL;
int seq_nr = -1;
! int ret = OK;
if (channel_peek(idx) == NULL)
return;
! /* Concatenate everything into one buffer.
! * TODO: only read what the callback will use.
! * TODO: avoid multiple allocations. */
! while (channel_collapse(idx) == OK)
! ;
! msg = channel_get(idx);
!
! if (channels[idx].ch_json_mode)
{
! ret = channel_decode_json(msg, &typetv, &argv[1], &arg3);
! if (ret == OK)
{
! /* TODO: error if arg3 is set when it shouldn't? */
! if (typetv.v_type == VAR_STRING)
! cmd = typetv.vval.v_string;
! else if (typetv.v_type == VAR_NUMBER)
! seq_nr = typetv.vval.v_number;
}
- }
- else
- {
- argv[1].v_type = VAR_STRING;
- argv[1].vval.v_string = msg;
- }
! if (ret == OK)
! {
! if (cmd != NULL)
{
! channel_exe_cmd(idx, cmd, &argv[1], &arg3);
}
! else if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
! {
! /* TODO: check the sequence number */
! /* invoke the one-time callback */
! invoke_callback(idx, channels[idx].ch_req_callback, argv);
! channels[idx].ch_req_callback = NULL;
! }
! else if (channels[idx].ch_callback != NULL)
{
! /* invoke the channel callback */
! invoke_callback(idx, channels[idx].ch_callback, argv);
}
- /* else: drop the message */
! if (channels[idx].ch_json_mode)
{
! clear_tv(&typetv);
! clear_tv(&argv[1]);
! clear_tv(&arg3);
}
}
vim_free(msg);
}
--- 700,788 ----
static void
may_invoke_callback(int idx)
{
! char_u *msg = NULL;
! typval_T *listtv = NULL;
! list_T *list;
! typval_T *typetv;
typval_T argv[3];
int seq_nr = -1;
! int json_mode = channels[idx].ch_json_mode;
if (channel_peek(idx) == NULL)
return;
+ if (channels[idx].ch_close_cb != NULL)
+ /* this channel is handled elsewhere (netbeans) */
+ return;
! if (json_mode)
{
! /* Get any json message. Return if there isn't one. */
! channel_read_json(idx);
! if (channel_get_json(idx, -1, &listtv) == FAIL)
! return;
! if (listtv->v_type != VAR_LIST)
{
! /* TODO: give error */
! clear_tv(listtv);
! return;
}
! list = listtv->vval.v_list;
! if (list->lv_len < 2)
{
! /* TODO: give error */
! clear_tv(listtv);
! return;
}
!
! argv[1] = list->lv_first->li_next->li_tv;
! typetv = &list->lv_first->li_tv;
! if (typetv->v_type == VAR_STRING)
{
! typval_T *arg3 = NULL;
! char_u *cmd = typetv->vval.v_string;
!
! /* ["cmd", arg] */
! if (list->lv_len == 3)
! arg3 = &list->lv_last->li_tv;
! channel_exe_cmd(idx, cmd, &argv[1], arg3);
! clear_tv(listtv);
! return;
}
! if (typetv->v_type != VAR_NUMBER)
{
! /* TODO: give error */
! clear_tv(listtv);
! return;
}
+ seq_nr = typetv->vval.v_number;
+ }
+ else
+ {
+ /* For a raw channel we don't know where the message ends, just get
+ * everything. */
+ msg = channel_get_all(idx);
+ argv[1].v_type = VAR_STRING;
+ argv[1].vval.v_string = msg;
+ }
+
+ if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
+ {
+ /* TODO: check the sequence number */
+ /* invoke the one-time callback */
+ invoke_callback(idx, channels[idx].ch_req_callback, argv);
+ channels[idx].ch_req_callback = NULL;
+ }
+ else if (channels[idx].ch_callback != NULL)
+ {
+ /* invoke the channel callback */
+ invoke_callback(idx, channels[idx].ch_callback, argv);
}
+ /* else: drop the message TODO: give error */
+ if (listtv != NULL)
+ clear_tv(listtv);
vim_free(msg);
}
***************
*** 661,677 ****
void
channel_close(int idx)
{
! channel_T *channel = &channels[idx];
if (channel->ch_fd >= 0)
{
sock_close(channel->ch_fd);
channel->ch_fd = -1;
#ifdef FEAT_GUI
channel_gui_unregister(idx);
#endif
vim_free(channel->ch_callback);
channel->ch_callback = NULL;
}
}
--- 803,831 ----
void
channel_close(int idx)
{
! channel_T *channel = &channels[idx];
! jsonq_T *jhead;
if (channel->ch_fd >= 0)
{
sock_close(channel->ch_fd);
channel->ch_fd = -1;
+ channel->ch_close_cb = NULL;
#ifdef FEAT_GUI
channel_gui_unregister(idx);
#endif
vim_free(channel->ch_callback);
channel->ch_callback = NULL;
+
+ while (channel_peek(idx) != NULL)
+ vim_free(channel_get(idx));
+
+ jhead = &channel->ch_json_head;
+ while (jhead->next != jhead)
+ {
+ clear_tv(jhead->next->value);
+ remove_json_node(jhead->next);
+ }
}
}
***************
*** 682,691 ****
int
channel_save(int idx, char_u *buf, int len)
{
! queue_T *node;
! queue_T *head = &channels[idx].ch_head;
! node = (queue_T *)alloc(sizeof(queue_T));
if (node == NULL)
return FAIL; /* out of memory */
node->buffer = alloc(len + 1);
--- 836,845 ----
int
channel_save(int idx, char_u *buf, int len)
{
! readq_T *node;
! readq_T *head = &channels[idx].ch_head;
! node = (readq_T *)alloc(sizeof(readq_T));
if (node == NULL)
return FAIL; /* out of memory */
node->buffer = alloc(len + 1);
***************
*** 697,708 ****
mch_memmove(node->buffer, buf, (size_t)len);
node->buffer[len] = NUL;
- if (head->next == NULL) /* initialize circular queue */
- {
- head->next = head;
- head->prev = head;
- }
-
/* insert node at tail of queue */
node->next = head;
node->prev = head->prev;
--- 851,856 ----
***************
*** 726,732 ****
char_u *
channel_peek(int idx)
{
! queue_T *head = &channels[idx].ch_head;
if (head->next == head || head->next == NULL)
return NULL;
--- 874,880 ----
char_u *
channel_peek(int idx)
{
! readq_T *head = &channels[idx].ch_head;
if (head->next == head || head->next == NULL)
return NULL;
***************
*** 734,801 ****
}
/*
- * Return the first buffer from the channel and remove it.
- * The caller must free it.
- * Returns NULL if there is nothing.
- */
- char_u *
- channel_get(int idx)
- {
- queue_T *head = &channels[idx].ch_head;
- queue_T *node;
- char_u *p;
-
- if (head->next == head || head->next == NULL)
- return NULL;
- node = head->next;
- /* dispose of the node but keep the buffer */
- p = node->buffer;
- head->next = node->next;
- node->next->prev = node->prev;
- vim_free(node);
- return p;
- }
-
- /*
- * Collapses the first and second buffer in the channel "idx".
- * Returns FAIL if that is not possible.
- */
- int
- channel_collapse(int idx)
- {
- queue_T *head = &channels[idx].ch_head;
- queue_T *node = head->next;
- char_u *p;
-
- if (node == head || node == NULL || node->next == head)
- return FAIL;
-
- p = alloc((unsigned)(STRLEN(node->buffer)
- + STRLEN(node->next->buffer) + 1));
- if (p == NULL)
- return FAIL; /* out of memory */
- STRCPY(p, node->buffer);
- STRCAT(p, node->next->buffer);
- vim_free(node->next->buffer);
- node->next->buffer = p;
-
- /* dispose of the node and buffer */
- head->next = node->next;
- node->next->prev = node->prev;
- vim_free(node->buffer);
- vim_free(node);
- return OK;
- }
-
- /*
* Clear the read buffer on channel "idx".
*/
void
channel_clear(int idx)
{
! queue_T *head = &channels[idx].ch_head;
! queue_T *node = head->next;
! queue_T *next;
while (node != NULL && node != head)
{
--- 882,895 ----
}
/*
* Clear the read buffer on channel "idx".
*/
void
channel_clear(int idx)
{
! readq_T *head = &channels[idx].ch_head;
! readq_T *node = head->next;
! readq_T *next;
while (node != NULL && node != head)
{
***************
*** 947,954 ****
}
/*
! * Read from channel "idx". Blocks until there is something to read or the
! * timeout expires.
* Returns what was read in allocated memory.
* Returns NULL in case of error or timeout.
*/
--- 1041,1048 ----
}
/*
! * Read from raw channel "idx". Blocks until there is something to read or
! * the timeout expires.
* Returns what was read in allocated memory.
* Returns NULL in case of error or timeout.
*/
***************
*** 964,975 ****
channel_read(idx);
}
! /* Concatenate everything into one buffer.
! * TODO: avoid multiple allocations. */
! while (channel_collapse(idx) == OK)
! ;
! return channel_get(idx);
}
# if defined(WIN32) || defined(PROTO)
--- 1058,1089 ----
channel_read(idx);
}
! return channel_get_all(idx);
! }
! /*
! * Read one JSON message from channel "ch_idx" with ID "id" and store the
! * result in "rettv".
! * Blocks until the message is received.
! */
! int
! channel_read_json_block(int ch_idx, int id, typval_T **rettv)
! {
! for (;;)
! {
! channel_read_json(ch_idx);
!
! /* search for messsage "id" */
! if (channel_get_json(ch_idx, id, rettv) == OK)
! return OK;
!
! /* Wait for up to 2 seconds.
! * TODO: use timeout set on the channel. */
! if (channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL)
! break;
! channel_read(ch_idx);
! }
! return FAIL;
}
# if defined(WIN32) || defined(PROTO)
*** ../vim-7.4.1230/src/json.c 2016-01-31 20:24:09.966066885 +0100
--- src/json.c 2016-01-31 22:49:58.975253082 +0100
***************
*** 549,562 ****
/*
* Decode the JSON from "reader" and store the result in "res".
*/
! void
json_decode(js_read_T *reader, typval_T *res)
{
json_skip_white(reader);
json_decode_item(reader, res);
json_skip_white(reader);
if (reader->js_buf[reader->js_used] != NUL)
! EMSG(_(e_invarg));
}
#endif
--- 549,564 ----
/*
* Decode the JSON from "reader" and store the result in "res".
+ * Return OK or FAIL;
*/
! int
json_decode(js_read_T *reader, typval_T *res)
{
json_skip_white(reader);
json_decode_item(reader, res);
json_skip_white(reader);
if (reader->js_buf[reader->js_used] != NUL)
! return FAIL;
! return OK;
}
#endif
*** ../vim-7.4.1230/src/proto/eval.pro 2016-01-28 22:36:15.052065044 +0100
--- src/proto/eval.pro 2016-01-31 23:02:33.143409845 +0100
***************
*** 101,106 ****
--- 101,107 ----
char_u *v_exception(char_u *oldval);
char_u *v_throwpoint(char_u *oldval);
char_u *set_cmdarg(exarg_T *eap, char_u *oldarg);
+ typval_T *alloc_tv(void);
void free_tv(typval_T *varp);
void clear_tv(typval_T *varp);
long get_tv_number_chk(typval_T *varp, int *denote);
*** ../vim-7.4.1230/src/proto/channel.pro 2016-01-31 20:24:09.970066843
+0100
--- src/proto/channel.pro 2016-01-31 22:55:53.875563126 +0100
***************
*** 4,10 ****
void channel_set_json_mode(int idx, int json_mode);
void channel_set_callback(int idx, char_u *callback);
void channel_set_req_callback(int idx, char_u *callback);
- int channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2, typval_T
*tv3);
int channel_is_open(int idx);
void channel_close(int idx);
int channel_save(int idx, char_u *buf, int len);
--- 4,9 ----
***************
*** 15,20 ****
--- 14,21 ----
int channel_get_id(void);
void channel_read(int idx);
char_u *channel_read_block(int idx);
+ int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
+ void channel_read_json(int ch_idx);
int channel_socket2idx(sock_T fd);
int channel_send(int idx, char_u *buf, char *fun);
int channel_poll_setup(int nfd_in, void *fds_in);
*** ../vim-7.4.1230/src/proto/json.pro 2016-01-31 20:24:09.970066843 +0100
--- src/proto/json.pro 2016-01-31 22:56:25.115238152 +0100
***************
*** 1,5 ****
/* json.c */
char_u *json_encode(typval_T *val);
char_u *json_encode_nr_expr(int nr, typval_T *val);
! void json_decode(js_read_T *reader, typval_T *res);
/* vim: set ft=c : */
--- 1,5 ----
/* json.c */
char_u *json_encode(typval_T *val);
char_u *json_encode_nr_expr(int nr, typval_T *val);
! int json_decode(js_read_T *reader, typval_T *res);
/* vim: set ft=c : */
*** ../vim-7.4.1230/src/structs.h 2016-01-29 23:20:35.313308119 +0100
--- src/structs.h 2016-01-31 21:19:43.711391593 +0100
***************
*** 2693,2697 ****
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
int js_used; /* bytes used from js_buf */
int js_eof; /* when TRUE js_buf is all there is */
! FILE *js_fd; /* file descriptor to read more from */
} js_read_T;
--- 2693,2698 ----
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
int js_used; /* bytes used from js_buf */
int js_eof; /* when TRUE js_buf is all there is */
! int (*js_fill)(void *); /* function to fill the buffer */
! void *js_cookie; /* passed to js_fill */
} js_read_T;
*** ../vim-7.4.1230/src/version.c 2016-02-01 21:32:51.622375175 +0100
--- src/version.c 2016-02-01 21:35:02.833003291 +0100
***************
*** 744,745 ****
--- 744,747 ----
{ /* Add new patch number below this line */
+ /**/
+ 1231,
/**/
--
>From "know your smileys":
|-( Contact lenses, but has lost them
/// Bram Moolenaar -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.