To: vim_dev@googlegroups.com Subject: Patch 7.4.1315 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ NOTE: The type that ch_open() returns changed from a number to Channel. Patch 7.4.1315 Problem: Using a channel handle does not allow for freeing it when unused. Solution: Add the Channel variable type. Files: src/structs.h, src/channel.c, src/misc2.c, src/eval.c, src/if_python.c, src/if_python3.c, src/json.c, src/gui_w48.c, src/netbeans.c, src/proto/channel.pro, src/os_unix.c, src/testdir/test_channel.py, src/testdir/test_channel.vim *** ../vim-7.4.1314/src/structs.h 2016-02-13 17:04:08.426819018 +0100 --- src/structs.h 2016-02-13 22:08:28.312195961 +0100 *************** *** 1127,1133 **** VAR_DICT, /* "v_dict" is used */ VAR_FLOAT, /* "v_float" is used */ VAR_SPECIAL, /* "v_number" is used */ ! VAR_JOB /* "v_job" is used */ } vartype_T; /* --- 1127,1134 ---- 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_CHANNEL /* "v_channel" is used */ } vartype_T; /* *************** *** 1149,1154 **** --- 1150,1158 ---- #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,1266 **** jobstatus_T jv_status; int jv_refcount; /* reference count */ ! int jv_channel; /* channel for I/O */ }; /* --- 1264,1270 ---- jobstatus_T jv_status; int jv_refcount; /* reference count */ ! channel_T *jv_channel; /* channel for I/O, reference counted */ }; /* *************** *** 1268,1302 **** */ struct readq_S { ! char_u *buffer; ! readq_T *next; ! readq_T *prev; }; struct jsonq_S { ! typval_T *value; ! jsonq_T *next; ! jsonq_T *prev; }; struct cbq_S { ! char_u *callback; ! int seq_nr; ! cbq_T *next; ! cbq_T *prev; }; /* mode for a channel */ typedef enum { ! MODE_RAW = 0, MODE_JSON, MODE_JS } ch_mode_T; struct channel_S { sock_T ch_sock; /* the socket, -1 for a closed channel */ #ifdef UNIX --- 1272,1312 ---- */ struct readq_S { ! char_u *rq_buffer; ! readq_T *rq_next; ! readq_T *rq_prev; }; struct jsonq_S { ! typval_T *jq_value; ! jsonq_T *jq_next; ! jsonq_T *jq_prev; }; struct cbq_S { ! char_u *cq_callback; ! int cq_seq_nr; ! cbq_T *cq_next; ! cbq_T *cq_prev; }; /* mode for a channel */ typedef enum { ! 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,1348 **** int ch_timeout; /* request timeout in msec */ ! job_T *ch_job; /* job that uses this channel */ }; --- 1352,1362 ---- int ch_timeout; /* request timeout in msec */ ! 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 */ }; *** ../vim-7.4.1314/src/channel.c 2016-02-13 18:50:34.552532464 +0100 --- src/channel.c 2016-02-13 23:01:10.466901784 +0100 *************** *** 54,65 **** 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; --- 54,59 ---- *************** *** 75,163 **** } static void ! ch_log_lead(char *what, int ch_idx) { if (log_fd != NULL) { ! if (ch_idx >= 0) ! fprintf(log_fd, "%son %d: ", what, ch_idx); else fprintf(log_fd, "%s: ", what); } } static void ! ch_log(int ch_idx, char *msg) { if (log_fd != NULL) { ! ch_log_lead("", ch_idx); fputs(msg, log_fd); fflush(log_fd); } } static void ! ch_logn(int ch_idx, char *msg, int nr) { if (log_fd != NULL) { ! ch_log_lead("", ch_idx); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void ! ch_logs(int ch_idx, char *msg, char *name) { if (log_fd != NULL) { ! ch_log_lead("", ch_idx); fprintf(log_fd, msg, name); fflush(log_fd); } } static void ! ch_logsn(int ch_idx, char *msg, char *name, int nr) { if (log_fd != NULL) { ! ch_log_lead("", ch_idx); fprintf(log_fd, msg, name, nr); fflush(log_fd); } } static void ! ch_error(int ch_idx, char *msg) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch_idx); fputs(msg, log_fd); fflush(log_fd); } } static void ! ch_errorn(int ch_idx, char *msg, int nr) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch_idx); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void ! ch_errors(int ch_idx, char *msg, char *arg) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch_idx); fprintf(log_fd, msg, arg); fflush(log_fd); } --- 69,157 ---- } static void ! ch_log_lead(char *what, channel_T *ch) { if (log_fd != NULL) { ! if (ch != NULL) ! fprintf(log_fd, "%son %d: ", what, ch->ch_id); else fprintf(log_fd, "%s: ", what); } } static void ! ch_log(channel_T *ch, char *msg) { if (log_fd != NULL) { ! ch_log_lead("", ch); fputs(msg, log_fd); fflush(log_fd); } } static void ! ch_logn(channel_T *ch, char *msg, int nr) { if (log_fd != NULL) { ! ch_log_lead("", ch); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void ! ch_logs(channel_T *ch, char *msg, char *name) { if (log_fd != NULL) { ! ch_log_lead("", ch); fprintf(log_fd, msg, name); fflush(log_fd); } } static void ! ch_logsn(channel_T *ch, char *msg, char *name, int nr) { if (log_fd != NULL) { ! ch_log_lead("", ch); fprintf(log_fd, msg, name, nr); fflush(log_fd); } } static void ! ch_error(channel_T *ch, char *msg) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch); fputs(msg, log_fd); fflush(log_fd); } } static void ! ch_errorn(channel_T *ch, char *msg, int nr) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch); fprintf(log_fd, msg, nr); fflush(log_fd); } } static void ! ch_errors(channel_T *ch, char *msg, char *arg) { if (log_fd != NULL) { ! ch_log_lead("ERR ", ch); fprintf(log_fd, msg, arg); fflush(log_fd); } *************** *** 205,280 **** } #endif ! static void ! init_channel(int ch_idx) { ! channel_T *ch; ! ch = &channels[ch_idx]; ! (void)vim_memset(ch, 0, sizeof(channel_T)); ! ch->ch_sock = (sock_T)-1; #ifdef CHANNEL_PIPES ! ch->ch_in = -1; ! ch->ch_out = -1; ! ch->ch_err = -1; #endif #ifdef FEAT_GUI_X11 ! ch->ch_inputHandler = (XtInputId)NULL; #endif #ifdef FEAT_GUI_GTK ! ch->ch_inputHandler = 0; #endif #ifdef FEAT_GUI_W32 ! ch->ch_inputHandler = -1; #endif - /* initialize circular queues */ - ch->ch_head.next = &ch->ch_head; - ch->ch_head.prev = &ch->ch_head; - ch->ch_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; ! ch->ch_timeout = 2000; } /* ! * 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. */ ! int ! add_channel(void) { ! 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; ! } 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++; } #if defined(FEAT_GUI) || defined(PROTO) /* * Read a command from netbeans. */ --- 199,299 ---- } #endif ! /* ! * The list of all allocated channels. ! */ ! static channel_T *first_channel = NULL; ! static int next_ch_id = 0; ! ! /* ! * 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)); ! ! 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 ! channel->ch_in = -1; ! channel->ch_out = -1; ! channel->ch_err = -1; #endif #ifdef FEAT_GUI_X11 ! channel->ch_inputHandler = (XtInputId)NULL; #endif #ifdef FEAT_GUI_GTK ! channel->ch_inputHandler = 0; #endif #ifdef FEAT_GUI_W32 ! channel->ch_inputHandler = -1; #endif ! channel->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; } /* ! * Close a channel and free all its resources. */ ! void ! channel_free(channel_T *channel) { ! 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 ! 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,290 **** int *unused1 UNUSED, XtInputId *unused2 UNUSED) { ! channel_read((int)(long)clientData, FALSE, "messageFromNetbeans"); } #endif --- 303,309 ---- int *unused1 UNUSED, XtInputId *unused2 UNUSED) { ! channel_read_netbeans((int)(long)clientData); } #endif *************** *** 294,308 **** gint unused1 UNUSED, GdkInputCondition unused2 UNUSED) { ! channel_read((int)(long)clientData, FALSE, "messageFromNetbeans"); } #endif static void ! channel_gui_register(int ch_idx) { - channel_T *channel = &channels[ch_idx]; - if (!CH_HAS_GUI) return; --- 313,325 ---- gint unused1 UNUSED, GdkInputCondition unused2 UNUSED) { ! channel_read_netbeans((int)(long)clientData); } #endif static void ! channel_gui_register(channel_T *channel) { if (!CH_HAS_GUI) return; *************** *** 315,321 **** channel->ch_inputHandler = XtAppAddInput((XtAppContext)app_context, channel->ch_sock, (XtPointer)(XtInputReadMask + XtInputExceptMask), ! messageFromNetbeans, (XtPointer)(long)ch_idx); # else # ifdef FEAT_GUI_GTK /* --- 332,338 ---- channel->ch_inputHandler = XtAppAddInput((XtAppContext)app_context, channel->ch_sock, (XtPointer)(XtInputReadMask + XtInputExceptMask), ! messageFromNetbeans, (XtPointer)(long)channel->ch_id); # else # ifdef FEAT_GUI_GTK /* *************** *** 326,332 **** channel->ch_inputHandler = gdk_input_add((gint)channel->ch_sock, (GdkInputCondition) ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), ! messageFromNetbeans, (gpointer)(long)ch_idx); # else # ifdef FEAT_GUI_W32 /* --- 343,349 ---- channel->ch_inputHandler = gdk_input_add((gint)channel->ch_sock, (GdkInputCondition) ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), ! messageFromNetbeans, (gpointer)(long)channel->ch_id); # else # ifdef FEAT_GUI_W32 /* *************** *** 348,366 **** void channel_gui_register_all(void) { ! int i; ! for (i = 0; i < channel_count; ++i) /* TODO: pipes */ ! if (channels[i].ch_sock >= 0) ! channel_gui_register(i); } static void ! channel_gui_unregister(int ch_idx) { - channel_T *channel = &channels[ch_idx]; - /* TODO: pipes */ # ifdef FEAT_GUI_X11 if (channel->ch_inputHandler != (XtInputId)NULL) --- 365,381 ---- void channel_gui_register_all(void) { ! channel_T *channel; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) /* TODO: pipes */ ! if (channel->ch_sock >= 0) ! channel_gui_register(channel); } static void ! channel_gui_unregister(channel_T *channel) { /* TODO: pipes */ # ifdef FEAT_GUI_X11 if (channel->ch_inputHandler != (XtInputId)NULL) *************** *** 391,400 **** /* * Open a socket channel to "hostname":"port". ! * Returns the channel number for success. ! * Returns a negative number for failure. */ ! int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)) { int sd; --- 406,415 ---- /* * Open a socket channel to "hostname":"port". ! * Returns the channel for success. ! * Returns NULL for failure. */ ! channel_T * channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)) { int sd; *************** *** 406,431 **** #else int port = port_in; #endif ! int ch_idx; int ret; #ifdef WIN32 channel_init_winsock(); #endif ! ch_idx = add_channel(); ! if (ch_idx < 0) { ! ch_error(-1, "All channels are in use.\n"); EMSG(_("E897: All channels are in use")); ! return -1; } if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { ! ch_error(-1, "in socket() in channel_open().\n"); PERROR("E898: socket() in channel_open()"); ! return -1; } /* Get the server internet address and put into addr structure */ --- 421,446 ---- #else int port = port_in; #endif ! channel_T *channel; int ret; #ifdef WIN32 channel_init_winsock(); #endif ! channel = add_channel(); ! if (channel == NULL) { ! ch_error(NULL, "Cannot allocate channel.\n"); EMSG(_("E897: All channels are in use")); ! return NULL; } if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { ! ch_error(NULL, "in socket() in channel_open().\n"); PERROR("E898: socket() in channel_open()"); ! return NULL; } /* Get the server internet address and put into addr structure */ *************** *** 435,444 **** server.sin_port = htons(port); if ((host = gethostbyname(hostname)) == NULL) { ! ch_error(-1, "in gethostbyname() in channel_open()\n"); PERROR("E901: gethostbyname() in channel_open()"); sock_close(sd); ! return -1; } memcpy((char *)&server.sin_addr, host->h_addr, host->h_length); --- 450,459 ---- server.sin_port = htons(port); if ((host = gethostbyname(hostname)) == NULL) { ! ch_error(NULL, "in gethostbyname() in channel_open()\n"); PERROR("E901: gethostbyname() in channel_open()"); sock_close(sd); ! return NULL; } memcpy((char *)&server.sin_addr, host->h_addr, host->h_length); *************** *** 454,468 **** ) { SOCK_ERRNO; ! ch_errorn(-1, "channel_open: Connect failed with errno %d\n", errno); sock_close(sd); ! return -1; } } /* Try connecting to the server. */ ! ch_logsn(-1, "Connecting to %s port %d", hostname, port); ret = connect(sd, (struct sockaddr *)&server, sizeof(server)); SOCK_ERRNO; if (ret < 0) --- 469,483 ---- ) { SOCK_ERRNO; ! ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); sock_close(sd); ! return NULL; } } /* Try connecting to the server. */ ! ch_logsn(NULL, "Connecting to %s port %d", hostname, port); ret = connect(sd, (struct sockaddr *)&server, sizeof(server)); SOCK_ERRNO; if (ret < 0) *************** *** 473,483 **** #endif ) { ! ch_errorn(-1, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); ! return -1; } } --- 488,498 ---- #endif ) { ! ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); ! return NULL; } } *************** *** 494,510 **** if (ret < 0) { SOCK_ERRNO; ! ch_errorn(-1, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); ! return -1; } if (!FD_ISSET(sd, &wfds)) { /* don't give an error, we just timed out. */ sock_close(sd); ! return -1; } } --- 509,525 ---- if (ret < 0) { SOCK_ERRNO; ! ch_errorn(NULL, "channel_open: Connect failed with errno %d\n", errno); PERROR(_("E902: Cannot connect to port")); sock_close(sd); ! return NULL; } if (!FD_ISSET(sd, &wfds)) { /* don't give an error, we just timed out. */ sock_close(sd); ! return NULL; } } *************** *** 525,533 **** if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { SOCK_ERRNO; ! ch_log(-1, "socket() retry in channel_open()\n"); PERROR("E900: socket() retry in channel_open()"); ! return -1; } if (connect(sd, (struct sockaddr *)&server, sizeof(server))) { --- 540,548 ---- if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { SOCK_ERRNO; ! ch_log(NULL, "socket() retry in channel_open()\n"); PERROR("E900: socket() retry in channel_open()"); ! return NULL; } if (connect(sd, (struct sockaddr *)&server, sizeof(server))) { *************** *** 538,544 **** while (retries-- && ((errno == ECONNREFUSED) || (errno == EINTR))) { ! ch_log(-1, "retrying...\n"); mch_delay(3000L, TRUE); ui_breakcheck(); if (got_int) --- 553,559 ---- while (retries-- && ((errno == ECONNREFUSED) || (errno == EINTR))) { ! ch_log(NULL, "retrying...\n"); mch_delay(3000L, TRUE); ui_breakcheck(); if (got_int) *************** *** 557,586 **** if (!success) { /* Get here when the server can't be found. */ ! ch_error(-1, "Cannot connect to port after retry\n"); PERROR(_("E899: Cannot connect to port after retry2")); sock_close(sd); ! return -1; } } } ! channels[ch_idx].ch_sock = sd; ! channels[ch_idx].ch_close_cb = close_cb; #ifdef FEAT_GUI ! channel_gui_register(ch_idx); #endif ! return ch_idx; } #if defined(CHANNEL_PIPES) || defined(PROTO) void ! channel_set_pipes(int ch_idx, int in, int out, int err) { - channel_T *channel = &channels[ch_idx]; - channel->ch_in = in; channel->ch_out = out; channel->ch_err = err; --- 572,599 ---- if (!success) { /* Get here when the server can't be found. */ ! ch_error(NULL, "Cannot connect to port after retry\n"); PERROR(_("E899: Cannot connect to port after retry2")); sock_close(sd); ! return NULL; } } } ! channel->ch_sock = sd; ! channel->ch_close_cb = close_cb; #ifdef FEAT_GUI ! channel_gui_register(channel); #endif ! return channel; } #if defined(CHANNEL_PIPES) || defined(PROTO) void ! channel_set_pipes(channel_T *channel, int in, int out, int err) { channel->ch_in = in; channel->ch_out = out; channel->ch_err = err; *************** *** 588,657 **** #endif void ! channel_set_job(int ch_idx, job_T *job) { ! channels[ch_idx].ch_job = job; } /* ! * Set the json mode of channel "ch_idx" to "ch_mode". */ void ! channel_set_json_mode(int ch_idx, ch_mode_T ch_mode) { ! channels[ch_idx].ch_mode = ch_mode; } /* ! * Set the read timeout of channel "ch_idx". */ void ! channel_set_timeout(int ch_idx, int timeout) { ! channels[ch_idx].ch_timeout = timeout; } /* ! * Set the callback for channel "ch_idx". */ void ! channel_set_callback(int ch_idx, char_u *callback) { ! vim_free(channels[ch_idx].ch_callback); ! channels[ch_idx].ch_callback = vim_strsave(callback); } /* ! * Set the callback for channel "ch_idx" for the response with "id". */ void ! channel_set_req_callback(int ch_idx, char_u *callback, int id) { ! cbq_T *cbhead = &channels[ch_idx].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; } } /* ! * Invoke the "callback" on channel "ch_idx". */ static void ! invoke_callback(int ch_idx, char_u *callback, typval_T *argv) { typval_T rettv; int dummy; ! argv[0].v_type = VAR_NUMBER; ! argv[0].vval.v_number = ch_idx; call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); --- 601,673 ---- #endif void ! channel_set_job(channel_T *channel, job_T *job) { ! channel->ch_job = job; } /* ! * Set the json mode of channel "channel" to "ch_mode". */ void ! channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode) { ! channel->ch_mode = ch_mode; } /* ! * Set the read timeout of channel "channel". */ void ! channel_set_timeout(channel_T *channel, int timeout) { ! channel->ch_timeout = timeout; } /* ! * Set the callback for channel "channel". */ void ! channel_set_callback(channel_T *channel, char_u *callback) { ! vim_free(channel->ch_callback); ! channel->ch_callback = vim_strsave(callback); } /* ! * Set the callback for channel "channel" for the response with "id". */ void ! channel_set_req_callback(channel_T *channel, char_u *callback, int id) { ! cbq_T *head = &channel->ch_cb_head; cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); if (item != NULL) { ! 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 "channel". */ static void ! invoke_callback(channel_T *channel, char_u *callback, typval_T *argv) { typval_T rettv; int dummy; ! 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,686 **** * Returns NULL if there is nothing. */ char_u * ! channel_get(int ch_idx) { ! readq_T *head = &channels[ch_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; } --- 684,704 ---- * Returns NULL if there is nothing. */ char_u * ! channel_get(channel_T *channel) { ! readq_T *head = &channel->ch_head; ! readq_T *node = head->rq_next; char_u *p; ! if (node == NULL) return NULL; /* dispose of the node but keep the buffer */ ! 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,759 **** * Returns the whole buffer contents concatenated. */ static char_u * ! channel_get_all(int ch_idx) { /* Concatenate everything into one buffer. * TODO: avoid multiple allocations. */ ! while (channel_collapse(ch_idx) == OK) ; ! return channel_get(ch_idx); } /* ! * Collapses the first and second buffer in the channel "ch_idx". * Returns FAIL if that is not possible. */ int ! channel_collapse(int ch_idx) { ! readq_T *head = &channels[ch_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 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) { 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) return FALSE; /* TODO: make reader work properly */ ! /* reader.js_buf = channel_peek(ch_idx); */ ! reader.js_buf = channel_get_all(ch_idx); reader.js_used = 0; reader.js_fill = NULL; /* reader.js_fill = channel_fill; */ ! reader.js_cookie = &ch_idx; ret = json_decode(&reader, &listtv, channel->ch_mode == MODE_JS ? JSON_JS : 0); if (ret == OK) --- 707,776 ---- * Returns the whole buffer contents concatenated. */ static char_u * ! channel_get_all(channel_T *channel) { /* Concatenate everything into one buffer. * TODO: avoid multiple allocations. */ ! while (channel_collapse(channel) == OK) ; ! return channel_get(channel); } /* ! * Collapses the first and second buffer in the channel "channel". * Returns FAIL if that is not possible. */ int ! channel_collapse(channel_T *channel) { ! readq_T *head = &channel->ch_head; ! readq_T *node = head->rq_next; char_u *p; ! if (node == NULL || node->rq_next == NULL) return FAIL; ! 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->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 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 "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(channel_T *channel) { js_read_T reader; typval_T listtv; jsonq_T *item; jsonq_T *head = &channel->ch_json_head; int ret; ! if (channel_peek(channel) == NULL) return FALSE; /* TODO: make reader work properly */ ! /* 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 = channel; ret = json_decode(&reader, &listtv, channel->ch_mode == MODE_JS ? JSON_JS : 0); if (ret == OK) *************** *** 772,790 **** 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; } } } --- 789,810 ---- clear_tv(&listtv); else { ! item->jq_value = alloc_tv(); ! if (item->jq_value == NULL) { vim_free(item); clear_tv(&listtv); } else { ! *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,800 **** * TODO: insert in front */ if (reader.js_buf[reader.js_used] != NUL) { ! channel_save(ch_idx, reader.js_buf + reader.js_used, (int)(reader.js_end - reader.js_buf) - reader.js_used); ret = TRUE; } --- 814,820 ---- * TODO: insert in front */ if (reader.js_buf[reader.js_used] != NUL) { ! channel_save(channel, reader.js_buf + reader.js_used, (int)(reader.js_end - reader.js_buf) - reader.js_used); ret = TRUE; } *************** *** 810,837 **** * Also frees the contained callback name. */ static void ! remove_cb_node(cbq_T *node) { ! node->prev->next = node->next; ! node->next->prev = node->prev; ! vim_free(node->callback); vim_free(node); } /* * 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. But not the one * with id ch_block_id. --- 830,869 ---- * Also frees the contained callback name. */ static void ! remove_cb_node(cbq_T *head, cbq_T *node) { ! 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->jq_value. */ static void ! remove_json_node(jsonq_T *head, jsonq_T *node) { ! 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 "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,853 **** * Return FAIL otherwise. */ static int ! channel_get_json(int ch_idx, int id, typval_T **rettv) { - channel_T *channel = &channels[ch_idx]; jsonq_T *head = &channel->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) --- 871,884 ---- * Return FAIL otherwise. */ static int ! channel_get_json(channel_T *channel, int id, typval_T **rettv) { jsonq_T *head = &channel->ch_json_head; ! jsonq_T *item = head->jq_next; ! while (item != NULL) { ! 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,876 **** || tv->vval.v_number == 0 || tv->vval.v_number != channel->ch_block_id))) { ! *rettv = item->value; ! remove_json_node(item); return OK; } ! item = item->next; } return FAIL; } /* ! * Execute a command received over channel "ch_idx". * "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) { char_u *arg; --- 886,907 ---- || tv->vval.v_number == 0 || tv->vval.v_number != channel->ch_block_id))) { ! *rettv = item->jq_value; ! remove_json_node(head, item); return OK; } ! item = item->jq_next; } return FAIL; } /* ! * 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(channel_T *channel, char_u *cmd, typval_T *arg2, typval_T *arg3) { char_u *arg; *************** *** 928,934 **** 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. */ --- 959,964 ---- *************** *** 943,948 **** --- 973,980 ---- { /* 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,957 **** } if (json != NULL) { ! channel_send(ch_idx, json, "eval"); vim_free(json); } } --- 983,989 ---- } if (json != NULL) { ! channel_send(channel, json, "eval"); vim_free(json); } } *************** *** 965,975 **** } /* ! * Invoke a callback for channel "ch_idx" if needed. * Return OK when a message was handled, there might be another one. */ static int ! may_invoke_callback(int ch_idx) { char_u *msg = NULL; typval_T *listtv = NULL; --- 997,1007 ---- } /* ! * 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(channel_T *channel) { char_u *msg = NULL; typval_T *listtv = NULL; *************** *** 977,983 **** 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) --- 1009,1014 ---- *************** *** 987,997 **** if (ch_mode != MODE_RAW) { /* Get any json message in the queue. */ ! if (channel_get_json(ch_idx, -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) return FALSE; } --- 1018,1028 ---- if (ch_mode != MODE_RAW) { /* Get any json message in the queue. */ ! if (channel_get_json(channel, -1, &listtv) == FAIL) { /* Parse readahead, return when there is still no message. */ ! channel_parse_json(channel); ! if (channel_get_json(channel, -1, &listtv) == FAIL) return FALSE; } *************** *** 1006,1027 **** /* ["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); return TRUE; } if (typetv->v_type != VAR_NUMBER) { ! ch_error(ch_idx, "Dropping message with invalid sequence number type\n"); ! clear_tv(listtv); return FALSE; } seq_nr = typetv->vval.v_number; } ! else if (channel_peek(ch_idx) == NULL) { /* nothing to read on raw channel */ return FALSE; --- 1037,1058 ---- /* ["cmd", arg] or ["cmd", arg, arg] */ if (list->lv_len == 3) arg3 = &list->lv_last->li_tv; ! 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(channel, "Dropping message with invalid sequence number type\n"); ! free_tv(listtv); return FALSE; } seq_nr = typetv->vval.v_number; } ! else if (channel_peek(channel) == NULL) { /* nothing to read on raw channel */ return FALSE; *************** *** 1034,1124 **** /* For a raw channel we don't know where the message ends, just get * everything. */ ! msg = channel_get_all(ch_idx); 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; int done = FALSE; /* invoke the one-time callback with the matching nr */ ! while (cbitem != cbhead) { ! if (cbitem->seq_nr == seq_nr) { ! ch_log(ch_idx, "Invoking one-time callback\n"); ! invoke_callback(ch_idx, cbitem->callback, argv); ! remove_cb_node(cbitem); done = TRUE; break; } ! cbitem = cbitem->next; } if (!done) ! ch_log(ch_idx, "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); } else ! ch_log(ch_idx, "Dropping message\n"); if (listtv != NULL) ! clear_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". */ int ! channel_can_write_to(int ch_idx) { ! return ch_idx >= 0 && ch_idx < channel_count ! && (channels[ch_idx].ch_sock >= 0 #ifdef CHANNEL_PIPES ! || channels[ch_idx].ch_in >= 0 #endif ); } /* ! * Return TRUE when channel "ch_idx" is open for reading or writing. ! * Also returns FALSE or invalid "ch_idx". */ int ! channel_is_open(int ch_idx) { ! return ch_idx >= 0 && ch_idx < channel_count ! && (channels[ch_idx].ch_sock >= 0 #ifdef CHANNEL_PIPES ! || channels[ch_idx].ch_in >= 0 ! || channels[ch_idx].ch_out >= 0 ! || channels[ch_idx].ch_err >= 0 #endif ); } /* ! * Close channel "ch_idx". * This does not trigger the close callback. */ void ! channel_close(int ch_idx) { ! channel_T *channel = &channels[ch_idx]; ! jsonq_T *jhead; ! cbq_T *cbhead; if (channel->ch_sock >= 0) { --- 1065,1164 ---- /* For a raw channel we don't know where the message ends, just get * everything. */ ! msg = channel_get_all(channel); argv[1].v_type = VAR_STRING; argv[1].vval.v_string = msg; } if (seq_nr > 0) { ! 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 (item != NULL) { ! if (item->cq_seq_nr == seq_nr) { ! ch_log(channel, "Invoking one-time callback\n"); ! invoke_callback(channel, item->cq_callback, argv); ! remove_cb_node(head, item); done = TRUE; break; } ! item = item->cq_next; } if (!done) ! ch_log(channel, "Dropping message without callback\n"); } else if (channel->ch_callback != NULL) { /* invoke the channel callback */ ! ch_log(channel, "Invoking channel callback\n"); ! invoke_callback(channel, channel->ch_callback, argv); } else ! ch_log(channel, "Dropping message\n"); if (listtv != NULL) ! free_tv(listtv); vim_free(msg); return TRUE; } /* ! * Return TRUE when channel "channel" is open for writing to. ! * Also returns FALSE or invalid "channel". */ int ! channel_can_write_to(channel_T *channel) { ! return channel != NULL && (channel->ch_sock >= 0 #ifdef CHANNEL_PIPES ! || channel->ch_in >= 0 #endif ); } /* ! * Return TRUE when channel "channel" is open for reading or writing. ! * Also returns FALSE for invalid "channel". */ int ! channel_is_open(channel_T *channel) { ! return channel != NULL && (channel->ch_sock >= 0 #ifdef CHANNEL_PIPES ! || channel->ch_in >= 0 ! || channel->ch_out >= 0 ! || channel->ch_err >= 0 #endif ); } /* ! * 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(channel_T *channel) { ! ch_log(channel, "Closing channel"); if (channel->ch_sock >= 0) { *************** *** 1126,1150 **** channel->ch_sock = -1; channel->ch_close_cb = NULL; #ifdef FEAT_GUI ! channel_gui_unregister(ch_idx); #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) --- 1166,1175 ---- channel->ch_sock = -1; channel->ch_close_cb = NULL; #ifdef FEAT_GUI ! channel_gui_unregister(channel); #endif vim_free(channel->ch_callback); channel->ch_callback = NULL; } #if defined(CHANNEL_PIPES) if (channel->ch_in >= 0) *************** *** 1163,1201 **** channel->ch_err = -1; } #endif } /* ! * Store "buf[len]" on channel "ch_idx". * Returns OK or FAIL. */ int ! channel_save(int ch_idx, char_u *buf, int len) { readq_T *node; ! readq_T *head = &channels[ch_idx].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) { vim_free(node); return FAIL; /* out of memory */ } ! mch_memmove(node->buffer, buf, (size_t)len); ! node->buffer[len] = NUL; ! /* insert node at tail of queue */ ! node->next = head; ! node->prev = head->prev; ! head->prev->next = node; ! head->prev = node; if (log_fd != NULL) { ! ch_log_lead("RECV ", ch_idx); fprintf(log_fd, "'"); if (fwrite(buf, len, 1, log_fd) != 1) return FAIL; --- 1188,1230 ---- channel->ch_err = -1; } #endif + channel_clear(channel); } /* ! * Store "buf[len]" on channel "channel". * Returns OK or FAIL. */ int ! channel_save(channel_T *channel, char_u *buf, int len) { readq_T *node; ! readq_T *head = &channel->ch_head; node = (readq_T *)alloc(sizeof(readq_T)); if (node == NULL) return FAIL; /* out of memory */ ! node->rq_buffer = alloc(len + 1); ! if (node->rq_buffer == NULL) { vim_free(node); return FAIL; /* out of memory */ } ! mch_memmove(node->rq_buffer, buf, (size_t)len); ! node->rq_buffer[len] = NUL; ! /* 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 ", channel); fprintf(log_fd, "'"); if (fwrite(buf, len, 1, log_fd) != 1) return FAIL; *************** *** 1209,1248 **** * Returns NULL if there is nothing. */ char_u * ! channel_peek(int ch_idx) { ! readq_T *head = &channels[ch_idx].ch_head; ! if (head->next == head || head->next == NULL) return NULL; ! return head->next->buffer; } /* ! * Clear the read buffer on channel "ch_idx". */ void ! channel_clear(int ch_idx) { ! readq_T *head = &channels[ch_idx].ch_head; ! readq_T *node = head->next; ! readq_T *next; ! while (node != NULL && node != head) { ! next = node->next; ! vim_free(node->buffer); ! vim_free(node); ! if (next == head) ! { ! head->next = head; ! head->prev = head; ! break; ! } ! node = next; } } /* Sent when the channel is found closed when reading. */ #define DETACH_MSG "\"DETACH\"\n" --- 1238,1286 ---- * Returns NULL if there is nothing. */ char_u * ! channel_peek(channel_T *channel) { ! readq_T *head = &channel->ch_head; ! if (head->rq_next == NULL) return NULL; ! return head->rq_next->rq_buffer; } /* ! * Clear the read buffer on channel "channel". */ void ! channel_clear(channel_T *channel) { ! jsonq_T *json_head = &channel->ch_json_head; ! cbq_T *cb_head = &channel->ch_cb_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) { ! 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,1261 **** * Always returns OK for FEAT_GUI_W32. */ static int ! channel_wait(int ch_idx, int fd, int timeout) { #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32) struct timeval tval; --- 1293,1299 ---- * Always returns OK for FEAT_GUI_W32. */ static int ! channel_wait(channel_T *channel, int fd, int timeout) { #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32) struct timeval tval; *************** *** 1263,1269 **** int ret; if (timeout > 0) ! ch_logn(ch_idx, "Waiting for %d msec\n", timeout); FD_ZERO(&rfds); FD_SET(fd, &rfds); tval.tv_sec = timeout / 1000; --- 1301,1307 ---- int ret; if (timeout > 0) ! ch_logn(channel, "Waiting for %d msec\n", timeout); FD_ZERO(&rfds); FD_SET(fd, &rfds); tval.tv_sec = timeout / 1000; *************** *** 1277,1283 **** # endif if (ret <= 0) { ! ch_log(ch_idx, "Nothing to read\n"); return FAIL; } break; --- 1315,1321 ---- # endif if (ret <= 0) { ! ch_log(channel, "Nothing to read\n"); return FAIL; } break; *************** *** 1287,1298 **** struct pollfd fds; if (timeout > 0) ! ch_logn(ch_idx, "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"); return FAIL; } # endif --- 1325,1336 ---- struct pollfd fds; if (timeout > 0) ! ch_logn(channel, "Waiting for %d msec\n", timeout); fds.fd = fd; fds.events = POLLIN; if (poll(&fds, 1, timeout) <= 0) { ! ch_log(channel, "Nothing to read\n"); return FAIL; } # endif *************** *** 1315,1324 **** * Get the file descriptor to read from, either the socket or stdout. */ static int ! get_read_fd(int ch_idx, int use_stderr) { - channel_T *channel = &channels[ch_idx]; - if (channel->ch_sock >= 0) return channel->ch_sock; #if defined(CHANNEL_PIPES) --- 1353,1360 ---- * Get the file descriptor to read from, either the socket or stdout. */ static int ! get_read_fd(channel_T *channel, int use_stderr) { if (channel->ch_sock >= 0) return channel->ch_sock; #if defined(CHANNEL_PIPES) *************** *** 1327,1351 **** if (use_stderr && channel->ch_err >= 0) return channel->ch_err; #endif ! ch_error(ch_idx, "channel_read() called while socket is closed\n"); return -1; } /* ! * Read from channel "ch_idx" 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_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); if (fd < 0) return; use_socket = channel->ch_sock >= 0; --- 1363,1386 ---- if (use_stderr && channel->ch_err >= 0) return channel->ch_err; #endif ! ch_error(channel, "channel_read() called while socket is closed\n"); return -1; } /* ! * Read from channel "channel" for as long as there is something to read. * The data is put in the read queue. */ void ! channel_read(channel_T *channel, int use_stderr, char *func) { static char_u *buf = NULL; int len = 0; int readlen = 0; int fd; int use_socket = FALSE; ! fd = get_read_fd(channel, use_stderr); if (fd < 0) return; use_socket = channel->ch_sock >= 0; *************** *** 1363,1369 **** * MAXMSGSIZE long. */ for (;;) { ! if (channel_wait(ch_idx, fd, 0) == FAIL) break; if (use_socket) len = sock_read(fd, buf, MAXMSGSIZE); --- 1398,1404 ---- * MAXMSGSIZE long. */ for (;;) { ! if (channel_wait(channel, fd, 0) == FAIL) break; if (use_socket) len = sock_read(fd, buf, MAXMSGSIZE); *************** *** 1373,1379 **** break; /* error or nothing more to read */ /* Store the read message in the queue. */ ! channel_save(ch_idx, buf, len); readlen += len; if (len < MAXMSGSIZE) break; /* did read everything that's available */ --- 1408,1414 ---- break; /* error or nothing more to read */ /* Store the read message in the queue. */ ! channel_save(channel, buf, len); readlen += len; if (len < MAXMSGSIZE) break; /* did read everything that's available */ *************** *** 1402,1413 **** * -> 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)); if (use_socket) { ! channel_close(ch_idx); if (channel->ch_close_cb != NULL) (*channel->ch_close_cb)(); } --- 1437,1448 ---- * -> gui event loop or select loop * -> channel_read() */ ! ch_errors(channel, "%s(): Cannot read\n", func); ! channel_save(channel, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG)); if (use_socket) { ! channel_close(channel); if (channel->ch_close_cb != NULL) (*channel->ch_close_cb)(); } *************** *** 1421,1427 **** if (len < 0) { ! ch_error(ch_idx, "channel_read(): cannot read from channel\n"); PERROR(_("E896: read from channel")); } } --- 1456,1462 ---- if (len < 0) { ! ch_error(channel, "channel_read(): cannot read from channel\n"); PERROR(_("E896: read from channel")); } } *************** *** 1434,1485 **** } /* ! * Read from raw channel "ch_idx". Blocks until there is something to read or * the timeout expires. * Returns what was read in allocated memory. * Returns NULL in case of error or timeout. */ char_u * ! channel_read_block(int ch_idx) { ! ch_log(ch_idx, "Reading raw\n"); ! if (channel_peek(ch_idx) == NULL) { ! int fd = get_read_fd(ch_idx, FALSE); ! ch_log(ch_idx, "No readahead\n"); /* Wait for up to the channel timeout. */ ! if (fd < 0 || channel_wait(ch_idx, fd, ! channels[ch_idx].ch_timeout) == FAIL) return NULL; ! channel_read(ch_idx, FALSE, "channel_read_block"); } /* TODO: only get the first message */ ! ch_log(ch_idx, "Returning readahead\n"); ! return channel_get_all(ch_idx); } /* ! * Read one JSON message from channel "ch_idx" with ID "id" 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) { int more; - channel_T *channel = &channels[ch_idx]; int fd; ! ch_log(ch_idx, "Reading JSON\n"); channel->ch_block_id = id; for (;;) { ! more = channel_parse_json(ch_idx); /* search for messsage "id" */ ! if (channel_get_json(ch_idx, id, rettv) == OK) { channel->ch_block_id = 0; return OK; --- 1469,1518 ---- } /* ! * 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(channel_T *channel) { ! ch_log(channel, "Reading raw\n"); ! if (channel_peek(channel) == NULL) { ! int fd = get_read_fd(channel, FALSE); ! ch_log(channel, "No readahead\n"); /* Wait for up to the channel timeout. */ ! if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) == FAIL) return NULL; ! channel_read(channel, FALSE, "channel_read_block"); } /* TODO: only get the first message */ ! ch_log(channel, "Returning readahead\n"); ! return channel_get_all(channel); } /* ! * 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(channel_T *channel, int id, typval_T **rettv) { int more; int fd; ! ch_log(channel, "Reading JSON\n"); channel->ch_block_id = id; for (;;) { ! more = channel_parse_json(channel); /* search for messsage "id" */ ! if (channel_get_json(channel, id, rettv) == OK) { channel->ch_block_id = 0; return OK; *************** *** 1493,1502 **** 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) break; ! channel_read(ch_idx, FALSE, "channel_read_json_block"); } } channel->ch_block_id = 0; --- 1526,1536 ---- continue; /* Wait for up to the channel timeout. */ ! fd = get_read_fd(channel, FALSE); ! if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) ! == FAIL) break; ! channel_read(channel, FALSE, "channel_read_json_block"); } } channel->ch_block_id = 0; *************** *** 1505,1540 **** # if defined(WIN32) || defined(PROTO) /* ! * Lookup the channel index from the socket. ! * Returns -1 when the socket isn't found. */ ! int ! channel_fd2idx(sock_T fd) { ! int i; if (fd >= 0) ! for (i = 0; i < channel_count; ++i) ! if (channels[i].ch_sock == fd # if defined(CHANNEL_PIPES) ! || channels[i].ch_out == fd ! || channels[i].ch_err == fd # endif ) ! return i; ! return -1; } # endif /* ! * Write "buf" (NUL terminated string) to channel "ch_idx". * 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_T *channel = &channels[ch_idx]; int len = (int)STRLEN(buf); int res; int fd = -1; --- 1539,1574 ---- # if defined(WIN32) || defined(PROTO) /* ! * Lookup the channel from the socket. ! * Returns NULL when the socket isn't found. */ ! channel_T * ! channel_fd2channel(sock_T fd) { ! channel_T *channel; if (fd >= 0) ! for (channel = first_channel; channel != NULL; ! channel = channel->ch_next) ! if (channel->ch_sock == fd # if defined(CHANNEL_PIPES) ! || channel->ch_out == fd ! || channel->ch_err == fd # endif ) ! return channel; ! return NULL; } # endif /* ! * 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(channel_T *channel, char_u *buf, char *fun) { int len = (int)STRLEN(buf); int res; int fd = -1; *************** *** 1553,1559 **** { if (!channel->ch_error && fun != NULL) { ! ch_errors(ch_idx, "%s(): write while not connected\n", fun); EMSG2("E630: %s(): write while not connected", fun); } channel->ch_error = TRUE; --- 1587,1593 ---- { if (!channel->ch_error && fun != NULL) { ! ch_errors(channel, "%s(): write while not connected\n", fun); EMSG2("E630: %s(): write while not connected", fun); } channel->ch_error = TRUE; *************** *** 1562,1568 **** if (log_fd != NULL) { ! ch_log_lead("SEND ", ch_idx); fprintf(log_fd, "'"); ignored = fwrite(buf, len, 1, log_fd); fprintf(log_fd, "'\n"); --- 1596,1602 ---- if (log_fd != NULL) { ! ch_log_lead("SEND ", channel); fprintf(log_fd, "'"); ignored = fwrite(buf, len, 1, log_fd); fprintf(log_fd, "'\n"); *************** *** 1577,1583 **** { if (!channel->ch_error && fun != NULL) { ! ch_errors(ch_idx, "%s(): write failed\n", fun); EMSG2("E631: %s(): write failed", fun); } channel->ch_error = TRUE; --- 1611,1617 ---- { if (!channel->ch_error && fun != NULL) { ! ch_errors(channel, "%s(): write failed\n", fun); EMSG2("E631: %s(): write failed", fun); } channel->ch_error = TRUE; *************** *** 1598,1638 **** channel_poll_setup(int nfd_in, void *fds_in) { int nfd = nfd_in; ! int i; struct pollfd *fds = fds_in; ! for (i = 0; i < channel_count; ++i) { ! if (channels[i].ch_sock >= 0) { ! channels[i].ch_sock_idx = nfd; ! fds[nfd].fd = channels[i].ch_sock; fds[nfd].events = POLLIN; nfd++; } else ! channels[i].ch_sock_idx = -1; # ifdef CHANNEL_PIPES ! if (channels[i].ch_out >= 0) { ! channels[i].ch_out_idx = nfd; ! fds[nfd].fd = channels[i].ch_out; fds[nfd].events = POLLIN; nfd++; } else ! channels[i].ch_out_idx = -1; ! if (channels[i].ch_err >= 0) { ! channels[i].ch_err_idx = nfd; ! fds[nfd].fd = channels[i].ch_err; fds[nfd].events = POLLIN; nfd++; } else ! channels[i].ch_err_idx = -1; # endif } --- 1632,1672 ---- channel_poll_setup(int nfd_in, void *fds_in) { int nfd = nfd_in; ! channel_T *channel; struct pollfd *fds = fds_in; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) { ! if (channel->ch_sock >= 0) { ! channel->ch_sock_idx = nfd; ! fds[nfd].fd = channel->ch_sock; fds[nfd].events = POLLIN; nfd++; } else ! channel->ch_sock_idx = -1; # ifdef CHANNEL_PIPES ! if (channel->ch_out >= 0) { ! channel->ch_out_idx = nfd; ! fds[nfd].fd = channel->ch_out; fds[nfd].events = POLLIN; nfd++; } else ! channel->ch_out_idx = -1; ! if (channel->ch_err >= 0) { ! channel->ch_err_idx = nfd; ! fds[nfd].fd = channel->ch_err; fds[nfd].events = POLLIN; nfd++; } else ! channel->ch_err_idx = -1; # endif } *************** *** 1646,1673 **** channel_poll_check(int ret_in, void *fds_in) { int ret = ret_in; ! int i; struct pollfd *fds = fds_in; ! for (i = 0; i < channel_count; ++i) { ! if (ret > 0 && channels[i].ch_sock_idx != -1 ! && fds[channels[i].ch_sock_idx].revents & POLLIN) { ! channel_read(i, 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) { ! channel_read(i, FALSE, "channel_poll_check"); --ret; } ! if (ret > 0 && channels[i].ch_err_idx != -1 ! && fds[channels[i].ch_err_idx].revents & POLLIN) { ! channel_read(i, TRUE, "channel_poll_check"); --ret; } # endif --- 1680,1707 ---- channel_poll_check(int ret_in, void *fds_in) { int ret = ret_in; ! channel_T *channel; struct pollfd *fds = fds_in; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) { ! if (ret > 0 && channel->ch_sock_idx != -1 ! && fds[channel->ch_sock_idx].revents & POLLIN) { ! channel_read(channel, FALSE, "channel_poll_check"); --ret; } # ifdef CHANNEL_PIPES ! if (ret > 0 && channel->ch_out_idx != -1 ! && fds[channel->ch_out_idx].revents & POLLIN) { ! channel_read(channel, FALSE, "channel_poll_check"); --ret; } ! if (ret > 0 && channel->ch_err_idx != -1 ! && fds[channel->ch_err_idx].revents & POLLIN) { ! channel_read(channel, TRUE, "channel_poll_check"); --ret; } # endif *************** *** 1685,1713 **** channel_select_setup(int maxfd_in, void *rfds_in) { int maxfd = maxfd_in; ! int i; fd_set *rfds = rfds_in; ! for (i = 0; i < channel_count; ++i) { ! if (channels[i].ch_sock >= 0) { ! FD_SET(channels[i].ch_sock, rfds); ! if (maxfd < channels[i].ch_sock) ! maxfd = channels[i].ch_sock; } # ifdef CHANNEL_PIPES ! if (channels[i].ch_out >= 0) { ! FD_SET(channels[i].ch_out, rfds); ! if (maxfd < channels[i].ch_out) ! maxfd = channels[i].ch_out; } ! if (channels[i].ch_err >= 0) { ! FD_SET(channels[i].ch_err, rfds); ! if (maxfd < channels[i].ch_err) ! maxfd = channels[i].ch_err; } # endif } --- 1719,1747 ---- channel_select_setup(int maxfd_in, void *rfds_in) { int maxfd = maxfd_in; ! channel_T *channel; fd_set *rfds = rfds_in; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) { ! if (channel->ch_sock >= 0) { ! FD_SET(channel->ch_sock, rfds); ! if (maxfd < channel->ch_sock) ! maxfd = channel->ch_sock; } # ifdef CHANNEL_PIPES ! if (channel->ch_out >= 0) { ! FD_SET(channel->ch_out, rfds); ! if (maxfd < channel->ch_out) ! maxfd = channel->ch_out; } ! if (channel->ch_err >= 0) { ! FD_SET(channel->ch_err, rfds); ! if (maxfd < channel->ch_err) ! maxfd = channel->ch_err; } # endif } *************** *** 1722,1749 **** channel_select_check(int ret_in, void *rfds_in) { int ret = ret_in; ! int i; fd_set *rfds = rfds_in; ! for (i = 0; i < channel_count; ++i) { ! if (ret > 0 && channels[i].ch_sock >= 0 ! && FD_ISSET(channels[i].ch_sock, rfds)) { ! channel_read(i, FALSE, "channel_select_check"); --ret; } # ifdef CHANNEL_PIPES ! if (ret > 0 && channels[i].ch_out >= 0 ! && FD_ISSET(channels[i].ch_out, rfds)) { ! channel_read(i, FALSE, "channel_select_check"); --ret; } ! if (ret > 0 && channels[i].ch_err >= 0 ! && FD_ISSET(channels[i].ch_err, rfds)) { ! channel_read(i, TRUE, "channel_select_check"); --ret; } # endif --- 1756,1783 ---- channel_select_check(int ret_in, void *rfds_in) { int ret = ret_in; ! channel_T *channel; fd_set *rfds = rfds_in; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) { ! if (ret > 0 && channel->ch_sock >= 0 ! && FD_ISSET(channel->ch_sock, rfds)) { ! channel_read(channel, FALSE, "channel_select_check"); --ret; } # ifdef CHANNEL_PIPES ! if (ret > 0 && channel->ch_out >= 0 ! && FD_ISSET(channel->ch_out, rfds)) { ! channel_read(channel, FALSE, "channel_select_check"); --ret; } ! if (ret > 0 && channel->ch_err >= 0 ! && FD_ISSET(channel->ch_err, rfds)) { ! channel_read(channel, TRUE, "channel_select_check"); --ret; } # endif *************** *** 1761,1773 **** int channel_parse_messages(void) { ! int i; int ret = FALSE; ! for (i = 0; i < channel_count; ++i) ! while (may_invoke_callback(i) == OK) { ! i = 0; /* start over */ ret = TRUE; } return ret; --- 1795,1807 ---- int channel_parse_messages(void) { ! channel_T *channel; int ret = FALSE; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) ! while (may_invoke_callback(channel) == OK) { ! channel = first_channel; /* start over */ ret = TRUE; } return ret; *************** *** 1779,1817 **** int set_ref_in_channel(int copyID) { ! int i; ! int abort = FALSE; ! for (i = 0; i < channel_count; ++i) { ! jsonq_T *head = &channels[i].ch_json_head; ! jsonq_T *item = head->next; ! while (item != head) { ! list_T *l = item->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; } } return abort; } /* ! * Return the mode of channel "ch_idx". ! * If "ch_idx" is invalid returns MODE_JSON. */ ch_mode_T ! channel_get_mode(int ch_idx) { ! if (ch_idx < 0 || ch_idx >= channel_count) return MODE_JSON; ! return channels[ch_idx].ch_mode; } #endif /* FEAT_CHANNEL */ --- 1813,1851 ---- int set_ref_in_channel(int copyID) { ! int abort = FALSE; ! channel_T *channel; ! for (channel = first_channel; channel != NULL; channel = channel->ch_next) { ! jsonq_T *head = &channel->ch_json_head; ! jsonq_T *item = head->jq_next; ! while (item != NULL) { ! 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->jq_next; } } return abort; } /* ! * Return the mode of channel "channel". ! * If "channel" is invalid returns MODE_JSON. */ ch_mode_T ! channel_get_mode(channel_T *channel) { ! if (channel == NULL) return MODE_JSON; ! return channel->ch_mode; } #endif /* FEAT_CHANNEL */ *** ../vim-7.4.1314/src/misc2.c 2016-01-30 23:20:28.523141073 +0100 --- src/misc2.c 2016-02-13 19:25:10.126930949 +0100 *************** *** 1139,1144 **** --- 1139,1147 ---- # 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. */ *** ../vim-7.4.1314/src/eval.c 2016-02-13 17:04:08.422819059 +0100 --- src/eval.c 2016-02-13 23:05:57.455884275 +0100 *************** *** 509,514 **** --- 509,515 ---- 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,3089 **** --- 3085,3091 ---- case VAR_FUNC: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; case VAR_LIST: *************** *** 3863,3868 **** --- 3865,3871 ---- case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; case VAR_LIST: *************** *** 5359,5364 **** --- 5362,5368 ---- #endif case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: if (verbose) EMSG(_("E909: Cannot index a special variable")); return FAIL; *************** *** 5471,5476 **** --- 5475,5481 ---- case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; /* not evaluating, skipping over subscript */ case VAR_NUMBER: *************** *** 6224,6229 **** --- 6229,6238 ---- #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,7730 **** return OK; } #ifdef FEAT_JOB static void job_free(job_T *job) { ! if (job->jv_channel >= 0) ! channel_close(job->jv_channel); mch_clear_job(job); vim_free(job); } --- 7728,7753 ---- 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 != 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,7754 **** job = (job_T *)alloc_clear(sizeof(job_T)); if (job != NULL) - { job->jv_refcount = 1; - } return job; } --- 7769,7775 ---- *************** *** 7850,7855 **** --- 7871,7877 ---- case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: + case VAR_CHANNEL: *tofree = NULL; r = get_tv_string_buf(tv, numbuf); break; *************** *** 7906,7911 **** --- 7928,7934 ---- case VAR_DICT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: case VAR_UNKNOWN: break; } *************** *** 8093,8098 **** --- 8116,8122 ---- {"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,9807 **** #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_channel_arg(typval_T *tv) { ! int ch_idx; ! if (tv->v_type != VAR_NUMBER) { 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; } /* --- 9805,9831 ---- #ifdef FEAT_CHANNEL /* ! * Get the channel from the argument. ! * Returns NULL if the handle is invalid. */ ! static channel_T * get_channel_arg(typval_T *tv) { ! channel_T *channel; ! if (tv->v_type != VAR_CHANNEL) { EMSG2(_(e_invarg2), get_tv_string(tv)); ! 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,9819 **** 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); } /* --- 9834,9843 ---- static void f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) { ! channel_T *channel = get_channel_arg(&argvars[0]); ! if (channel != NULL) ! channel_close(channel); } /* *************** *** 9873,9882 **** int waittime = 0; int timeout = 2000; ch_mode_T ch_mode = MODE_JSON; ! int ch_idx; /* default: fail */ ! rettv->vval.v_number = -1; address = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN --- 9897,9907 ---- int waittime = 0; int timeout = 2000; ch_mode_T ch_mode = MODE_JSON; ! channel_T *channel; /* default: fail */ ! rettv->v_type = VAR_CHANNEL; ! rettv->vval.v_channel = NULL; address = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN *************** *** 9936,9950 **** 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); if (callback != NULL && *callback != NUL) ! channel_set_callback(ch_idx, callback); } ! rettv->vval.v_number = ch_idx; } /* --- 9961,9975 ---- return; } ! 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(channel, callback); } ! rettv->vval.v_channel = channel; } /* *************** *** 9953,10005 **** static void f_ch_readraw(typval_T *argvars, typval_T *rettv) { ! int ch_idx; /* 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); } /* * common for "sendexpr()" and "sendraw()" ! * Returns the channel index if the caller should read the response. ! * Otherwise returns -1. */ ! static int send_common(typval_T *argvars, char_u *text, int id, char *fun) { ! int ch_idx; char_u *callback = NULL; ! ch_idx = get_channel_arg(&argvars[0]); ! if (ch_idx < 0) ! { ! EMSG(_(e_invarg)); ! return -1; ! } if (argvars[2].v_type != VAR_UNKNOWN) { callback = get_callback(&argvars[2]); if (callback == NULL) ! return -1; } /* 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; } /* --- 9978,10042 ---- static void f_ch_readraw(typval_T *argvars, typval_T *rettv) { ! channel_T *channel; /* return an empty string by default */ rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; ! 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 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) { ! channel_T *channel; char_u *callback = NULL; ! 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 NULL; } /* Set the callback. An empty callback means no callback and not reading * the response. */ if (callback != NULL && *callback != NUL) ! channel_set_req_callback(channel, callback, id); ! if (channel_send(channel, text, fun) == OK && callback == NULL) ! return channel; ! return NULL; } /* *************** *** 10010,10016 **** { char_u *text; typval_T *listtv; ! int ch_idx; int id; ch_mode_T ch_mode; --- 10047,10053 ---- { char_u *text; typval_T *listtv; ! channel_T *channel; int id; ch_mode_T ch_mode; *************** *** 10018,10031 **** 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); if (ch_mode == MODE_RAW) { EMSG(_("E912: cannot use ch_sendexpr() with a raw channel")); --- 10055,10065 ---- rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; ! 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,10048 **** if (text == NULL) return; ! ch_idx = send_common(argvars, text, id, "sendexpr"); vim_free(text); ! if (ch_idx >= 0) { ! if (channel_read_json_block(ch_idx, id, &listtv) == OK) { list_T *list = listtv->vval.v_list; --- 10072,10082 ---- if (text == NULL) return; ! channel = send_common(argvars, text, id, "sendexpr"); vim_free(text); ! if (channel != NULL) { ! if (channel_read_json_block(channel, id, &listtv) == OK) { list_T *list = listtv->vval.v_list; *************** *** 10050,10056 **** * avoid the value being freed. */ *rettv = list->lv_last->li_tv; list->lv_last->li_tv.v_type = VAR_NUMBER; ! clear_tv(listtv); } } } --- 10084,10090 ---- * avoid the value being freed. */ *rettv = list->lv_last->li_tv; list->lv_last->li_tv.v_type = VAR_NUMBER; ! free_tv(listtv); } } } *************** *** 10063,10078 **** { char_u buf[NUMBUFLEN]; char_u *text; ! int ch_idx; /* 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); } #endif --- 10097,10112 ---- { char_u buf[NUMBUFLEN]; char_u *text; ! 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); ! channel = send_common(argvars, text, 0, "sendraw"); ! if (channel != NULL) ! rettv->vval.v_string = channel_read_block(channel); } #endif *************** *** 10708,10714 **** case VAR_JOB: #ifdef FEAT_JOB ! n = argvars[0].vval.v_job->jv_status != JOB_STARTED; break; #endif case VAR_UNKNOWN: --- 10742,10755 ---- case VAR_JOB: #ifdef FEAT_JOB ! 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,14373 **** { job_T *job = argvars[0].vval.v_job; ! rettv->v_type = VAR_NUMBER; ! rettv->vval.v_number = job->jv_channel; } } --- 14407,14416 ---- { job_T *job = argvars[0].vval.v_job; ! rettv->v_type = VAR_CHANNEL; ! rettv->vval.v_channel = job->jv_channel; ! if (job->jv_channel != NULL) ! ++job->jv_channel->ch_refcount; } } *************** *** 14659,14664 **** --- 14702,14708 ---- case VAR_FLOAT: case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: EMSG(_("E701: Invalid type for len()")); break; } *************** *** 20040,20046 **** else n = 7; break; ! case VAR_JOB: n = 8; break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_type(UNKNOWN)"); n = -1; --- 20084,20091 ---- else n = 7; 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,21417 **** --- 21457,21467 ---- 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,21467 **** --- 21512,21522 ---- 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,21536 **** --- 21586,21596 ---- 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,21577 **** --- 21632,21642 ---- 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,21712 **** --- 21772,21789 ---- } #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,22342 **** --- 22414,22425 ---- ++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,22409 **** --- 22487,22493 ---- case VAR_FUNC: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: copy_tv(from, to); break; case VAR_LIST: *************** *** 25076,25081 **** --- 25160,25166 ---- case VAR_UNKNOWN: case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: continue; } fprintf(fp, "!%s\t%s\t", this_var->di_key, s); *** ../vim-7.4.1314/src/if_python.c 2016-02-07 19:25:46.768454441 +0100 --- src/if_python.c 2016-02-13 21:09:31.133460811 +0100 *************** *** 1561,1566 **** --- 1561,1567 ---- case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; } } *** ../vim-7.4.1314/src/if_python3.c 2016-02-07 19:25:46.768454441 +0100 --- src/if_python3.c 2016-02-13 21:09:50.937253226 +0100 *************** *** 1654,1659 **** --- 1654,1660 ---- case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: + case VAR_CHANNEL: break; } } *** ../vim-7.4.1314/src/json.c 2016-02-07 19:16:24.230303817 +0100 --- src/json.c 2016-02-13 21:10:20.928938856 +0100 *************** *** 183,188 **** --- 183,189 ---- case VAR_FUNC: case VAR_JOB: + case VAR_CHANNEL: /* no JSON equivalent TODO: better error */ EMSG(_(e_invarg)); return FAIL; *** ../vim-7.4.1314/src/gui_w48.c 2016-02-13 18:40:26.746849444 +0100 --- src/gui_w48.c 2016-02-13 22:00:52.500999372 +0100 *************** *** 1780,1793 **** #ifdef FEAT_CHANNEL if (msg.message == WM_NETBEANS) { ! int channel_idx = channel_fd2idx((sock_T)msg.wParam); ! if (channel_idx >= 0) { /* Disable error messages, they can mess up the display and throw * an exception. */ ++emsg_off; ! channel_read(channel_idx, FALSE, "process_message"); --emsg_off; } return; --- 1780,1793 ---- #ifdef FEAT_CHANNEL if (msg.message == WM_NETBEANS) { ! 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, FALSE, "process_message"); --emsg_off; } return; *** ../vim-7.4.1314/src/netbeans.c 2016-02-05 22:36:09.741738101 +0100 --- src/netbeans.c 2016-02-13 22:30:03.474555922 +0100 *************** *** 63,70 **** 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; static int r_cmdno; /* current command number for reply */ static int dosetvisible = FALSE; --- 63,70 ---- static void nb_send(char *buf, char *fun); static void nb_free(void); ! #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,91 **** static void nb_channel_closed(void) { ! nb_channel_idx = -1; } /* --- 85,91 ---- static void nb_channel_closed(void) { ! nb_channel = NULL; } /* *************** *** 98,107 **** if (NETBEANS_OPEN) { netbeans_send_disconnect(); ! if (nb_channel_idx >= 0) /* Close the socket and remove the input handlers. */ ! channel_close(nb_channel_idx); ! nb_channel_idx = -1; } #ifdef FEAT_BEVAL --- 98,107 ---- if (NETBEANS_OPEN) { netbeans_send_disconnect(); ! if (nb_channel != NULL) /* Close the socket and remove the input handlers. */ ! channel_close(nb_channel); ! nb_channel = NULL; } #ifdef FEAT_BEVAL *************** *** 213,220 **** 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) { /* success */ # ifdef FEAT_BEVAL --- 213,220 ---- if (hostname != NULL && address != NULL && password != NULL) { port = atoi(address); ! nb_channel = channel_open(hostname, port, 0, nb_channel_closed); ! if (nb_channel != NULL) { /* success */ # ifdef FEAT_BEVAL *************** *** 230,236 **** } } ! if (nb_channel_idx < 0 && doabort) getout(1); vim_free(hostname); --- 230,236 ---- } } ! if (nb_channel == NULL && doabort) getout(1); vim_free(hostname); *************** *** 383,391 **** char_u *p; int own_node; ! while (nb_channel_idx >= 0) { ! buffer = channel_peek(nb_channel_idx); if (buffer == NULL) break; /* nothing to read */ --- 383,391 ---- char_u *p; int own_node; ! while (nb_channel != NULL) { ! buffer = channel_peek(nb_channel); if (buffer == NULL) break; /* nothing to read */ *************** *** 396,402 **** /* 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) return; } else --- 396,402 ---- /* 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) == FAIL) return; } else *************** *** 409,415 **** if (*p == NUL) { own_node = TRUE; ! channel_get(nb_channel_idx); } else own_node = FALSE; --- 409,415 ---- if (*p == NUL) { own_node = TRUE; ! channel_get(nb_channel); } else own_node = FALSE; *************** *** 600,607 **** } /* free the queued netbeans commands */ ! if (nb_channel_idx >= 0) ! channel_clear(nb_channel_idx); } /* --- 600,607 ---- } /* free the queued netbeans commands */ ! if (nb_channel != NULL) ! channel_clear(nb_channel); } /* *************** *** 756,763 **** static void nb_send(char *buf, char *fun) { ! if (nb_channel_idx >= 0) ! channel_send(nb_channel_idx, (char_u *)buf, fun); } /* --- 756,763 ---- static void nb_send(char *buf, char *fun) { ! if (nb_channel != NULL) ! channel_send(nb_channel, (char_u *)buf, fun); } /* *** ../vim-7.4.1314/src/proto/channel.pro 2016-02-13 17:04:08.426819018 +0100 --- src/proto/channel.pro 2016-02-13 22:24:43.637923828 +0100 *************** *** 1,33 **** /* channel.c */ void ch_logfile(FILE *file); ! int add_channel(void); 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); 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); 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); /* vim: set ft=c : */ --- 1,36 ---- /* channel.c */ void ch_logfile(FILE *file); ! channel_T *add_channel(void); ! void channel_free(channel_T *channel); void channel_gui_register_all(void); ! 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(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(channel_T *channel); /* vim: set ft=c : */ *** ../vim-7.4.1315/src/os_unix.c 2016-02-13 17:04:08.426819018 +0100 --- src/os_unix.c 2016-02-13 22:25:41.197317646 +0100 *************** *** 5043,5049 **** int fd_in[2]; /* for stdin */ int fd_out[2]; /* for stdout */ int fd_err[2]; /* for stderr */ ! int ch_idx; /* default is to fail */ job->jv_status = JOB_FAILED; --- 5043,5049 ---- int fd_in[2]; /* for stdin */ int fd_out[2]; /* for stdout */ int fd_err[2]; /* for stderr */ ! channel_T *channel; /* default is to fail */ job->jv_status = JOB_FAILED; *************** *** 5055,5062 **** if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0)) goto failed; ! ch_idx = add_channel(); ! if (ch_idx < 0) goto failed; pid = fork(); /* maybe we should use vfork() */ --- 5055,5062 ---- if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0)) goto failed; ! channel = add_channel(); ! if (channel == NULL) goto failed; pid = fork(); /* maybe we should use vfork() */ *************** *** 5108,5121 **** /* parent */ job->jv_pid = pid; job->jv_status = JOB_STARTED; ! job->jv_channel = ch_idx; /* 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); return; --- 5108,5121 ---- /* parent */ job->jv_pid = pid; job->jv_status = JOB_STARTED; ! job->jv_channel = channel; /* child stdin, stdout and stderr */ close(fd_in[0]); close(fd_out[1]); close(fd_err[1]); ! channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); ! channel_set_job(channel, job); return; *** ../vim-7.4.1315/src/testdir/test_channel.py 2016-02-10 21:07:09.016869231 +0100 --- src/testdir/test_channel.py 2016-02-12 22:51:17.093927821 +0100 *************** *** 1,13 **** #!/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. # # This requires Python 2.6 or later. --- 1,7 ---- #!/usr/bin/python # # Server that will accept connections from a Vim channel. ! # Used by test_channel.vim. # # This requires Python 2.6 or later. *** ../vim-7.4.1315/src/testdir/test_channel.vim 2016-02-13 18:40:26.746849444 +0100 --- src/testdir/test_channel.vim 2016-02-13 23:09:19.433753983 +0100 *************** *** 83,89 **** endif endfunc - let s:responseHandle = -1 let s:responseMsg = '' func s:RequestHandler(handle, msg) let s:responseHandle = a:handle --- 83,88 ---- *************** *** 92,98 **** func s:communicate(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if handle < 0 call assert_false(1, "Can't open channel") return endif --- 91,97 ---- func s:communicate(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif *************** *** 115,128 **** " Send a request with a specific handler. call ch_sendexpr(handle, 'hello!', 's:RequestHandler') sleep 10m ! call assert_equal(handle, s:responseHandle) call assert_equal('got it', s:responseMsg) ! let s:responseHandle = -1 let s:responseMsg = '' call ch_sendexpr(handle, 'hello!', function('s:RequestHandler')) sleep 10m ! call assert_equal(handle, s:responseHandle) call assert_equal('got it', s:responseMsg) " Send an eval request that works. --- 114,135 ---- " Send a request with a specific handler. call ch_sendexpr(handle, 'hello!', 's:RequestHandler') sleep 10m ! 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) ! unlet s:responseHandle let s:responseMsg = '' call ch_sendexpr(handle, 'hello!', function('s:RequestHandler')) sleep 10m ! 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,175 **** " Test that we can open two channels. func s:two_channels(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if handle < 0 call assert_false(1, "Can't open channel") return endif --- 176,182 ---- " Test that we can open two channels. func s:two_channels(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif *************** *** 177,183 **** call assert_equal('got it', ch_sendexpr(handle, 'hello!')) let newhandle = ch_open('localhost:' . a:port, s:chopt) ! if newhandle < 0 call assert_false(1, "Can't open second channel") return endif --- 184,190 ---- call assert_equal('got it', ch_sendexpr(handle, 'hello!')) let newhandle = ch_open('localhost:' . a:port, s:chopt) ! if ch_status(newhandle) == "fail" call assert_false(1, "Can't open second channel") return endif *************** *** 197,203 **** " 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 call assert_false(1, "Can't open channel") return endif --- 204,210 ---- " Test that a server crash is handled gracefully. func s:server_crash(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif *************** *** 219,225 **** func s:channel_handler(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if handle < 0 call assert_false(1, "Can't open channel") return endif --- 226,232 ---- func s:channel_handler(port) let handle = ch_open('localhost:' . a:port, s:chopt) ! if ch_status(handle) == "fail" call assert_false(1, "Can't open channel") return endif *************** *** 247,253 **** func Test_connect_waittime() let start = reltime() let handle = ch_open('localhost:9876', s:chopt) ! if handle >= 0 " Oops, port does exists. call ch_close(handle) else --- 254,260 ---- func Test_connect_waittime() let start = reltime() let handle = ch_open('localhost:9876', s:chopt) ! if ch_status(handle) == "fail" " Oops, port does exists. call ch_close(handle) else *************** *** 257,263 **** let start = reltime() let handle = ch_open('localhost:9867', {'waittime': 2000}) ! if handle >= 0 " Oops, port does exists. call ch_close(handle) else --- 264,270 ---- let start = reltime() let handle = ch_open('localhost:9867', {'waittime': 2000}) ! if ch_status(handle) != "fail" " Oops, port does exists. call ch_close(handle) else *** ../vim-7.4.1314/src/version.c 2016-02-13 18:50:34.556532422 +0100 --- src/version.c 2016-02-13 23:15:09.982056322 +0100 *************** *** 749,750 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1315, /**/ -- hundred-and-one symptoms of being an internet addict: 244. You use more than 20 passwords. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///