# HG changeset patch # User Christian Brabandt # Date 1455402605 -3600 # Node ID c6443e78cf2decd7cdf3d81a0e91a5dc30fc88b8 # Parent 306f639c99a834842964828e01981e3347db5d9a commit https://github.com/vim/vim/commit/7707344ddec9069b495b2a5ed41f2104466fc88b Author: Bram Moolenaar Date: Sat Feb 13 23:23:53 2016 +0100 patch 7.4.1315 Problem: Using a channel handle does not allow for freeing it when unused. Solution: Add the Channel variable type. diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -54,12 +54,6 @@ extern HWND s_hwnd; /* Gvim's Window handle */ #endif -/* - * Information about all channels. - * There can be gaps for closed channels, they will be reused later. - */ -static channel_T *channels = NULL; -static int channel_count = 0; /* Log file opened with ch_logfile(). */ static FILE *log_fd = NULL; @@ -75,89 +69,89 @@ ch_logfile(FILE *file) } static void -ch_log_lead(char *what, int ch_idx) +ch_log_lead(char *what, channel_T *ch) { if (log_fd != NULL) { - if (ch_idx >= 0) - fprintf(log_fd, "%son %d: ", what, ch_idx); + if (ch != NULL) + fprintf(log_fd, "%son %d: ", what, ch->ch_id); else fprintf(log_fd, "%s: ", what); } } static void -ch_log(int ch_idx, char *msg) +ch_log(channel_T *ch, char *msg) { if (log_fd != NULL) { - ch_log_lead("", ch_idx); + ch_log_lead("", ch); fputs(msg, log_fd); fflush(log_fd); } } static void -ch_logn(int ch_idx, char *msg, int nr) +ch_logn(channel_T *ch, char *msg, int nr) { if (log_fd != NULL) { - ch_log_lead("", ch_idx); + ch_log_lead("", ch); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void -ch_logs(int ch_idx, char *msg, char *name) +ch_logs(channel_T *ch, char *msg, char *name) { if (log_fd != NULL) { - ch_log_lead("", ch_idx); + ch_log_lead("", ch); fprintf(log_fd, msg, name); fflush(log_fd); } } static void -ch_logsn(int ch_idx, char *msg, char *name, int nr) +ch_logsn(channel_T *ch, char *msg, char *name, int nr) { if (log_fd != NULL) { - ch_log_lead("", ch_idx); + ch_log_lead("", ch); fprintf(log_fd, msg, name, nr); fflush(log_fd); } } static void -ch_error(int ch_idx, char *msg) +ch_error(channel_T *ch, char *msg) { if (log_fd != NULL) { - ch_log_lead("ERR ", ch_idx); + ch_log_lead("ERR ", ch); fputs(msg, log_fd); fflush(log_fd); } } static void -ch_errorn(int ch_idx, char *msg, int nr) +ch_errorn(channel_T *ch, char *msg, int nr) { if (log_fd != NULL) { - ch_log_lead("ERR ", ch_idx); + ch_log_lead("ERR ", ch); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void -ch_errors(int ch_idx, char *msg, char *arg) +ch_errors(channel_T *ch, char *msg, char *arg) { if (log_fd != NULL) { - ch_log_lead("ERR ", ch_idx); + ch_log_lead("ERR ", ch); fprintf(log_fd, msg, arg); fflush(log_fd); } @@ -205,76 +199,101 @@ strerror_win32(int eno) } #endif - static void -init_channel(int ch_idx) -{ - channel_T *ch; +/* + * The list of all allocated channels. + */ +static channel_T *first_channel = NULL; +static int next_ch_id = 0; - ch = &channels[ch_idx]; - (void)vim_memset(ch, 0, sizeof(channel_T)); +/* + * Allocate a new channel. The refcount is set to 1. + * The channel isn't actually used until it is opened. + * Returns NULL if out of memory. + */ + channel_T * +add_channel(void) +{ + channel_T *channel = (channel_T *)alloc_clear((int)sizeof(channel_T)); - ch->ch_sock = (sock_T)-1; + if (channel == NULL) + return NULL; + + channel->ch_id = next_ch_id++; + ch_log(channel, "Opening channel\n"); + + channel->ch_sock = (sock_T)-1; #ifdef CHANNEL_PIPES - ch->ch_in = -1; - ch->ch_out = -1; - ch->ch_err = -1; + channel->ch_in = -1; + channel->ch_out = -1; + channel->ch_err = -1; #endif #ifdef FEAT_GUI_X11 - ch->ch_inputHandler = (XtInputId)NULL; + channel->ch_inputHandler = (XtInputId)NULL; #endif #ifdef FEAT_GUI_GTK - ch->ch_inputHandler = 0; + channel->ch_inputHandler = 0; #endif #ifdef FEAT_GUI_W32 - ch->ch_inputHandler = -1; + channel->ch_inputHandler = -1; #endif - /* initialize circular queues */ - ch->ch_head.next = &ch->ch_head; - ch->ch_head.prev = &ch->ch_head; - ch->ch_cb_head.next = &ch->ch_cb_head; - ch->ch_cb_head.prev = &ch->ch_cb_head; - ch->ch_json_head.next = &ch->ch_json_head; - ch->ch_json_head.prev = &ch->ch_json_head; + + channel->ch_timeout = 2000; - ch->ch_timeout = 2000; + if (first_channel != NULL) + { + first_channel->ch_prev = channel; + channel->ch_next = first_channel; + } + first_channel = channel; + + channel->ch_refcount = 1; + return channel; } /* - * Add a new channel slot, return the index. - * The channel isn't actually used into ch_sock is set >= 0; - * Returns -1 if all channels are in use. + * Close a channel and free all its resources. */ - int -add_channel(void) + void +channel_free(channel_T *channel) { - int ch_idx; - - if (channels != NULL) - { - for (ch_idx = 0; ch_idx < channel_count; ++ch_idx) - if (!channel_is_open(ch_idx)) - { - /* re-use a closed channel slot */ - init_channel(ch_idx); - ch_log(ch_idx, "Opening channel (used before)\n"); - return ch_idx; - } - if (channel_count == MAX_OPEN_CHANNELS) - return -1; - } + channel_close(channel); + if (channel->ch_next != NULL) + channel->ch_next->ch_prev = channel->ch_prev; + if (channel->ch_prev == NULL) + first_channel = channel->ch_next; else - { - channels = (channel_T *)alloc((int)sizeof(channel_T) - * MAX_OPEN_CHANNELS); - if (channels == NULL) - return -1; - } - init_channel(channel_count); - ch_log(channel_count, "Opening new channel\n"); - return channel_count++; + channel->ch_prev->ch_next = channel->ch_next; + vim_free(channel); } +#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) + static channel_T * +channel_from_id(int id) +{ + channel_T *channel; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + if (channel->ch_id == id) + return channel; + return NULL; +} +#endif + #if defined(FEAT_GUI) || defined(PROTO) + +#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) + static void +channel_read_netbeans(int id) +{ + channel_T *channel = channel_from_id(id); + + if (channel == NULL) + ch_errorn(NULL, "Channel %d not found", id); + else + channel_read(channel, FALSE, "messageFromNetbeans"); +} +#endif + /* * Read a command from netbeans. */ @@ -284,7 +303,7 @@ messageFromNetbeans(XtPointer clientData int *unused1 UNUSED, XtInputId *unused2 UNUSED) { - channel_read((int)(long)clientData, FALSE, "messageFromNetbeans"); + channel_read_netbeans((int)(long)clientData); } #endif @@ -294,15 +313,13 @@ messageFromNetbeans(gpointer clientData, gint unused1 UNUSED, GdkInputCondition unused2 UNUSED) { - channel_read((int)(long)clientData, FALSE, "messageFromNetbeans"); + channel_read_netbeans((int)(long)clientData); } #endif static void -channel_gui_register(int ch_idx) +channel_gui_register(channel_T *channel) { - channel_T *channel = &channels[ch_idx]; - if (!CH_HAS_GUI) return; @@ -315,7 +332,7 @@ channel_gui_register(int ch_idx) channel->ch_inputHandler = XtAppAddInput((XtAppContext)app_context, channel->ch_sock, (XtPointer)(XtInputReadMask + XtInputExceptMask), - messageFromNetbeans, (XtPointer)(long)ch_idx); + messageFromNetbeans, (XtPointer)(long)channel->ch_id); # else # ifdef FEAT_GUI_GTK /* @@ -326,7 +343,7 @@ channel_gui_register(int ch_idx) channel->ch_inputHandler = gdk_input_add((gint)channel->ch_sock, (GdkInputCondition) ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), - messageFromNetbeans, (gpointer)(long)ch_idx); + messageFromNetbeans, (gpointer)(long)channel->ch_id); # else # ifdef FEAT_GUI_W32 /* @@ -348,19 +365,17 @@ channel_gui_register(int ch_idx) void channel_gui_register_all(void) { - int i; + channel_T *channel; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) /* TODO: pipes */ - if (channels[i].ch_sock >= 0) - channel_gui_register(i); + if (channel->ch_sock >= 0) + channel_gui_register(channel); } static void -channel_gui_unregister(int ch_idx) +channel_gui_unregister(channel_T *channel) { - channel_T *channel = &channels[ch_idx]; - /* TODO: pipes */ # ifdef FEAT_GUI_X11 if (channel->ch_inputHandler != (XtInputId)NULL) @@ -391,10 +406,10 @@ channel_gui_unregister(int ch_idx) /* * Open a socket channel to "hostname":"port". - * Returns the channel number for success. - * Returns a negative number for failure. + * Returns the channel for success. + * Returns NULL for failure. */ - int + channel_T * channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)) { int sd; @@ -406,26 +421,26 @@ channel_open(char *hostname, int port_in #else int port = port_in; #endif - int ch_idx; + channel_T *channel; int ret; #ifdef WIN32 channel_init_winsock(); #endif - ch_idx = add_channel(); - if (ch_idx < 0) + channel = add_channel(); + if (channel == NULL) { - ch_error(-1, "All channels are in use.\n"); + ch_error(NULL, "Cannot allocate channel.\n"); EMSG(_("E897: All channels are in use")); - return -1; + return NULL; } if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { - ch_error(-1, "in socket() in channel_open().\n"); + ch_error(NULL, "in socket() in channel_open().\n"); PERROR("E898: socket() in channel_open()"); - return -1; + return NULL; } /* Get the server internet address and put into addr structure */ @@ -435,10 +450,10 @@ channel_open(char *hostname, int port_in server.sin_port = htons(port); if ((host = gethostbyname(hostname)) == NULL) { - ch_error(-1, "in gethostbyname() in channel_open()\n"); + ch_error(NULL, "in gethostbyname() in channel_open()\n"); PERROR("E901: gethostbyname() in channel_open()"); sock_close(sd); - return -1; + return NULL; } memcpy((char *)&server.sin_addr, host->h_addr, host->h_length); @@ -454,15 +469,15 @@ channel_open(char *hostname, int port_in ) { SOCK_ERRNO; - ch_errorn(-1, "channel_open: Connect failed with errno %d\n", + ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); sock_close(sd); - return -1; + return NULL; } } /* Try connecting to the server. */ - ch_logsn(-1, "Connecting to %s port %d", hostname, port); + ch_logsn(NULL, "Connecting to %s port %d", hostname, port); ret = connect(sd, (struct sockaddr *)&server, sizeof(server)); SOCK_ERRNO; if (ret < 0) @@ -473,11 +488,11 @@ channel_open(char *hostname, int port_in #endif ) { - ch_errorn(-1, "channel_open: Connect failed with errno %d\n", + ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); - return -1; + return NULL; } } @@ -494,17 +509,17 @@ channel_open(char *hostname, int port_in if (ret < 0) { SOCK_ERRNO; - ch_errorn(-1, "channel_open: Connect failed with errno %d\n", + ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); - return -1; + return NULL; } if (!FD_ISSET(sd, &wfds)) { /* don't give an error, we just timed out. */ sock_close(sd); - return -1; + return NULL; } } @@ -525,9 +540,9 @@ channel_open(char *hostname, int port_in if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { SOCK_ERRNO; - ch_log(-1, "socket() retry in channel_open()\n"); + ch_log(NULL, "socket() retry in channel_open()\n"); PERROR("E900: socket() retry in channel_open()"); - return -1; + return NULL; } if (connect(sd, (struct sockaddr *)&server, sizeof(server))) { @@ -538,7 +553,7 @@ channel_open(char *hostname, int port_in while (retries-- && ((errno == ECONNREFUSED) || (errno == EINTR))) { - ch_log(-1, "retrying...\n"); + ch_log(NULL, "retrying...\n"); mch_delay(3000L, TRUE); ui_breakcheck(); if (got_int) @@ -557,30 +572,28 @@ channel_open(char *hostname, int port_in if (!success) { /* Get here when the server can't be found. */ - ch_error(-1, "Cannot connect to port after retry\n"); + ch_error(NULL, "Cannot connect to port after retry\n"); PERROR(_("E899: Cannot connect to port after retry2")); sock_close(sd); - return -1; + return NULL; } } } - channels[ch_idx].ch_sock = sd; - channels[ch_idx].ch_close_cb = close_cb; + channel->ch_sock = sd; + channel->ch_close_cb = close_cb; #ifdef FEAT_GUI - channel_gui_register(ch_idx); + channel_gui_register(channel); #endif - return ch_idx; + return channel; } #if defined(CHANNEL_PIPES) || defined(PROTO) void -channel_set_pipes(int ch_idx, int in, int out, int err) +channel_set_pipes(channel_T *channel, int in, int out, int err) { - channel_T *channel = &channels[ch_idx]; - channel->ch_in = in; channel->ch_out = out; channel->ch_err = err; @@ -588,70 +601,73 @@ channel_set_pipes(int ch_idx, int in, in #endif void -channel_set_job(int ch_idx, job_T *job) +channel_set_job(channel_T *channel, job_T *job) { - channels[ch_idx].ch_job = job; + channel->ch_job = job; } /* - * Set the json mode of channel "ch_idx" to "ch_mode". + * Set the json mode of channel "channel" to "ch_mode". */ void -channel_set_json_mode(int ch_idx, ch_mode_T ch_mode) +channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode) { - channels[ch_idx].ch_mode = ch_mode; + channel->ch_mode = ch_mode; } /* - * Set the read timeout of channel "ch_idx". + * Set the read timeout of channel "channel". */ void -channel_set_timeout(int ch_idx, int timeout) +channel_set_timeout(channel_T *channel, int timeout) { - channels[ch_idx].ch_timeout = timeout; + channel->ch_timeout = timeout; } /* - * Set the callback for channel "ch_idx". + * Set the callback for channel "channel". */ void -channel_set_callback(int ch_idx, char_u *callback) +channel_set_callback(channel_T *channel, char_u *callback) { - vim_free(channels[ch_idx].ch_callback); - channels[ch_idx].ch_callback = vim_strsave(callback); + vim_free(channel->ch_callback); + channel->ch_callback = vim_strsave(callback); } /* - * Set the callback for channel "ch_idx" for the response with "id". + * Set the callback for channel "channel" for the response with "id". */ void -channel_set_req_callback(int ch_idx, char_u *callback, int id) +channel_set_req_callback(channel_T *channel, char_u *callback, int id) { - cbq_T *cbhead = &channels[ch_idx].ch_cb_head; + cbq_T *head = &channel->ch_cb_head; cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); if (item != NULL) { - item->callback = vim_strsave(callback); - item->seq_nr = id; - item->prev = cbhead->prev; - cbhead->prev = item; - item->next = cbhead; - item->prev->next = item; + item->cq_callback = vim_strsave(callback); + item->cq_seq_nr = id; + item->cq_prev = head->cq_prev; + head->cq_prev = item; + item->cq_next = NULL; + if (item->cq_prev == NULL) + head->cq_next = item; + else + item->cq_prev->cq_next = item; } } /* - * Invoke the "callback" on channel "ch_idx". + * Invoke the "callback" on channel "channel". */ static void -invoke_callback(int ch_idx, char_u *callback, typval_T *argv) +invoke_callback(channel_T *channel, char_u *callback, typval_T *argv) { typval_T rettv; int dummy; - argv[0].v_type = VAR_NUMBER; - argv[0].vval.v_number = ch_idx; + argv[0].v_type = VAR_CHANNEL; + argv[0].vval.v_channel = channel; call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); @@ -668,19 +684,21 @@ invoke_callback(int ch_idx, char_u *call * Returns NULL if there is nothing. */ char_u * -channel_get(int ch_idx) +channel_get(channel_T *channel) { - readq_T *head = &channels[ch_idx].ch_head; - readq_T *node; + readq_T *head = &channel->ch_head; + readq_T *node = head->rq_next; char_u *p; - if (head->next == head || head->next == NULL) + if (node == 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; + p = node->rq_buffer; + head->rq_next = node->rq_next; + if (node->rq_next == NULL) + head->rq_prev = NULL; + else + node->rq_next->rq_prev = NULL; vim_free(node); return p; } @@ -689,71 +707,70 @@ channel_get(int ch_idx) * Returns the whole buffer contents concatenated. */ static char_u * -channel_get_all(int ch_idx) +channel_get_all(channel_T *channel) { /* Concatenate everything into one buffer. * TODO: avoid multiple allocations. */ - while (channel_collapse(ch_idx) == OK) + while (channel_collapse(channel) == OK) ; - return channel_get(ch_idx); + return channel_get(channel); } /* - * Collapses the first and second buffer in the channel "ch_idx". + * Collapses the first and second buffer in the channel "channel". * Returns FAIL if that is not possible. */ int -channel_collapse(int ch_idx) +channel_collapse(channel_T *channel) { - readq_T *head = &channels[ch_idx].ch_head; - readq_T *node = head->next; + readq_T *head = &channel->ch_head; + readq_T *node = head->rq_next; char_u *p; - if (node == head || node == NULL || node->next == head) + if (node == NULL || node->rq_next == NULL) return FAIL; - p = alloc((unsigned)(STRLEN(node->buffer) - + STRLEN(node->next->buffer) + 1)); + p = alloc((unsigned)(STRLEN(node->rq_buffer) + + STRLEN(node->rq_next->rq_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; + STRCPY(p, node->rq_buffer); + STRCAT(p, node->rq_next->rq_buffer); + vim_free(node->rq_next->rq_buffer); + node->rq_next->rq_buffer = p; - /* dispose of the node and buffer */ - head->next = node->next; - node->next->prev = node->prev; - vim_free(node->buffer); + /* dispose of the node and its buffer */ + head->rq_next = node->rq_next; + head->rq_next->rq_prev = NULL; + vim_free(node->rq_buffer); vim_free(node); return OK; } /* - * Use the read buffer of channel "ch_idx" and parse a JSON messages that is + * Use the read buffer of channel "channel" and parse a JSON messages that is * complete. The messages are added to the queue. * Return TRUE if there is more to read. */ static int -channel_parse_json(int ch_idx) +channel_parse_json(channel_T *channel) { js_read_T reader; typval_T listtv; jsonq_T *item; - channel_T *channel = &channels[ch_idx]; jsonq_T *head = &channel->ch_json_head; int ret; - if (channel_peek(ch_idx) == NULL) + if (channel_peek(channel) == NULL) return FALSE; /* TODO: make reader work properly */ - /* reader.js_buf = channel_peek(ch_idx); */ - reader.js_buf = channel_get_all(ch_idx); + /* reader.js_buf = channel_peek(channel); */ + reader.js_buf = channel_get_all(channel); reader.js_used = 0; reader.js_fill = NULL; /* reader.js_fill = channel_fill; */ - reader.js_cookie = &ch_idx; + reader.js_cookie = channel; ret = json_decode(&reader, &listtv, channel->ch_mode == MODE_JS ? JSON_JS : 0); if (ret == OK) @@ -772,19 +789,22 @@ channel_parse_json(int ch_idx) clear_tv(&listtv); else { - item->value = alloc_tv(); - if (item->value == NULL) + item->jq_value = alloc_tv(); + if (item->jq_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; + *item->jq_value = listtv; + item->jq_prev = head->jq_prev; + head->jq_prev = item; + item->jq_next = NULL; + if (item->jq_prev == NULL) + head->jq_next = item; + else + item->jq_prev->jq_next = item; } } } @@ -794,7 +814,7 @@ channel_parse_json(int ch_idx) * TODO: insert in front */ if (reader.js_buf[reader.js_used] != NUL) { - channel_save(ch_idx, reader.js_buf + reader.js_used, + channel_save(channel, reader.js_buf + reader.js_used, (int)(reader.js_end - reader.js_buf) - reader.js_used); ret = TRUE; } @@ -810,28 +830,40 @@ channel_parse_json(int ch_idx) * Also frees the contained callback name. */ static void -remove_cb_node(cbq_T *node) +remove_cb_node(cbq_T *head, cbq_T *node) { - node->prev->next = node->next; - node->next->prev = node->prev; - vim_free(node->callback); + if (node->cq_prev == NULL) + head->cq_next = node->cq_next; + else + node->cq_prev->cq_next = node->cq_next; + if (node->cq_next == NULL) + head->cq_prev = node->cq_prev; + else + node->cq_next->cq_prev = node->cq_prev; + vim_free(node->cq_callback); vim_free(node); } /* * Remove "node" from the queue that it is in and free it. - * Caller should have freed or used node->value. + * Caller should have freed or used node->jq_value. */ static void -remove_json_node(jsonq_T *node) +remove_json_node(jsonq_T *head, jsonq_T *node) { - node->prev->next = node->next; - node->next->prev = node->prev; + if (node->jq_prev == NULL) + head->jq_next = node->jq_next; + else + node->jq_prev->jq_next = node->jq_next; + if (node->jq_next == NULL) + head->jq_prev = node->jq_prev; + else + node->jq_next->jq_prev = node->jq_prev; vim_free(node); } /* - * Get a message from the JSON queue for channel "ch_idx". + * Get a message from the JSON queue for channel "channel". * When "id" is positive it must match the first number in the list. * When "id" is zero or negative jut get the first message. But not the one * with id ch_block_id. @@ -839,15 +871,14 @@ remove_json_node(jsonq_T *node) * Return FAIL otherwise. */ static int -channel_get_json(int ch_idx, int id, typval_T **rettv) +channel_get_json(channel_T *channel, int id, typval_T **rettv) { - channel_T *channel = &channels[ch_idx]; jsonq_T *head = &channel->ch_json_head; - jsonq_T *item = head->next; + jsonq_T *item = head->jq_next; - while (item != head) + while (item != NULL) { - list_T *l = item->value->vval.v_list; + list_T *l = item->jq_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) @@ -855,22 +886,22 @@ channel_get_json(int ch_idx, int id, typ || tv->vval.v_number == 0 || tv->vval.v_number != channel->ch_block_id))) { - *rettv = item->value; - remove_json_node(item); + *rettv = item->jq_value; + remove_json_node(head, item); return OK; } - item = item->next; + item = item->jq_next; } return FAIL; } /* - * Execute a command received over channel "ch_idx". + * Execute a command received over channel "channel". * "cmd" is the command string, "arg2" the second argument. * "arg3" is the third argument, NULL if missing. */ static void -channel_exe_cmd(int ch_idx, char_u *cmd, typval_T *arg2, typval_T *arg3) +channel_exe_cmd(channel_T *channel, char_u *cmd, typval_T *arg2, typval_T *arg3) { char_u *arg; @@ -928,7 +959,6 @@ channel_exe_cmd(int ch_idx, char_u *cmd, typval_T *tv; typval_T err_tv; char_u *json = NULL; - channel_T *channel = &channels[ch_idx]; int options = channel->ch_mode == MODE_JS ? JSON_JS : 0; /* Don't pollute the display with errors. */ @@ -943,6 +973,8 @@ channel_exe_cmd(int ch_idx, char_u *cmd, { /* If evaluation failed or the result can't be encoded * then return the string "ERROR". */ + vim_free(json); + free_tv(tv); err_tv.v_type = VAR_STRING; err_tv.vval.v_string = (char_u *)"ERROR"; tv = &err_tv; @@ -951,7 +983,7 @@ channel_exe_cmd(int ch_idx, char_u *cmd, } if (json != NULL) { - channel_send(ch_idx, json, "eval"); + channel_send(channel, json, "eval"); vim_free(json); } } @@ -965,11 +997,11 @@ channel_exe_cmd(int ch_idx, char_u *cmd, } /* - * Invoke a callback for channel "ch_idx" if needed. + * Invoke a callback for channel "channel" if needed. * Return OK when a message was handled, there might be another one. */ static int -may_invoke_callback(int ch_idx) +may_invoke_callback(channel_T *channel) { char_u *msg = NULL; typval_T *listtv = NULL; @@ -977,7 +1009,6 @@ may_invoke_callback(int ch_idx) typval_T *typetv; typval_T argv[3]; int seq_nr = -1; - channel_T *channel = &channels[ch_idx]; ch_mode_T ch_mode = channel->ch_mode; if (channel->ch_close_cb != NULL) @@ -987,11 +1018,11 @@ may_invoke_callback(int ch_idx) if (ch_mode != MODE_RAW) { /* Get any json message in the queue. */ - if (channel_get_json(ch_idx, -1, &listtv) == FAIL) + if (channel_get_json(channel, -1, &listtv) == FAIL) { /* Parse readahead, return when there is still no message. */ - channel_parse_json(ch_idx); - if (channel_get_json(ch_idx, -1, &listtv) == FAIL) + channel_parse_json(channel); + if (channel_get_json(channel, -1, &listtv) == FAIL) return FALSE; } @@ -1006,22 +1037,22 @@ may_invoke_callback(int ch_idx) /* ["cmd", arg] or ["cmd", arg, arg] */ if (list->lv_len == 3) arg3 = &list->lv_last->li_tv; - ch_logs(ch_idx, "Executing %s command", (char *)cmd); - channel_exe_cmd(ch_idx, cmd, &argv[1], arg3); - clear_tv(listtv); + ch_logs(channel, "Executing %s command", (char *)cmd); + channel_exe_cmd(channel, cmd, &argv[1], arg3); + free_tv(listtv); return TRUE; } if (typetv->v_type != VAR_NUMBER) { - ch_error(ch_idx, + ch_error(channel, "Dropping message with invalid sequence number type\n"); - clear_tv(listtv); + free_tv(listtv); return FALSE; } seq_nr = typetv->vval.v_number; } - else if (channel_peek(ch_idx) == NULL) + else if (channel_peek(channel) == NULL) { /* nothing to read on raw channel */ return FALSE; @@ -1034,91 +1065,100 @@ may_invoke_callback(int ch_idx) /* For a raw channel we don't know where the message ends, just get * everything. */ - msg = channel_get_all(ch_idx); + msg = channel_get_all(channel); argv[1].v_type = VAR_STRING; argv[1].vval.v_string = msg; } if (seq_nr > 0) { - cbq_T *cbhead = &channel->ch_cb_head; - cbq_T *cbitem = cbhead->next; + cbq_T *head = &channel->ch_cb_head; + cbq_T *item = head->cq_next; int done = FALSE; /* invoke the one-time callback with the matching nr */ - while (cbitem != cbhead) + while (item != NULL) { - if (cbitem->seq_nr == seq_nr) + if (item->cq_seq_nr == seq_nr) { - ch_log(ch_idx, "Invoking one-time callback\n"); - invoke_callback(ch_idx, cbitem->callback, argv); - remove_cb_node(cbitem); + ch_log(channel, "Invoking one-time callback\n"); + invoke_callback(channel, item->cq_callback, argv); + remove_cb_node(head, item); done = TRUE; break; } - cbitem = cbitem->next; + item = item->cq_next; } if (!done) - ch_log(ch_idx, "Dropping message without callback\n"); + ch_log(channel, "Dropping message without callback\n"); } else if (channel->ch_callback != NULL) { /* invoke the channel callback */ - ch_log(ch_idx, "Invoking channel callback\n"); - invoke_callback(ch_idx, channel->ch_callback, argv); + ch_log(channel, "Invoking channel callback\n"); + invoke_callback(channel, channel->ch_callback, argv); } else - ch_log(ch_idx, "Dropping message\n"); + ch_log(channel, "Dropping message\n"); if (listtv != NULL) - clear_tv(listtv); + free_tv(listtv); vim_free(msg); return TRUE; } /* - * Return TRUE when channel "ch_idx" is open for writing to. - * Also returns FALSE or invalid "ch_idx". + * Return TRUE when channel "channel" is open for writing to. + * Also returns FALSE or invalid "channel". */ int -channel_can_write_to(int ch_idx) +channel_can_write_to(channel_T *channel) { - return ch_idx >= 0 && ch_idx < channel_count - && (channels[ch_idx].ch_sock >= 0 + return channel != NULL && (channel->ch_sock >= 0 #ifdef CHANNEL_PIPES - || channels[ch_idx].ch_in >= 0 + || channel->ch_in >= 0 #endif ); } /* - * Return TRUE when channel "ch_idx" is open for reading or writing. - * Also returns FALSE or invalid "ch_idx". + * Return TRUE when channel "channel" is open for reading or writing. + * Also returns FALSE for invalid "channel". */ int -channel_is_open(int ch_idx) +channel_is_open(channel_T *channel) { - return ch_idx >= 0 && ch_idx < channel_count - && (channels[ch_idx].ch_sock >= 0 + return channel != NULL && (channel->ch_sock >= 0 #ifdef CHANNEL_PIPES - || channels[ch_idx].ch_in >= 0 - || channels[ch_idx].ch_out >= 0 - || channels[ch_idx].ch_err >= 0 + || channel->ch_in >= 0 + || channel->ch_out >= 0 + || channel->ch_err >= 0 #endif ); } /* - * Close channel "ch_idx". + * Return a string indicating the status of the channel. + */ + char * +channel_status(channel_T *channel) +{ + if (channel == NULL) + return "fail"; + if (channel_is_open(channel)) + return "open"; + return "closed"; +} + +/* + * Close channel "channel". * This does not trigger the close callback. */ void -channel_close(int ch_idx) +channel_close(channel_T *channel) { - channel_T *channel = &channels[ch_idx]; - jsonq_T *jhead; - cbq_T *cbhead; + ch_log(channel, "Closing channel"); if (channel->ch_sock >= 0) { @@ -1126,25 +1166,10 @@ channel_close(int ch_idx) channel->ch_sock = -1; channel->ch_close_cb = NULL; #ifdef FEAT_GUI - channel_gui_unregister(ch_idx); + channel_gui_unregister(channel); #endif vim_free(channel->ch_callback); channel->ch_callback = NULL; - channel->ch_timeout = 2000; - - while (channel_peek(ch_idx) != NULL) - vim_free(channel_get(ch_idx)); - - cbhead = &channel->ch_cb_head; - while (cbhead->next != cbhead) - remove_cb_node(cbhead->next); - - jhead = &channel->ch_json_head; - while (jhead->next != jhead) - { - clear_tv(jhead->next->value); - remove_json_node(jhead->next); - } } #if defined(CHANNEL_PIPES) if (channel->ch_in >= 0) @@ -1163,39 +1188,43 @@ channel_close(int ch_idx) channel->ch_err = -1; } #endif + channel_clear(channel); } /* - * Store "buf[len]" on channel "ch_idx". + * Store "buf[len]" on channel "channel". * Returns OK or FAIL. */ int -channel_save(int ch_idx, char_u *buf, int len) +channel_save(channel_T *channel, char_u *buf, int len) { readq_T *node; - readq_T *head = &channels[ch_idx].ch_head; + readq_T *head = &channel->ch_head; node = (readq_T *)alloc(sizeof(readq_T)); if (node == NULL) return FAIL; /* out of memory */ - node->buffer = alloc(len + 1); - if (node->buffer == NULL) + node->rq_buffer = alloc(len + 1); + if (node->rq_buffer == NULL) { vim_free(node); return FAIL; /* out of memory */ } - mch_memmove(node->buffer, buf, (size_t)len); - node->buffer[len] = NUL; + mch_memmove(node->rq_buffer, buf, (size_t)len); + node->rq_buffer[len] = NUL; - /* insert node at tail of queue */ - node->next = head; - node->prev = head->prev; - head->prev->next = node; - head->prev = node; + /* append node to the tail of the queue */ + node->rq_next = NULL; + node->rq_prev = head->rq_prev; + if (head->rq_prev == NULL) + head->rq_next = node; + else + head->rq_prev->rq_next = node; + head->rq_prev = node; if (log_fd != NULL) { - ch_log_lead("RECV ", ch_idx); + ch_log_lead("RECV ", channel); fprintf(log_fd, "'"); if (fwrite(buf, len, 1, log_fd) != 1) return FAIL; @@ -1209,40 +1238,49 @@ channel_save(int ch_idx, char_u *buf, in * Returns NULL if there is nothing. */ char_u * -channel_peek(int ch_idx) +channel_peek(channel_T *channel) { - readq_T *head = &channels[ch_idx].ch_head; + readq_T *head = &channel->ch_head; - if (head->next == head || head->next == NULL) + if (head->rq_next == NULL) return NULL; - return head->next->buffer; + return head->rq_next->rq_buffer; } /* - * Clear the read buffer on channel "ch_idx". + * Clear the read buffer on channel "channel". */ void -channel_clear(int ch_idx) +channel_clear(channel_T *channel) { - readq_T *head = &channels[ch_idx].ch_head; - readq_T *node = head->next; - readq_T *next; + jsonq_T *json_head = &channel->ch_json_head; + cbq_T *cb_head = &channel->ch_cb_head; - while (node != NULL && node != head) + while (channel_peek(channel) != NULL) + vim_free(channel_get(channel)); + + while (cb_head->cq_next != NULL) + remove_cb_node(cb_head, cb_head->cq_next); + + while (json_head->jq_next != NULL) { - next = node->next; - vim_free(node->buffer); - vim_free(node); - if (next == head) - { - head->next = head; - head->prev = head; - break; - } - node = next; + free_tv(json_head->jq_next->jq_value); + remove_json_node(json_head, json_head->jq_next); } } +#if defined(EXITFREE) || defined(PROTO) + void +channel_free_all(void) +{ + channel_T *channel; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + channel_clear(channel); +} +#endif + + /* Sent when the channel is found closed when reading. */ #define DETACH_MSG "\"DETACH\"\n" @@ -1255,7 +1293,7 @@ channel_clear(int ch_idx) * Always returns OK for FEAT_GUI_W32. */ static int -channel_wait(int ch_idx, int fd, int timeout) +channel_wait(channel_T *channel, int fd, int timeout) { #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32) struct timeval tval; @@ -1263,7 +1301,7 @@ channel_wait(int ch_idx, int fd, int tim int ret; if (timeout > 0) - ch_logn(ch_idx, "Waiting for %d msec\n", timeout); + ch_logn(channel, "Waiting for %d msec\n", timeout); FD_ZERO(&rfds); FD_SET(fd, &rfds); tval.tv_sec = timeout / 1000; @@ -1277,7 +1315,7 @@ channel_wait(int ch_idx, int fd, int tim # endif if (ret <= 0) { - ch_log(ch_idx, "Nothing to read\n"); + ch_log(channel, "Nothing to read\n"); return FAIL; } break; @@ -1287,12 +1325,12 @@ channel_wait(int ch_idx, int fd, int tim struct pollfd fds; if (timeout > 0) - ch_logn(ch_idx, "Waiting for %d msec\n", timeout); + ch_logn(channel, "Waiting for %d msec\n", timeout); fds.fd = fd; fds.events = POLLIN; if (poll(&fds, 1, timeout) <= 0) { - ch_log(ch_idx, "Nothing to read\n"); + ch_log(channel, "Nothing to read\n"); return FAIL; } # endif @@ -1315,10 +1353,8 @@ channel_get_id(void) * Get the file descriptor to read from, either the socket or stdout. */ static int -get_read_fd(int ch_idx, int use_stderr) +get_read_fd(channel_T *channel, int use_stderr) { - channel_T *channel = &channels[ch_idx]; - if (channel->ch_sock >= 0) return channel->ch_sock; #if defined(CHANNEL_PIPES) @@ -1327,25 +1363,24 @@ get_read_fd(int ch_idx, int use_stderr) if (use_stderr && channel->ch_err >= 0) return channel->ch_err; #endif - ch_error(ch_idx, "channel_read() called while socket is closed\n"); + ch_error(channel, "channel_read() called while socket is closed\n"); return -1; } /* - * Read from channel "ch_idx" for as long as there is something to read. + * Read from channel "channel" for as long as there is something to read. * The data is put in the read queue. */ void -channel_read(int ch_idx, int use_stderr, char *func) +channel_read(channel_T *channel, int use_stderr, char *func) { - channel_T *channel = &channels[ch_idx]; static char_u *buf = NULL; int len = 0; int readlen = 0; int fd; int use_socket = FALSE; - fd = get_read_fd(ch_idx, use_stderr); + fd = get_read_fd(channel, use_stderr); if (fd < 0) return; use_socket = channel->ch_sock >= 0; @@ -1363,7 +1398,7 @@ channel_read(int ch_idx, int use_stderr, * MAXMSGSIZE long. */ for (;;) { - if (channel_wait(ch_idx, fd, 0) == FAIL) + if (channel_wait(channel, fd, 0) == FAIL) break; if (use_socket) len = sock_read(fd, buf, MAXMSGSIZE); @@ -1373,7 +1408,7 @@ channel_read(int ch_idx, int use_stderr, break; /* error or nothing more to read */ /* Store the read message in the queue. */ - channel_save(ch_idx, buf, len); + channel_save(channel, buf, len); readlen += len; if (len < MAXMSGSIZE) break; /* did read everything that's available */ @@ -1402,12 +1437,12 @@ channel_read(int ch_idx, int use_stderr, * -> gui event loop or select loop * -> channel_read() */ - ch_errors(ch_idx, "%s(): Cannot read\n", func); - channel_save(ch_idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG)); + ch_errors(channel, "%s(): Cannot read\n", func); + channel_save(channel, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG)); if (use_socket) { - channel_close(ch_idx); + channel_close(channel); if (channel->ch_close_cb != NULL) (*channel->ch_close_cb)(); } @@ -1421,7 +1456,7 @@ channel_read(int ch_idx, int use_stderr, if (len < 0) { - ch_error(ch_idx, "channel_read(): cannot read from channel\n"); + ch_error(channel, "channel_read(): cannot read from channel\n"); PERROR(_("E896: read from channel")); } } @@ -1434,52 +1469,50 @@ channel_read(int ch_idx, int use_stderr, } /* - * Read from raw channel "ch_idx". Blocks until there is something to read or + * Read from raw channel "channel". 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. */ char_u * -channel_read_block(int ch_idx) +channel_read_block(channel_T *channel) { - ch_log(ch_idx, "Reading raw\n"); - if (channel_peek(ch_idx) == NULL) + ch_log(channel, "Reading raw\n"); + if (channel_peek(channel) == NULL) { - int fd = get_read_fd(ch_idx, FALSE); + int fd = get_read_fd(channel, FALSE); - ch_log(ch_idx, "No readahead\n"); + ch_log(channel, "No readahead\n"); /* Wait for up to the channel timeout. */ - if (fd < 0 || channel_wait(ch_idx, fd, - channels[ch_idx].ch_timeout) == FAIL) + if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) == FAIL) return NULL; - channel_read(ch_idx, FALSE, "channel_read_block"); + channel_read(channel, FALSE, "channel_read_block"); } /* TODO: only get the first message */ - ch_log(ch_idx, "Returning readahead\n"); - return channel_get_all(ch_idx); + ch_log(channel, "Returning readahead\n"); + return channel_get_all(channel); } /* - * Read one JSON message from channel "ch_idx" with ID "id" and store the + * Read one JSON message with ID "id" from channel "channel" and store the * result in "rettv". * Blocks until the message is received or the timeout is reached. */ int -channel_read_json_block(int ch_idx, int id, typval_T **rettv) +channel_read_json_block(channel_T *channel, int id, typval_T **rettv) { int more; - channel_T *channel = &channels[ch_idx]; int fd; - ch_log(ch_idx, "Reading JSON\n"); + ch_log(channel, "Reading JSON\n"); channel->ch_block_id = id; for (;;) { - more = channel_parse_json(ch_idx); + more = channel_parse_json(channel); /* search for messsage "id" */ - if (channel_get_json(ch_idx, id, rettv) == OK) + if (channel_get_json(channel, id, rettv) == OK) { channel->ch_block_id = 0; return OK; @@ -1493,10 +1526,11 @@ channel_read_json_block(int ch_idx, int continue; /* Wait for up to the channel timeout. */ - fd = get_read_fd(ch_idx, FALSE); - if (fd < 0 || channel_wait(ch_idx, fd, channel->ch_timeout) == FAIL) + fd = get_read_fd(channel, FALSE); + if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) + == FAIL) break; - channel_read(ch_idx, FALSE, "channel_read_json_block"); + channel_read(channel, FALSE, "channel_read_json_block"); } } channel->ch_block_id = 0; @@ -1505,36 +1539,36 @@ channel_read_json_block(int ch_idx, int # if defined(WIN32) || defined(PROTO) /* - * Lookup the channel index from the socket. - * Returns -1 when the socket isn't found. + * Lookup the channel from the socket. + * Returns NULL when the socket isn't found. */ - int -channel_fd2idx(sock_T fd) + channel_T * +channel_fd2channel(sock_T fd) { - int i; + channel_T *channel; if (fd >= 0) - for (i = 0; i < channel_count; ++i) - if (channels[i].ch_sock == fd + for (channel = first_channel; channel != NULL; + channel = channel->ch_next) + if (channel->ch_sock == fd # if defined(CHANNEL_PIPES) - || channels[i].ch_out == fd - || channels[i].ch_err == fd + || channel->ch_out == fd + || channel->ch_err == fd # endif ) - return i; - return -1; + return channel; + return NULL; } # endif /* - * Write "buf" (NUL terminated string) to channel "ch_idx". + * Write "buf" (NUL terminated string) to channel "channel". * When "fun" is not NULL an error message might be given. * Return FAIL or OK. */ int -channel_send(int ch_idx, char_u *buf, char *fun) +channel_send(channel_T *channel, char_u *buf, char *fun) { - channel_T *channel = &channels[ch_idx]; int len = (int)STRLEN(buf); int res; int fd = -1; @@ -1553,7 +1587,7 @@ channel_send(int ch_idx, char_u *buf, ch { if (!channel->ch_error && fun != NULL) { - ch_errors(ch_idx, "%s(): write while not connected\n", fun); + ch_errors(channel, "%s(): write while not connected\n", fun); EMSG2("E630: %s(): write while not connected", fun); } channel->ch_error = TRUE; @@ -1562,7 +1596,7 @@ channel_send(int ch_idx, char_u *buf, ch if (log_fd != NULL) { - ch_log_lead("SEND ", ch_idx); + ch_log_lead("SEND ", channel); fprintf(log_fd, "'"); ignored = fwrite(buf, len, 1, log_fd); fprintf(log_fd, "'\n"); @@ -1577,7 +1611,7 @@ channel_send(int ch_idx, char_u *buf, ch { if (!channel->ch_error && fun != NULL) { - ch_errors(ch_idx, "%s(): write failed\n", fun); + ch_errors(channel, "%s(): write failed\n", fun); EMSG2("E631: %s(): write failed", fun); } channel->ch_error = TRUE; @@ -1598,41 +1632,41 @@ channel_send(int ch_idx, char_u *buf, ch channel_poll_setup(int nfd_in, void *fds_in) { int nfd = nfd_in; - int i; + channel_T *channel; struct pollfd *fds = fds_in; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) { - if (channels[i].ch_sock >= 0) + if (channel->ch_sock >= 0) { - channels[i].ch_sock_idx = nfd; - fds[nfd].fd = channels[i].ch_sock; + channel->ch_sock_idx = nfd; + fds[nfd].fd = channel->ch_sock; fds[nfd].events = POLLIN; nfd++; } else - channels[i].ch_sock_idx = -1; + channel->ch_sock_idx = -1; # ifdef CHANNEL_PIPES - if (channels[i].ch_out >= 0) + if (channel->ch_out >= 0) { - channels[i].ch_out_idx = nfd; - fds[nfd].fd = channels[i].ch_out; + channel->ch_out_idx = nfd; + fds[nfd].fd = channel->ch_out; fds[nfd].events = POLLIN; nfd++; } else - channels[i].ch_out_idx = -1; + channel->ch_out_idx = -1; - if (channels[i].ch_err >= 0) + if (channel->ch_err >= 0) { - channels[i].ch_err_idx = nfd; - fds[nfd].fd = channels[i].ch_err; + channel->ch_err_idx = nfd; + fds[nfd].fd = channel->ch_err; fds[nfd].events = POLLIN; nfd++; } else - channels[i].ch_err_idx = -1; + channel->ch_err_idx = -1; # endif } @@ -1646,28 +1680,28 @@ channel_poll_setup(int nfd_in, void *fds channel_poll_check(int ret_in, void *fds_in) { int ret = ret_in; - int i; + channel_T *channel; struct pollfd *fds = fds_in; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) { - if (ret > 0 && channels[i].ch_sock_idx != -1 - && fds[channels[i].ch_sock_idx].revents & POLLIN) + if (ret > 0 && channel->ch_sock_idx != -1 + && fds[channel->ch_sock_idx].revents & POLLIN) { - channel_read(i, FALSE, "channel_poll_check"); + channel_read(channel, FALSE, "channel_poll_check"); --ret; } # ifdef CHANNEL_PIPES - if (ret > 0 && channels[i].ch_out_idx != -1 - && fds[channels[i].ch_out_idx].revents & POLLIN) + if (ret > 0 && channel->ch_out_idx != -1 + && fds[channel->ch_out_idx].revents & POLLIN) { - channel_read(i, FALSE, "channel_poll_check"); + channel_read(channel, FALSE, "channel_poll_check"); --ret; } - if (ret > 0 && channels[i].ch_err_idx != -1 - && fds[channels[i].ch_err_idx].revents & POLLIN) + if (ret > 0 && channel->ch_err_idx != -1 + && fds[channel->ch_err_idx].revents & POLLIN) { - channel_read(i, TRUE, "channel_poll_check"); + channel_read(channel, TRUE, "channel_poll_check"); --ret; } # endif @@ -1685,29 +1719,29 @@ channel_poll_check(int ret_in, void *fds channel_select_setup(int maxfd_in, void *rfds_in) { int maxfd = maxfd_in; - int i; + channel_T *channel; fd_set *rfds = rfds_in; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) { - if (channels[i].ch_sock >= 0) + if (channel->ch_sock >= 0) { - FD_SET(channels[i].ch_sock, rfds); - if (maxfd < channels[i].ch_sock) - maxfd = channels[i].ch_sock; + FD_SET(channel->ch_sock, rfds); + if (maxfd < channel->ch_sock) + maxfd = channel->ch_sock; } # ifdef CHANNEL_PIPES - if (channels[i].ch_out >= 0) + if (channel->ch_out >= 0) { - FD_SET(channels[i].ch_out, rfds); - if (maxfd < channels[i].ch_out) - maxfd = channels[i].ch_out; + FD_SET(channel->ch_out, rfds); + if (maxfd < channel->ch_out) + maxfd = channel->ch_out; } - if (channels[i].ch_err >= 0) + if (channel->ch_err >= 0) { - FD_SET(channels[i].ch_err, rfds); - if (maxfd < channels[i].ch_err) - maxfd = channels[i].ch_err; + FD_SET(channel->ch_err, rfds); + if (maxfd < channel->ch_err) + maxfd = channel->ch_err; } # endif } @@ -1722,28 +1756,28 @@ channel_select_setup(int maxfd_in, void channel_select_check(int ret_in, void *rfds_in) { int ret = ret_in; - int i; + channel_T *channel; fd_set *rfds = rfds_in; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) { - if (ret > 0 && channels[i].ch_sock >= 0 - && FD_ISSET(channels[i].ch_sock, rfds)) + if (ret > 0 && channel->ch_sock >= 0 + && FD_ISSET(channel->ch_sock, rfds)) { - channel_read(i, FALSE, "channel_select_check"); + channel_read(channel, FALSE, "channel_select_check"); --ret; } # ifdef CHANNEL_PIPES - if (ret > 0 && channels[i].ch_out >= 0 - && FD_ISSET(channels[i].ch_out, rfds)) + if (ret > 0 && channel->ch_out >= 0 + && FD_ISSET(channel->ch_out, rfds)) { - channel_read(i, FALSE, "channel_select_check"); + channel_read(channel, FALSE, "channel_select_check"); --ret; } - if (ret > 0 && channels[i].ch_err >= 0 - && FD_ISSET(channels[i].ch_err, rfds)) + if (ret > 0 && channel->ch_err >= 0 + && FD_ISSET(channel->ch_err, rfds)) { - channel_read(i, TRUE, "channel_select_check"); + channel_read(channel, TRUE, "channel_select_check"); --ret; } # endif @@ -1761,13 +1795,13 @@ channel_select_check(int ret_in, void *r int channel_parse_messages(void) { - int i; + channel_T *channel; int ret = FALSE; - for (i = 0; i < channel_count; ++i) - while (may_invoke_callback(i) == OK) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + while (may_invoke_callback(channel) == OK) { - i = 0; /* start over */ + channel = first_channel; /* start over */ ret = TRUE; } return ret; @@ -1779,39 +1813,39 @@ channel_parse_messages(void) int set_ref_in_channel(int copyID) { - int i; - int abort = FALSE; + int abort = FALSE; + channel_T *channel; - for (i = 0; i < channel_count; ++i) + for (channel = first_channel; channel != NULL; channel = channel->ch_next) { - jsonq_T *head = &channels[i].ch_json_head; - jsonq_T *item = head->next; + jsonq_T *head = &channel->ch_json_head; + jsonq_T *item = head->jq_next; - while (item != head) + while (item != NULL) { - list_T *l = item->value->vval.v_list; + list_T *l = item->jq_value->vval.v_list; if (l->lv_copyID != copyID) { l->lv_copyID = copyID; abort = abort || set_ref_in_list(l, copyID, NULL); } - item = item->next; + item = item->jq_next; } } return abort; } /* - * Return the mode of channel "ch_idx". - * If "ch_idx" is invalid returns MODE_JSON. + * Return the mode of channel "channel". + * If "channel" is invalid returns MODE_JSON. */ ch_mode_T -channel_get_mode(int ch_idx) +channel_get_mode(channel_T *channel) { - if (ch_idx < 0 || ch_idx >= channel_count) + if (channel == NULL) return MODE_JSON; - return channels[ch_idx].ch_mode; + return channel->ch_mode; } #endif /* FEAT_CHANNEL */ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -509,6 +509,7 @@ static void f_ch_open(typval_T *argvars, static void f_ch_readraw(typval_T *argvars, typval_T *rettv); static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); +static void f_ch_status(typval_T *argvars, typval_T *rettv); #endif static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); @@ -3084,6 +3085,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char case VAR_FUNC: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; case VAR_LIST: @@ -3863,6 +3865,7 @@ item_lock(typval_T *tv, int deep, int lo case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; case VAR_LIST: @@ -5359,6 +5362,7 @@ eval_index( #endif case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: if (verbose) EMSG(_("E909: Cannot index a special variable")); return FAIL; @@ -5471,6 +5475,7 @@ eval_index( case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; /* not evaluating, skipping over subscript */ case VAR_NUMBER: @@ -6224,6 +6229,10 @@ tv_equal( #ifdef FEAT_JOB return tv1->vval.v_job == tv2->vval.v_job; #endif + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + return tv1->vval.v_channel == tv2->vval.v_channel; +#endif case VAR_UNKNOWN: break; } @@ -7719,12 +7728,26 @@ failret: return OK; } +#ifdef FEAT_CHANNEL + static void +channel_unref(channel_T *channel) +{ + if (channel != NULL && --channel->ch_refcount <= 0) + channel_free(channel); +} +#endif + #ifdef FEAT_JOB static void job_free(job_T *job) { - if (job->jv_channel >= 0) - channel_close(job->jv_channel); + if (job->jv_channel != NULL) + { + /* The channel doesn't count as a references for the job, we need to + * NULL the reference when the job is freed. */ + job->jv_channel->ch_job = NULL; + channel_unref(job->jv_channel); + } mch_clear_job(job); vim_free(job); } @@ -7746,9 +7769,7 @@ job_alloc(void) job = (job_T *)alloc_clear(sizeof(job_T)); if (job != NULL) - { job->jv_refcount = 1; - } return job; } @@ -7850,6 +7871,7 @@ echo_string( case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: + case VAR_CHANNEL: *tofree = NULL; r = get_tv_string_buf(tv, numbuf); break; @@ -7906,6 +7928,7 @@ tv2string( case VAR_DICT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: case VAR_UNKNOWN: break; } @@ -8093,6 +8116,7 @@ static struct fst {"ch_readraw", 1, 2, f_ch_readraw}, {"ch_sendexpr", 2, 3, f_ch_sendexpr}, {"ch_sendraw", 2, 3, f_ch_sendraw}, + {"ch_status", 1, 1, f_ch_status}, #endif {"changenr", 0, 0, f_changenr}, {"char2nr", 1, 2, f_char2nr}, @@ -9781,27 +9805,27 @@ f_ceil(typval_T *argvars, typval_T *rett #ifdef FEAT_CHANNEL /* - * Get the channel index from the handle argument. - * Returns -1 if the handle is invalid or the channel is closed. - */ - static int + * Get the channel from the argument. + * Returns NULL if the handle is invalid. + */ + static channel_T * get_channel_arg(typval_T *tv) { - int ch_idx; - - if (tv->v_type != VAR_NUMBER) + channel_T *channel; + + if (tv->v_type != VAR_CHANNEL) { EMSG2(_(e_invarg2), get_tv_string(tv)); - return -1; - } - ch_idx = tv->vval.v_number; - - if (!channel_can_write_to(ch_idx)) - { - EMSGN(_("E906: not an open channel"), ch_idx); - return -1; - } - return ch_idx; + return NULL; + } + channel = tv->vval.v_channel; + + if (channel == NULL || !channel_is_open(channel)) + { + EMSG(_("E906: not an open channel")); + return NULL; + } + return channel; } /* @@ -9810,10 +9834,10 @@ get_channel_arg(typval_T *tv) static void f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) { - int ch_idx = get_channel_arg(&argvars[0]); - - if (ch_idx >= 0) - channel_close(ch_idx); + channel_T *channel = get_channel_arg(&argvars[0]); + + if (channel != NULL) + channel_close(channel); } /* @@ -9873,10 +9897,11 @@ f_ch_open(typval_T *argvars, typval_T *r int waittime = 0; int timeout = 2000; ch_mode_T ch_mode = MODE_JSON; - int ch_idx; + channel_T *channel; /* default: fail */ - rettv->vval.v_number = -1; + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = NULL; address = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN @@ -9936,15 +9961,15 @@ f_ch_open(typval_T *argvars, typval_T *r return; } - ch_idx = channel_open((char *)address, port, waittime, NULL); - if (ch_idx >= 0) - { - channel_set_json_mode(ch_idx, ch_mode); - channel_set_timeout(ch_idx, timeout); + channel = channel_open((char *)address, port, waittime, NULL); + if (channel != NULL) + { + channel_set_json_mode(channel, ch_mode); + channel_set_timeout(channel, timeout); if (callback != NULL && *callback != NUL) - channel_set_callback(ch_idx, callback); - } - rettv->vval.v_number = ch_idx; + channel_set_callback(channel, callback); + } + rettv->vval.v_channel = channel; } /* @@ -9953,53 +9978,65 @@ f_ch_open(typval_T *argvars, typval_T *r static void f_ch_readraw(typval_T *argvars, typval_T *rettv) { - int ch_idx; + channel_T *channel; /* return an empty string by default */ rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - ch_idx = get_channel_arg(&argvars[0]); - if (ch_idx < 0) - { - EMSG(_(e_invarg)); - return; - } - rettv->vval.v_string = channel_read_block(ch_idx); + channel = get_channel_arg(&argvars[0]); + if (channel != NULL) + rettv->vval.v_string = channel_read_block(channel); +} + +/* + * "ch_status()" function + */ + static void +f_ch_status(typval_T *argvars, typval_T *rettv) +{ + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + + if (argvars[0].v_type != VAR_CHANNEL) + { + EMSG2(_(e_invarg2), get_tv_string(&argvars[0])); + rettv->vval.v_string = NULL; + } + else + rettv->vval.v_string = vim_strsave( + (char_u *)channel_status(argvars[0].vval.v_channel)); } /* * common for "sendexpr()" and "sendraw()" - * Returns the channel index if the caller should read the response. - * Otherwise returns -1. - */ - static int + * Returns the channel if the caller should read the response. + * Otherwise returns NULL. + */ + static channel_T * send_common(typval_T *argvars, char_u *text, int id, char *fun) { - int ch_idx; + channel_T *channel; char_u *callback = NULL; - ch_idx = get_channel_arg(&argvars[0]); - if (ch_idx < 0) - { - EMSG(_(e_invarg)); - return -1; - } + channel = get_channel_arg(&argvars[0]); + if (channel == NULL) + return NULL; if (argvars[2].v_type != VAR_UNKNOWN) { callback = get_callback(&argvars[2]); if (callback == NULL) - return -1; + return NULL; } /* Set the callback. An empty callback means no callback and not reading * the response. */ if (callback != NULL && *callback != NUL) - channel_set_req_callback(ch_idx, callback, id); - - if (channel_send(ch_idx, text, fun) == OK && callback == NULL) - return ch_idx; - return -1; + channel_set_req_callback(channel, callback, id); + + if (channel_send(channel, text, fun) == OK && callback == NULL) + return channel; + return NULL; } /* @@ -10010,7 +10047,7 @@ f_ch_sendexpr(typval_T *argvars, typval_ { char_u *text; typval_T *listtv; - int ch_idx; + channel_T *channel; int id; ch_mode_T ch_mode; @@ -10018,14 +10055,11 @@ f_ch_sendexpr(typval_T *argvars, typval_ rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - ch_idx = get_channel_arg(&argvars[0]); - if (ch_idx < 0) - { - EMSG(_(e_invarg)); - return; - } - - ch_mode = channel_get_mode(ch_idx); + channel = get_channel_arg(&argvars[0]); + if (channel == NULL) + return; + + ch_mode = channel_get_mode(channel); if (ch_mode == MODE_RAW) { EMSG(_("E912: cannot use ch_sendexpr() with a raw channel")); @@ -10038,11 +10072,11 @@ f_ch_sendexpr(typval_T *argvars, typval_ if (text == NULL) return; - ch_idx = send_common(argvars, text, id, "sendexpr"); + channel = send_common(argvars, text, id, "sendexpr"); vim_free(text); - if (ch_idx >= 0) - { - if (channel_read_json_block(ch_idx, id, &listtv) == OK) + if (channel != NULL) + { + if (channel_read_json_block(channel, id, &listtv) == OK) { list_T *list = listtv->vval.v_list; @@ -10050,7 +10084,7 @@ f_ch_sendexpr(typval_T *argvars, typval_ * avoid the value being freed. */ *rettv = list->lv_last->li_tv; list->lv_last->li_tv.v_type = VAR_NUMBER; - clear_tv(listtv); + free_tv(listtv); } } } @@ -10063,16 +10097,16 @@ f_ch_sendraw(typval_T *argvars, typval_T { char_u buf[NUMBUFLEN]; char_u *text; - int ch_idx; + channel_T *channel; /* return an empty string by default */ rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; text = get_tv_string_buf(&argvars[1], buf); - ch_idx = send_common(argvars, text, 0, "sendraw"); - if (ch_idx >= 0) - rettv->vval.v_string = channel_read_block(ch_idx); + channel = send_common(argvars, text, 0, "sendraw"); + if (channel != NULL) + rettv->vval.v_string = channel_read_block(channel); } #endif @@ -10708,7 +10742,14 @@ f_empty(typval_T *argvars, typval_T *ret case VAR_JOB: #ifdef FEAT_JOB - n = argvars[0].vval.v_job->jv_status != JOB_STARTED; + n = argvars[0].vval.v_job == NULL + || argvars[0].vval.v_job->jv_status != JOB_STARTED; + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_CMDWIN + n = argvars[0].vval.v_channel == NULL + || !channel_is_open(argvars[0].vval.v_channel); break; #endif case VAR_UNKNOWN: @@ -14366,8 +14407,10 @@ f_job_getchannel(typval_T *argvars, typv { job_T *job = argvars[0].vval.v_job; - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = job->jv_channel; + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = job->jv_channel; + if (job->jv_channel != NULL) + ++job->jv_channel->ch_refcount; } } @@ -14659,6 +14702,7 @@ f_len(typval_T *argvars, typval_T *rettv case VAR_FLOAT: case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: EMSG(_("E701: Invalid type for len()")); break; } @@ -20040,7 +20084,8 @@ f_type(typval_T *argvars, typval_T *rett else n = 7; break; - case VAR_JOB: n = 8; break; + case VAR_JOB: n = 8; break; + case VAR_CHANNEL: n = 9; break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_type(UNKNOWN)"); n = -1; @@ -21412,6 +21457,11 @@ free_tv(typval_T *varp) job_unref(varp->vval.v_job); break; #endif + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + channel_unref(varp->vval.v_channel); + break; +#endif case VAR_NUMBER: case VAR_FLOAT: case VAR_UNKNOWN: @@ -21462,6 +21512,11 @@ clear_tv(typval_T *varp) varp->vval.v_job = NULL; #endif break; + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + channel_unref(varp->vval.v_channel); + varp->vval.v_channel = NULL; +#endif case VAR_UNKNOWN: break; } @@ -21531,6 +21586,11 @@ get_tv_number_chk(typval_T *varp, int *d EMSG(_("E910: Using a Job as a Number")); break; #endif + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + EMSG(_("E913: Using a Channel as a Number")); + break; +#endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); break; @@ -21572,6 +21632,11 @@ get_tv_float(typval_T *varp) EMSG(_("E911: Using a Job as a Float")); break; # endif + case VAR_CHANNEL: +# ifdef FEAT_CHANNEL + EMSG(_("E914: Using a Channel as a Float")); + break; +# endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); break; @@ -21707,6 +21772,18 @@ get_tv_string_buf_chk(typval_T *varp, ch } #endif break; + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + { + channel_T *channel = varp->vval.v_channel; + char *status = channel_status(channel); + + vim_snprintf((char *)buf, NUMBUFLEN, + "channel %d %s", channel->ch_id, status); + return buf; + } +#endif + break; case VAR_UNKNOWN: EMSG(_("E908: using an invalid value as a String")); break; @@ -22337,6 +22414,12 @@ copy_tv(typval_T *from, typval_T *to) ++to->vval.v_job->jv_refcount; break; #endif + case VAR_CHANNEL: +#ifdef FEAT_CHANNEL + to->vval.v_channel = from->vval.v_channel; + ++to->vval.v_channel->ch_refcount; + break; +#endif case VAR_STRING: case VAR_FUNC: if (from->vval.v_string == NULL) @@ -22404,6 +22487,7 @@ item_copy( case VAR_FUNC: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: copy_tv(from, to); break; case VAR_LIST: @@ -25076,6 +25160,7 @@ write_viminfo_varlist(FILE *fp) case VAR_UNKNOWN: case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: continue; } fprintf(fp, "!%s\t%s\t", this_var->di_key, s); diff --git a/src/gui_w48.c b/src/gui_w48.c --- a/src/gui_w48.c +++ b/src/gui_w48.c @@ -1780,14 +1780,14 @@ process_message(void) #ifdef FEAT_CHANNEL if (msg.message == WM_NETBEANS) { - int channel_idx = channel_fd2idx((sock_T)msg.wParam); - - if (channel_idx >= 0) + channel_T *channel = channel_fd2channel((sock_T)msg.wParam); + + if (channel != NULL) { /* Disable error messages, they can mess up the display and throw * an exception. */ ++emsg_off; - channel_read(channel_idx, FALSE, "process_message"); + channel_read(channel, FALSE, "process_message"); --emsg_off; } return; diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1561,6 +1561,7 @@ do_pyeval (char_u *str, typval_T *rettv) case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; } } diff --git a/src/if_python3.c b/src/if_python3.c --- a/src/if_python3.c +++ b/src/if_python3.c @@ -1654,6 +1654,7 @@ do_py3eval (char_u *str, typval_T *rettv case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; } } diff --git a/src/json.c b/src/json.c --- a/src/json.c +++ b/src/json.c @@ -183,6 +183,7 @@ json_encode_item(garray_T *gap, typval_T case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: /* no JSON equivalent TODO: better error */ EMSG(_(e_invarg)); return FAIL; diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -1139,6 +1139,9 @@ free_all_mem(void) # ifdef FEAT_DIFF diff_clear(curtab); # endif +# ifdef FEAT_CHANNEL + channel_free_all(); +# endif clear_sb_text(); /* free any scrollback text */ /* Free some global vars. */ diff --git a/src/netbeans.c b/src/netbeans.c --- a/src/netbeans.c +++ b/src/netbeans.c @@ -63,8 +63,8 @@ static int nb_do_cmd(int, char_u *, int static void nb_send(char *buf, char *fun); static void nb_free(void); -#define NETBEANS_OPEN (nb_channel_idx >= 0 && channel_is_open(nb_channel_idx)) -static int nb_channel_idx = -1; +#define NETBEANS_OPEN (channel_can_write_to(nb_channel)) +static channel_T *nb_channel = NULL; static int r_cmdno; /* current command number for reply */ static int dosetvisible = FALSE; @@ -85,7 +85,7 @@ static int inAtomic = 0; static void nb_channel_closed(void) { - nb_channel_idx = -1; + nb_channel = NULL; } /* @@ -98,10 +98,10 @@ netbeans_close(void) if (NETBEANS_OPEN) { netbeans_send_disconnect(); - if (nb_channel_idx >= 0) + if (nb_channel != NULL) /* Close the socket and remove the input handlers. */ - channel_close(nb_channel_idx); - nb_channel_idx = -1; + channel_close(nb_channel); + nb_channel = NULL; } #ifdef FEAT_BEVAL @@ -213,8 +213,8 @@ netbeans_connect(char *params, int doabo if (hostname != NULL && address != NULL && password != NULL) { port = atoi(address); - nb_channel_idx = channel_open(hostname, port, 0, nb_channel_closed); - if (nb_channel_idx >= 0) + nb_channel = channel_open(hostname, port, 0, nb_channel_closed); + if (nb_channel != NULL) { /* success */ # ifdef FEAT_BEVAL @@ -230,7 +230,7 @@ netbeans_connect(char *params, int doabo } } - if (nb_channel_idx < 0 && doabort) + if (nb_channel == NULL && doabort) getout(1); vim_free(hostname); @@ -383,9 +383,9 @@ netbeans_parse_messages(void) char_u *p; int own_node; - while (nb_channel_idx >= 0) + while (nb_channel != NULL) { - buffer = channel_peek(nb_channel_idx); + buffer = channel_peek(nb_channel); if (buffer == NULL) break; /* nothing to read */ @@ -396,7 +396,7 @@ netbeans_parse_messages(void) /* Command isn't complete. If there is no following buffer, * return (wait for more). If there is another buffer following, * prepend the text to that buffer and delete this one. */ - if (channel_collapse(nb_channel_idx) == FAIL) + if (channel_collapse(nb_channel) == FAIL) return; } else @@ -409,7 +409,7 @@ netbeans_parse_messages(void) if (*p == NUL) { own_node = TRUE; - channel_get(nb_channel_idx); + channel_get(nb_channel); } else own_node = FALSE; @@ -600,8 +600,8 @@ nb_free(void) } /* free the queued netbeans commands */ - if (nb_channel_idx >= 0) - channel_clear(nb_channel_idx); + if (nb_channel != NULL) + channel_clear(nb_channel); } /* @@ -756,8 +756,8 @@ netbeans_end(void) static void nb_send(char *buf, char *fun) { - if (nb_channel_idx >= 0) - channel_send(nb_channel_idx, (char_u *)buf, fun); + if (nb_channel != NULL) + channel_send(nb_channel, (char_u *)buf, fun); } /* diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5043,7 +5043,7 @@ mch_start_job(char **argv, job_T *job) int fd_in[2]; /* for stdin */ int fd_out[2]; /* for stdout */ int fd_err[2]; /* for stderr */ - int ch_idx; + channel_T *channel; /* default is to fail */ job->jv_status = JOB_FAILED; @@ -5055,8 +5055,8 @@ mch_start_job(char **argv, job_T *job) if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0)) goto failed; - ch_idx = add_channel(); - if (ch_idx < 0) + channel = add_channel(); + if (channel == NULL) goto failed; pid = fork(); /* maybe we should use vfork() */ @@ -5108,14 +5108,14 @@ mch_start_job(char **argv, job_T *job) /* parent */ job->jv_pid = pid; job->jv_status = JOB_STARTED; - job->jv_channel = ch_idx; + job->jv_channel = channel; /* child stdin, stdout and stderr */ close(fd_in[0]); close(fd_out[1]); close(fd_err[1]); - channel_set_pipes(ch_idx, fd_in[1], fd_out[0], fd_err[0]); - channel_set_job(ch_idx, job); + channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); + channel_set_job(channel, job); return; diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -1,33 +1,36 @@ /* channel.c */ void ch_logfile(FILE *file); -int add_channel(void); +channel_T *add_channel(void); +void channel_free(channel_T *channel); void channel_gui_register_all(void); -int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); -void channel_set_pipes(int idx, int in, int out, int err); -void channel_set_job(int idx, job_T *job); -void channel_set_json_mode(int idx, ch_mode_T ch_mode); -void channel_set_timeout(int idx, int timeout); -void channel_set_callback(int idx, char_u *callback); -void channel_set_req_callback(int idx, char_u *callback, int id); -char_u *channel_get(int idx); -int channel_collapse(int idx); -int channel_can_write_to(int idx); -int channel_is_open(int idx); -void channel_close(int idx); -int channel_save(int idx, char_u *buf, int len); -char_u *channel_peek(int idx); -void channel_clear(int idx); +channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); +void channel_set_pipes(channel_T *channel, int in, int out, int err); +void channel_set_job(channel_T *channel, job_T *job); +void channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode); +void channel_set_timeout(channel_T *channel, int timeout); +void channel_set_callback(channel_T *channel, char_u *callback); +void channel_set_req_callback(channel_T *channel, char_u *callback, int id); +char_u *channel_get(channel_T *channel); +int channel_collapse(channel_T *channel); +int channel_can_write_to(channel_T *channel); +int channel_is_open(channel_T *channel); +char *channel_status(channel_T *channel); +void channel_close(channel_T *channel); +int channel_save(channel_T *channel, char_u *buf, int len); +char_u *channel_peek(channel_T *channel); +void channel_clear(channel_T *channel); +void channel_free_all(void); int channel_get_id(void); -void channel_read(int idx, int use_stderr, char *func); -char_u *channel_read_block(int idx); -int channel_read_json_block(int ch_idx, int id, typval_T **rettv); -int channel_fd2idx(sock_T fd); -int channel_send(int idx, char_u *buf, char *fun); +void channel_read(channel_T *channel, int use_stderr, char *func); +char_u *channel_read_block(channel_T *channel); +int channel_read_json_block(channel_T *channel, int id, typval_T **rettv); +channel_T *channel_fd2channel(sock_T fd); +int channel_send(channel_T *channel, char_u *buf, char *fun); int channel_poll_setup(int nfd_in, void *fds_in); int channel_poll_check(int ret_in, void *fds_in); int channel_select_setup(int maxfd_in, void *rfds_in); int channel_select_check(int ret_in, void *rfds_in); int channel_parse_messages(void); int set_ref_in_channel(int copyID); -ch_mode_T channel_get_mode(int idx); +ch_mode_T channel_get_mode(channel_T *channel); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1127,7 +1127,8 @@ typedef enum VAR_DICT, /* "v_dict" is used */ VAR_FLOAT, /* "v_float" is used */ VAR_SPECIAL, /* "v_number" is used */ - VAR_JOB /* "v_job" is used */ + VAR_JOB, /* "v_job" is used */ + VAR_CHANNEL /* "v_channel" is used */ } vartype_T; /* @@ -1149,6 +1150,9 @@ typedef struct #ifdef FEAT_JOB job_T *v_job; /* job value (can be NULL!) */ #endif +#ifdef FEAT_CHANNEL + channel_T *v_channel; /* channel value (can be NULL!) */ +#endif } vval; } typval_T; @@ -1260,7 +1264,7 @@ struct jobvar_S jobstatus_T jv_status; int jv_refcount; /* reference count */ - int jv_channel; /* channel for I/O */ + channel_T *jv_channel; /* channel for I/O, reference counted */ }; /* @@ -1268,35 +1272,41 @@ struct jobvar_S */ struct readq_S { - char_u *buffer; - readq_T *next; - readq_T *prev; + char_u *rq_buffer; + readq_T *rq_next; + readq_T *rq_prev; }; struct jsonq_S { - typval_T *value; - jsonq_T *next; - jsonq_T *prev; + typval_T *jq_value; + jsonq_T *jq_next; + jsonq_T *jq_prev; }; struct cbq_S { - char_u *callback; - int seq_nr; - cbq_T *next; - cbq_T *prev; + char_u *cq_callback; + int cq_seq_nr; + cbq_T *cq_next; + cbq_T *cq_prev; }; /* mode for a channel */ typedef enum { - MODE_RAW = 0, + MODE_NL = 0, + MODE_RAW, MODE_JSON, MODE_JS } ch_mode_T; struct channel_S { + channel_T *ch_next; + channel_T *ch_prev; + + int ch_id; /* ID of the channel */ + sock_T ch_sock; /* the socket, -1 for a closed channel */ #ifdef UNIX @@ -1342,7 +1352,11 @@ struct channel_S { int ch_timeout; /* request timeout in msec */ - job_T *ch_job; /* job that uses this channel */ + job_T *ch_job; /* Job that uses this channel; this does not + * count as a reference to avoid a circular + * reference. */ + + int ch_refcount; /* reference count */ }; diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -1,13 +1,7 @@ #!/usr/bin/python # # Server that will accept connections from a Vim channel. -# Run this server and then in Vim you can open the channel: -# :let handle = ch_open('localhost:8765', 'json') -# -# Then Vim can send requests to the server: -# :let response = ch_sendexpr(handle, 'hello!') -# -# See ":help channel-demo" in Vim. +# Used by test_channel.vim. # # This requires Python 2.6 or later. diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -83,7 +83,6 @@ func s:kill_server() endif endfunc -let s:responseHandle = -1 let s:responseMsg = '' func s:RequestHandler(handle, msg) let s:responseHandle = a:handle @@ -92,7 +91,7 @@ endfunc func s:communicate(port) let handle = ch_open('localhost:' . a:port, s:chopt) - if handle < 0 + if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif @@ -115,14 +114,22 @@ func s:communicate(port) " Send a request with a specific handler. call ch_sendexpr(handle, 'hello!', 's:RequestHandler') sleep 10m - call assert_equal(handle, s:responseHandle) + if !exists('s:responseHandle') + call assert_false(1, 's:responseHandle was not set') + else + call assert_equal(handle, s:responseHandle) + endif call assert_equal('got it', s:responseMsg) - let s:responseHandle = -1 + unlet s:responseHandle let s:responseMsg = '' call ch_sendexpr(handle, 'hello!', function('s:RequestHandler')) sleep 10m - call assert_equal(handle, s:responseHandle) + if !exists('s:responseHandle') + call assert_false(1, 's:responseHandle was not set') + else + call assert_equal(handle, s:responseHandle) + endif call assert_equal('got it', s:responseMsg) " Send an eval request that works. @@ -169,7 +176,7 @@ endfunc " Test that we can open two channels. func s:two_channels(port) let handle = ch_open('localhost:' . a:port, s:chopt) - if handle < 0 + if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif @@ -177,7 +184,7 @@ func s:two_channels(port) call assert_equal('got it', ch_sendexpr(handle, 'hello!')) let newhandle = ch_open('localhost:' . a:port, s:chopt) - if newhandle < 0 + if ch_status(newhandle) == "fail" call assert_false(1, "Can't open second channel") return endif @@ -197,7 +204,7 @@ endfunc " Test that a server crash is handled gracefully. func s:server_crash(port) let handle = ch_open('localhost:' . a:port, s:chopt) - if handle < 0 + if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif @@ -219,7 +226,7 @@ endfunc func s:channel_handler(port) let handle = ch_open('localhost:' . a:port, s:chopt) - if handle < 0 + if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif @@ -247,7 +254,7 @@ endfunc func Test_connect_waittime() let start = reltime() let handle = ch_open('localhost:9876', s:chopt) - if handle >= 0 + if ch_status(handle) == "fail" " Oops, port does exists. call ch_close(handle) else @@ -257,7 +264,7 @@ func Test_connect_waittime() let start = reltime() let handle = ch_open('localhost:9867', {'waittime': 2000}) - if handle >= 0 + if ch_status(handle) != "fail" " Oops, port does exists. call ch_close(handle) else diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1315, +/**/ 1314, /**/ 1313,