mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 7.4.1231
Problem: JSON messages are not parsed properly. Solution: Queue received messages.
This commit is contained in:
454
src/channel.c
454
src/channel.c
@@ -74,12 +74,20 @@ struct readqueue
|
|||||||
struct readqueue *next;
|
struct readqueue *next;
|
||||||
struct readqueue *prev;
|
struct readqueue *prev;
|
||||||
};
|
};
|
||||||
typedef struct readqueue queue_T;
|
typedef struct readqueue readq_T;
|
||||||
|
|
||||||
|
struct jsonqueue
|
||||||
|
{
|
||||||
|
typval_T *value;
|
||||||
|
struct jsonqueue *next;
|
||||||
|
struct jsonqueue *prev;
|
||||||
|
};
|
||||||
|
typedef struct jsonqueue jsonq_T;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
sock_T ch_fd; /* the socket, -1 for a closed channel */
|
sock_T ch_fd; /* the socket, -1 for a closed channel */
|
||||||
int ch_idx; /* used by channel_poll_setup() */
|
int ch_idx; /* used by channel_poll_setup() */
|
||||||
queue_T ch_head; /* dummy node, header for circular queue */
|
readq_T ch_head; /* dummy node, header for circular queue */
|
||||||
|
|
||||||
int ch_error; /* When TRUE an error was reported. Avoids giving
|
int ch_error; /* When TRUE an error was reported. Avoids giving
|
||||||
* pages full of error messages when the other side
|
* pages full of error messages when the other side
|
||||||
@@ -100,7 +108,8 @@ typedef struct {
|
|||||||
char_u *ch_callback; /* function to call when a msg is not handled */
|
char_u *ch_callback; /* function to call when a msg is not handled */
|
||||||
char_u *ch_req_callback; /* function to call for current request */
|
char_u *ch_req_callback; /* function to call for current request */
|
||||||
|
|
||||||
int ch_json_mode;
|
int ch_json_mode; /* TRUE for a json channel */
|
||||||
|
jsonq_T ch_json_head; /* dummy node, header for circular queue */
|
||||||
} channel_T;
|
} channel_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -125,6 +134,7 @@ add_channel(void)
|
|||||||
{
|
{
|
||||||
int idx;
|
int idx;
|
||||||
channel_T *new_channels;
|
channel_T *new_channels;
|
||||||
|
channel_T *ch;
|
||||||
|
|
||||||
if (channels != NULL)
|
if (channels != NULL)
|
||||||
for (idx = 0; idx < channel_count; ++idx)
|
for (idx = 0; idx < channel_count; ++idx)
|
||||||
@@ -139,18 +149,24 @@ add_channel(void)
|
|||||||
if (channels != NULL)
|
if (channels != NULL)
|
||||||
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
|
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
|
||||||
channels = new_channels;
|
channels = new_channels;
|
||||||
(void)vim_memset(&channels[channel_count], 0, sizeof(channel_T));
|
ch = &channels[channel_count];
|
||||||
|
(void)vim_memset(ch, 0, sizeof(channel_T));
|
||||||
|
|
||||||
channels[channel_count].ch_fd = (sock_T)-1;
|
ch->ch_fd = (sock_T)-1;
|
||||||
#ifdef FEAT_GUI_X11
|
#ifdef FEAT_GUI_X11
|
||||||
channels[channel_count].ch_inputHandler = (XtInputId)NULL;
|
ch->ch_inputHandler = (XtInputId)NULL;
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEAT_GUI_GTK
|
#ifdef FEAT_GUI_GTK
|
||||||
channels[channel_count].ch_inputHandler = 0;
|
ch->ch_inputHandler = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEAT_GUI_W32
|
#ifdef FEAT_GUI_W32
|
||||||
channels[channel_count].ch_inputHandler = -1;
|
ch->ch_inputHandler = -1;
|
||||||
#endif
|
#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++;
|
return channel_count++;
|
||||||
}
|
}
|
||||||
@@ -412,62 +428,12 @@ channel_set_callback(int idx, char_u *callback)
|
|||||||
void
|
void
|
||||||
channel_set_req_callback(int idx, char_u *callback)
|
channel_set_req_callback(int idx, char_u *callback)
|
||||||
{
|
{
|
||||||
|
/* TODO: make a list of callbacks */
|
||||||
vim_free(channels[idx].ch_req_callback);
|
vim_free(channels[idx].ch_req_callback);
|
||||||
channels[idx].ch_req_callback = callback == NULL
|
channels[idx].ch_req_callback = callback == NULL
|
||||||
? NULL : vim_strsave(callback);
|
? 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".
|
* Invoke the "callback" on channel "idx".
|
||||||
*/
|
*/
|
||||||
@@ -489,6 +455,163 @@ invoke_callback(int idx, char_u *callback, typval_T *argv)
|
|||||||
out_flush();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute a command received over channel "idx".
|
* Execute a command received over channel "idx".
|
||||||
* "cmd" is the command string, "arg2" the second argument.
|
* "cmd" is the command string, "arg2" the second argument.
|
||||||
@@ -524,7 +647,7 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
|
|||||||
{
|
{
|
||||||
exarg_T ea;
|
exarg_T ea;
|
||||||
|
|
||||||
ea.forceit = *arg != NUL;
|
ea.forceit = arg != NULL && *arg != NUL;
|
||||||
ex_redraw(&ea);
|
ex_redraw(&ea);
|
||||||
showruler(FALSE);
|
showruler(FALSE);
|
||||||
setcursor();
|
setcursor();
|
||||||
@@ -577,49 +700,74 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
|
|||||||
static void
|
static void
|
||||||
may_invoke_callback(int idx)
|
may_invoke_callback(int idx)
|
||||||
{
|
{
|
||||||
char_u *msg;
|
char_u *msg = NULL;
|
||||||
typval_T typetv;
|
typval_T *listtv = NULL;
|
||||||
|
list_T *list;
|
||||||
|
typval_T *typetv;
|
||||||
typval_T argv[3];
|
typval_T argv[3];
|
||||||
typval_T arg3;
|
|
||||||
char_u *cmd = NULL;
|
|
||||||
int seq_nr = -1;
|
int seq_nr = -1;
|
||||||
int ret = OK;
|
int json_mode = channels[idx].ch_json_mode;
|
||||||
|
|
||||||
if (channel_peek(idx) == NULL)
|
if (channel_peek(idx) == NULL)
|
||||||
return;
|
return;
|
||||||
|
if (channels[idx].ch_close_cb != NULL)
|
||||||
|
/* this channel is handled elsewhere (netbeans) */
|
||||||
|
return;
|
||||||
|
|
||||||
/* Concatenate everything into one buffer.
|
if (json_mode)
|
||||||
* 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);
|
/* Get any json message. Return if there isn't one. */
|
||||||
if (ret == OK)
|
channel_read_json(idx);
|
||||||
|
if (channel_get_json(idx, -1, &listtv) == FAIL)
|
||||||
|
return;
|
||||||
|
if (listtv->v_type != VAR_LIST)
|
||||||
{
|
{
|
||||||
/* TODO: error if arg3 is set when it shouldn't? */
|
/* TODO: give error */
|
||||||
if (typetv.v_type == VAR_STRING)
|
clear_tv(listtv);
|
||||||
cmd = typetv.vval.v_string;
|
return;
|
||||||
else if (typetv.v_type == VAR_NUMBER)
|
|
||||||
seq_nr = typetv.vval.v_number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
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].v_type = VAR_STRING;
|
||||||
argv[1].vval.v_string = msg;
|
argv[1].vval.v_string = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == OK)
|
if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
|
||||||
{
|
|
||||||
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 */
|
/* TODO: check the sequence number */
|
||||||
/* invoke the one-time callback */
|
/* invoke the one-time callback */
|
||||||
@@ -631,16 +779,10 @@ may_invoke_callback(int idx)
|
|||||||
/* invoke the channel callback */
|
/* invoke the channel callback */
|
||||||
invoke_callback(idx, channels[idx].ch_callback, argv);
|
invoke_callback(idx, channels[idx].ch_callback, argv);
|
||||||
}
|
}
|
||||||
/* else: drop the message */
|
/* else: drop the message TODO: give error */
|
||||||
|
|
||||||
if (channels[idx].ch_json_mode)
|
|
||||||
{
|
|
||||||
clear_tv(&typetv);
|
|
||||||
clear_tv(&argv[1]);
|
|
||||||
clear_tv(&arg3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (listtv != NULL)
|
||||||
|
clear_tv(listtv);
|
||||||
vim_free(msg);
|
vim_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,16 +804,28 @@ channel_is_open(int idx)
|
|||||||
channel_close(int idx)
|
channel_close(int idx)
|
||||||
{
|
{
|
||||||
channel_T *channel = &channels[idx];
|
channel_T *channel = &channels[idx];
|
||||||
|
jsonq_T *jhead;
|
||||||
|
|
||||||
if (channel->ch_fd >= 0)
|
if (channel->ch_fd >= 0)
|
||||||
{
|
{
|
||||||
sock_close(channel->ch_fd);
|
sock_close(channel->ch_fd);
|
||||||
channel->ch_fd = -1;
|
channel->ch_fd = -1;
|
||||||
|
channel->ch_close_cb = NULL;
|
||||||
#ifdef FEAT_GUI
|
#ifdef FEAT_GUI
|
||||||
channel_gui_unregister(idx);
|
channel_gui_unregister(idx);
|
||||||
#endif
|
#endif
|
||||||
vim_free(channel->ch_callback);
|
vim_free(channel->ch_callback);
|
||||||
channel->ch_callback = NULL;
|
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,10 +836,10 @@ channel_close(int idx)
|
|||||||
int
|
int
|
||||||
channel_save(int idx, char_u *buf, int len)
|
channel_save(int idx, char_u *buf, int len)
|
||||||
{
|
{
|
||||||
queue_T *node;
|
readq_T *node;
|
||||||
queue_T *head = &channels[idx].ch_head;
|
readq_T *head = &channels[idx].ch_head;
|
||||||
|
|
||||||
node = (queue_T *)alloc(sizeof(queue_T));
|
node = (readq_T *)alloc(sizeof(readq_T));
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return FAIL; /* out of memory */
|
return FAIL; /* out of memory */
|
||||||
node->buffer = alloc(len + 1);
|
node->buffer = alloc(len + 1);
|
||||||
@@ -697,12 +851,6 @@ channel_save(int idx, char_u *buf, int len)
|
|||||||
mch_memmove(node->buffer, buf, (size_t)len);
|
mch_memmove(node->buffer, buf, (size_t)len);
|
||||||
node->buffer[len] = NUL;
|
node->buffer[len] = NUL;
|
||||||
|
|
||||||
if (head->next == NULL) /* initialize circular queue */
|
|
||||||
{
|
|
||||||
head->next = head;
|
|
||||||
head->prev = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* insert node at tail of queue */
|
/* insert node at tail of queue */
|
||||||
node->next = head;
|
node->next = head;
|
||||||
node->prev = head->prev;
|
node->prev = head->prev;
|
||||||
@@ -726,76 +874,22 @@ channel_save(int idx, char_u *buf, int len)
|
|||||||
char_u *
|
char_u *
|
||||||
channel_peek(int idx)
|
channel_peek(int idx)
|
||||||
{
|
{
|
||||||
queue_T *head = &channels[idx].ch_head;
|
readq_T *head = &channels[idx].ch_head;
|
||||||
|
|
||||||
if (head->next == head || head->next == NULL)
|
if (head->next == head || head->next == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return head->next->buffer;
|
return head->next->buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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".
|
* Clear the read buffer on channel "idx".
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
channel_clear(int idx)
|
channel_clear(int idx)
|
||||||
{
|
{
|
||||||
queue_T *head = &channels[idx].ch_head;
|
readq_T *head = &channels[idx].ch_head;
|
||||||
queue_T *node = head->next;
|
readq_T *node = head->next;
|
||||||
queue_T *next;
|
readq_T *next;
|
||||||
|
|
||||||
while (node != NULL && node != head)
|
while (node != NULL && node != head)
|
||||||
{
|
{
|
||||||
@@ -947,8 +1041,8 @@ channel_read(int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read from channel "idx". Blocks until there is something to read or the
|
* Read from raw channel "idx". Blocks until there is something to read or
|
||||||
* timeout expires.
|
* the timeout expires.
|
||||||
* Returns what was read in allocated memory.
|
* Returns what was read in allocated memory.
|
||||||
* Returns NULL in case of error or timeout.
|
* Returns NULL in case of error or timeout.
|
||||||
*/
|
*/
|
||||||
@@ -964,12 +1058,32 @@ channel_read_block(int idx)
|
|||||||
channel_read(idx);
|
channel_read(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Concatenate everything into one buffer.
|
return channel_get_all(idx);
|
||||||
* TODO: avoid multiple allocations. */
|
}
|
||||||
while (channel_collapse(idx) == OK)
|
|
||||||
;
|
|
||||||
|
|
||||||
return channel_get(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)
|
# if defined(WIN32) || defined(PROTO)
|
||||||
|
@@ -549,14 +549,16 @@ json_decode_item(js_read_T *reader, typval_T *res)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Decode the JSON from "reader" and store the result in "res".
|
* Decode the JSON from "reader" and store the result in "res".
|
||||||
|
* Return OK or FAIL;
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
json_decode(js_read_T *reader, typval_T *res)
|
json_decode(js_read_T *reader, typval_T *res)
|
||||||
{
|
{
|
||||||
json_skip_white(reader);
|
json_skip_white(reader);
|
||||||
json_decode_item(reader, res);
|
json_decode_item(reader, res);
|
||||||
json_skip_white(reader);
|
json_skip_white(reader);
|
||||||
if (reader->js_buf[reader->js_used] != NUL)
|
if (reader->js_buf[reader->js_used] != NUL)
|
||||||
EMSG(_(e_invarg));
|
return FAIL;
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,7 +4,6 @@ int channel_open(char *hostname, int port_in, void (*close_cb)(void));
|
|||||||
void channel_set_json_mode(int idx, int json_mode);
|
void channel_set_json_mode(int idx, int json_mode);
|
||||||
void channel_set_callback(int idx, char_u *callback);
|
void channel_set_callback(int idx, char_u *callback);
|
||||||
void channel_set_req_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);
|
int channel_is_open(int idx);
|
||||||
void channel_close(int idx);
|
void channel_close(int idx);
|
||||||
int channel_save(int idx, char_u *buf, int len);
|
int channel_save(int idx, char_u *buf, int len);
|
||||||
@@ -15,6 +14,8 @@ void channel_clear(int idx);
|
|||||||
int channel_get_id(void);
|
int channel_get_id(void);
|
||||||
void channel_read(int idx);
|
void channel_read(int idx);
|
||||||
char_u *channel_read_block(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_socket2idx(sock_T fd);
|
||||||
int channel_send(int idx, char_u *buf, char *fun);
|
int channel_send(int idx, char_u *buf, char *fun);
|
||||||
int channel_poll_setup(int nfd_in, void *fds_in);
|
int channel_poll_setup(int nfd_in, void *fds_in);
|
||||||
|
@@ -101,6 +101,7 @@ void set_reg_var(int c);
|
|||||||
char_u *v_exception(char_u *oldval);
|
char_u *v_exception(char_u *oldval);
|
||||||
char_u *v_throwpoint(char_u *oldval);
|
char_u *v_throwpoint(char_u *oldval);
|
||||||
char_u *set_cmdarg(exarg_T *eap, char_u *oldarg);
|
char_u *set_cmdarg(exarg_T *eap, char_u *oldarg);
|
||||||
|
typval_T *alloc_tv(void);
|
||||||
void free_tv(typval_T *varp);
|
void free_tv(typval_T *varp);
|
||||||
void clear_tv(typval_T *varp);
|
void clear_tv(typval_T *varp);
|
||||||
long get_tv_number_chk(typval_T *varp, int *denote);
|
long get_tv_number_chk(typval_T *varp, int *denote);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* json.c */
|
/* json.c */
|
||||||
char_u *json_encode(typval_T *val);
|
char_u *json_encode(typval_T *val);
|
||||||
char_u *json_encode_nr_expr(int nr, typval_T *val);
|
char_u *json_encode_nr_expr(int nr, typval_T *val);
|
||||||
void json_decode(js_read_T *reader, typval_T *res);
|
int json_decode(js_read_T *reader, typval_T *res);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -2693,5 +2693,6 @@ typedef struct
|
|||||||
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
|
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
|
||||||
int js_used; /* bytes used from js_buf */
|
int js_used; /* bytes used from js_buf */
|
||||||
int js_eof; /* when TRUE js_buf is all there is */
|
int js_eof; /* when TRUE js_buf is all there is */
|
||||||
FILE *js_fd; /* file descriptor to read more from */
|
int (*js_fill)(void *); /* function to fill the buffer */
|
||||||
|
void *js_cookie; /* passed to js_fill */
|
||||||
} js_read_T;
|
} js_read_T;
|
||||||
|
@@ -742,6 +742,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 */
|
||||||
|
/**/
|
||||||
|
1231,
|
||||||
/**/
|
/**/
|
||||||
1230,
|
1230,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user