# HG changeset patch # User Christian Brabandt # Date 1453848305 -3600 # Node ID 42c1a4e63d1239099c78427afe0cb9f9cc2c5f69 # Parent 3bdef7bd2244a56de3813c8e62cfd52fe2cbc8d6 commit https://github.com/vim/vim/commit/d04a020a8a8d7a438b091d49218c438880beb50c Author: Bram Moolenaar Date: Tue Jan 26 23:30:18 2016 +0100 patch 7.4.1182 Problem: Still socket code intertwined with netbeans. Solution: Move code from netbeans.c to channel.c diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -14,17 +14,106 @@ #if defined(FEAT_CHANNEL) || defined(PROTO) +/* + * Change the zero to 1 to enable debugging. + * This will write a file "channel_debug.log". + */ +#if 0 +# define CHERROR(fmt, arg) cherror(fmt, arg) +# define CHLOG(idx, send, buf) chlog(idx, send, buf) +# define CHFILE "channel_debug.log" + +static void cherror(char *fmt, char *arg); +static void chlog(int send, char_u *buf); +#else +# define CHERROR(fmt, arg) +# define CHLOG(idx, send, buf) +#endif + +/* TRUE when netbeans is running with a GUI. */ +#ifdef FEAT_GUI +# define CH_HAS_GUI (gui.in_use || gui.starting) +#endif + +/* Note: when making changes here also adjust configure.in. */ +#ifdef WIN32 +/* WinSock API is separated from C API, thus we can't use read(), write(), + * errno... */ +# define SOCK_ERRNO errno = WSAGetLastError() +# undef ECONNREFUSED +# define ECONNREFUSED WSAECONNREFUSED +# ifdef EINTR +# undef EINTR +# endif +# define EINTR WSAEINTR +# define sock_write(sd, buf, len) send(sd, buf, len, 0) +# define sock_read(sd, buf, len) recv(sd, buf, len, 0) +# define sock_close(sd) closesocket(sd) +# define sleep(t) Sleep(t*1000) /* WinAPI Sleep() accepts milliseconds */ +#else +# include +# include + +# include +# ifdef HAVE_LIBGEN_H +# include +# endif +# define SOCK_ERRNO +# define sock_write(sd, buf, len) write(sd, buf, len) +# define sock_read(sd, buf, len) read(sd, buf, len) +# define sock_close(sd) close(sd) +#endif + +#ifdef FEAT_GUI_W32 +extern HWND s_hwnd; /* Gvim's Window handle */ +#endif + +struct readqueue +{ + char_u *buffer; + struct readqueue *next; + struct readqueue *prev; +}; +typedef struct readqueue queue_T; + typedef struct { - sock_T ch_fd; - int ch_idx; + sock_T ch_fd; /* the socket, -1 for a closed channel */ + int ch_idx; /* used by channel_poll_setup() */ + queue_T ch_head; /* dummy node, header for circular queue */ + + int ch_error; /* When TRUE an error was reported. Avoids giving + * pages full of error messages when the other side + * has exited, only mention the first error until the + * connection works again. */ +#ifdef FEAT_GUI_X11 + XtInputId ch_inputHandler; /* Cookie for input */ +#endif +#ifdef FEAT_GUI_GTK + gint ch_inputHandler; /* Cookie for input */ +#endif +#ifdef FEAT_GUI_W32 + int ch_inputHandler = -1; /* simply ret.value of WSAAsyncSelect() */ +#endif + + void (*ch_close_cb)(void); /* callback invoked when channel is closed */ } channel_T; +/* + * 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; /* + * TODO: open debug file when desired. + */ +FILE *debugfd = NULL; + +/* * Add a new channel slot, return the index. - * Returns -1 if out of space. + * The channel isn't actually used into ch_fd is set >= 0; + * Returns -1 if all channels are in use. */ static int add_channel(void) @@ -39,59 +128,559 @@ add_channel(void) return idx; if (channel_count == MAX_OPEN_CHANNELS) return -1; - new_channels = (channel_T *)alloc(sizeof(channel_T) * channel_count + 1); + new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1)); if (new_channels == NULL) return -1; if (channels != NULL) mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); channels = new_channels; + (void)vim_memset(&channels[channel_count], 0, sizeof(channel_T)); + channels[channel_count].ch_fd = (sock_T)-1; +#ifdef FEAT_GUI_X11 + channels[channel_count].ch_inputHandler = (XtInputId)NULL; +#endif +#ifdef FEAT_GUI_GTK + channels[channel_count].ch_inputHandler = 0; +#endif +#ifdef FEAT_GUI_W32 + channels[channel_count].ch_inputHandler = -1; +#endif return channel_count++; } -#if defined(FEAT_NETBEANS_INTG) || defined(PROTO) -static int netbeans_channel = -1; - +#if defined(FEAT_GUI) || defined(PROTO) /* - * Add the netbeans socket to the channels. - * Return the channel index. + * Read a command from netbeans. */ - int -channel_add_netbeans(sock_T fd) +#ifdef FEAT_GUI_X11 + static void +messageFromNetbeans(XtPointer clientData, + int *unused1 UNUSED, + XtInputId *unused2 UNUSED) { - int idx = add_channel(); + channel_read((int)(long)clientData); +} +#endif - if (idx >= 0) - { - channels[idx].ch_fd = fd; - netbeans_channel = idx; - } - return idx; -} - - void -channel_remove_netbeans() +#ifdef FEAT_GUI_GTK + static void +messageFromNetbeans(gpointer clientData, + gint unused1 UNUSED, + GdkInputCondition unused2 UNUSED) { - channels[netbeans_channel].ch_fd = (sock_T)-1; - netbeans_channel = -1; + channel_read((int)(long)clientData); } #endif static void -channel_read(int idx) +channel_gui_register(int idx) +{ + channel_T *channel = &channels[idx]; + + if (!CH_HAS_GUI) + return; + +# ifdef FEAT_GUI_X11 + /* tell notifier we are interested in being called + * when there is input on the editor connection socket + */ + if (channel->ch_inputHandler == (XtInputId)NULL) + channel->ch_inputHandler = + XtAppAddInput((XtAppContext)app_context, channel->ch_fd, + (XtPointer)(XtInputReadMask + XtInputExceptMask), + messageFromNetbeans, (XtPointer)idx); +# else +# ifdef FEAT_GUI_GTK + /* + * Tell gdk we are interested in being called when there + * is input on the editor connection socket + */ + if (channel->ch_inputHandler == 0) + channel->ch_inputHandler = + gdk_input_add((gint)channel->ch_fd, (GdkInputCondition) + ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), + messageFromNetbeans, (gpointer)(long)idx); +# else +# ifdef FEAT_GUI_W32 + /* + * Tell Windows we are interested in receiving message when there + * is input on the editor connection socket. + * TODO: change WM_NETBEANS to something related to the channel index. + */ + if (channel->ch_inputHandler == -1) + channel->ch_inputHandler = + WSAAsyncSelect(channel->ch_fd, s_hwnd, WM_NETBEANS, FD_READ); +# endif +# endif +# endif +} + +/* + * Register any of our file descriptors with the GUI event handling system. + * Called when the GUI has started. + */ + void +channel_gui_register_all(void) +{ + int i; + + for (i = 0; i < channel_count; ++i) + if (channels[i].ch_fd >= 0) + channel_gui_register(i); +} + + static void +channel_gui_unregister(int idx) +{ + channel_T *channel = &channels[idx]; + +# ifdef FEAT_GUI_X11 + if (channel->ch_inputHandler != (XtInputId)NULL) + { + XtRemoveInput(channel->ch_inputHandler); + channel->ch_inputHandler = (XtInputId)NULL; + } +# else +# ifdef FEAT_GUI_GTK + if (channel->ch_inputHandler != 0) + { + gdk_input_remove(channel->ch_inputHandler); + channel->ch_inputHandler = 0; + } +# else +# ifdef FEAT_GUI_W32 + if (channel->ch_inputHandler == 0) + { + WSAAsyncSelect(nbsock, s_hwnd, 0, 0); + channel->ch_inputHandler = -1; + } +# endif +# endif +# endif +} + +#endif + +/* + * Open a channel to "hostname":"port". + * Returns the channel number for success. + * Returns a negative number for failure. + */ + int +channel_open(char *hostname, int port_in, void (*close_cb)(void)) { -# ifdef FEAT_NETBEANS_INTG - if (idx == netbeans_channel) - netbeans_read(); - else -# endif + int sd; + struct sockaddr_in server; + struct hostent * host; +#ifdef FEAT_GUI_W32 + u_short port = port_in; +#else + int port = port_in; +#endif + int idx; + +#ifdef FEAT_GUI_W32 + channel_init_winsock(); +#endif + + idx = add_channel(); + if (idx < 0) + { + CHERROR("All channels are in use\n", ""); + EMSG(_("E999: All channels are in use")); + return -1; + } + + if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) + { + CHERROR("error in socket() in channel_open()\n", ""); + PERROR("E999: socket() in channel_open()"); + return -1; + } + + /* Get the server internet address and put into addr structure */ + /* fill in the socket address structure and connect to server */ + vim_memset((char *)&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + if ((host = gethostbyname(hostname)) == NULL) + { + CHERROR("error in gethostbyname() in channel_open()\n", ""); + PERROR("E999: gethostbyname() in channel_open()"); + sock_close(sd); + return -1; + } + memcpy((char *)&server.sin_addr, host->h_addr, host->h_length); + + /* Connect to server */ + if (connect(sd, (struct sockaddr *)&server, sizeof(server))) { - ; /* TODO: read */ + SOCK_ERRNO; + CHERROR("channel_open: Connect failed with errno %d\n", errno); + if (errno == ECONNREFUSED) + { + sock_close(sd); + if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) + { + SOCK_ERRNO; + CHERROR("socket() retry in channel_open()\n", ""); + PERROR("E999: socket() retry in channel_open()"); + return -1; + } + if (connect(sd, (struct sockaddr *)&server, sizeof(server))) + { + int retries = 36; + int success = FALSE; + + SOCK_ERRNO; + while (retries-- && ((errno == ECONNREFUSED) + || (errno == EINTR))) + { + CHERROR("retrying...\n", ""); + mch_delay(3000L, TRUE); + ui_breakcheck(); + if (got_int) + { + errno = EINTR; + break; + } + if (connect(sd, (struct sockaddr *)&server, + sizeof(server)) == 0) + { + success = TRUE; + break; + } + SOCK_ERRNO; + } + if (!success) + { + /* Get here when the server can't be found. */ + CHERROR("Cannot connect to port after retry\n", ""); + PERROR(_("E999: Cannot connect to port after retry2")); + sock_close(sd); + return -1; + } + } + } + else + { + CHERROR("Cannot connect to port\n", ""); + PERROR(_("E999: Cannot connect to port")); + sock_close(sd); + return -1; + } + } + + channels[idx].ch_fd = sd; + channels[idx].ch_close_cb = close_cb; + +#ifdef FEAT_GUI + channel_gui_register(idx); +#endif + + return idx; +} + +/* + * Return TRUE when channel "idx" is open. + */ + int +channel_is_open(int idx) +{ + return channels[idx].ch_fd >= 0; +} + +/* + * Close channel "idx". + * This does not trigger the close callback. + */ + void +channel_close(int idx) +{ + channel_T *channel = &channels[idx]; + + if (channel->ch_fd >= 0) + { + sock_close(channel->ch_fd); + channel->ch_fd = -1; +#ifdef FEAT_GUI + channel_gui_unregister(idx); +#endif } } -#if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) +/* + * Store "buf[len]" on channel "idx". + */ + void +channel_save(int idx, char_u *buf, int len) +{ + queue_T *node; + queue_T *head = &channels[idx].ch_head; + + node = (queue_T *)alloc(sizeof(queue_T)); + if (node == NULL) + return; /* out of memory */ + node->buffer = alloc(len + 1); + if (node->buffer == NULL) + { + vim_free(node); + return; /* out of memory */ + } + mch_memmove(node->buffer, buf, (size_t)len); + node->buffer[len] = NUL; + + if (head->next == NULL) /* initialize circular queue */ + { + head->next = head; + head->prev = head; + } + + /* insert node at tail of queue */ + node->next = head; + node->prev = head->prev; + head->prev->next = node; + head->prev = node; + + if (debugfd != NULL) + { + fprintf(debugfd, "RECV on %d: ", idx); + fwrite(buf, len, 1, debugfd); + fprintf(debugfd, "\n"); + } +} + +/* + * Return the first buffer from the channel without removing it. + * Returns NULL if there is nothing. + */ + char_u * +channel_peek(int idx) +{ + queue_T *head = &channels[idx].ch_head; + + if (head->next == head || head->next == NULL) + return NULL; + return head->next->buffer; +} + +/* + * Return the first buffer from the channel and remove it. + * The caller must free it. + * Returns NULL if there is nothing. + */ + char_u * +channel_get(int idx) +{ + queue_T *head = &channels[idx].ch_head; + queue_T *node; + char_u *p; + + if (head->next == head || head->next == NULL) + return NULL; + node = head->next; + /* dispose of the node but keep the buffer */ + p = node->buffer; + head->next = node->next; + node->next->prev = node->prev; + vim_free(node); + return p; +} + +/* + * Collapses the first and second buffer in the channel "idx". + * Returns FAIL if that is not possible. + */ + int +channel_collapse(int idx) +{ + queue_T *head = &channels[idx].ch_head; + queue_T *node = head->next; + char_u *p; + + if (node == head || node == NULL || node->next == head) + return FAIL; + + p = alloc((unsigned)(STRLEN(node->buffer) + + STRLEN(node->next->buffer) + 1)); + if (p == NULL) + return FAIL; /* out of memory */ + STRCPY(p, node->buffer); + STRCAT(p, node->next->buffer); + vim_free(node->next->buffer); + node->next->buffer = p; + + /* dispose of the node and buffer */ + head->next = node->next; + node->next->prev = node->prev; + vim_free(node->buffer); + vim_free(node); + return OK; +} + +/* + * Clear the read buffer on channel "idx". + */ + void +channel_clear(int idx) +{ + queue_T *head = &channels[idx].ch_head; + queue_T *node = head->next; + queue_T *next; + + while (node != NULL && node != head) + { + 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" + +/* Buffer size for reading incoming messages. */ +#define MAXMSGSIZE 4096 + +/* + * Read from channel "idx". The data is put in the read queue. + */ + void +channel_read(int idx) +{ + static char_u *buf = NULL; + int len = 0; + int readlen = 0; +#ifdef HAVE_SELECT + struct timeval tval; + fd_set rfds; +#else +# ifdef HAVE_POLL + struct pollfd fds; +# endif +#endif + channel_T *channel = &channels[idx]; + + if (channel->ch_fd < 0) + { + CHLOG(idx, FALSE, "channel_read() called while socket is closed\n"); + return; + } + + /* Allocate a buffer to read into. */ + if (buf == NULL) + { + buf = alloc(MAXMSGSIZE); + if (buf == NULL) + return; /* out of memory! */ + } + + /* Keep on reading for as long as there is something to read. + * Use select() or poll() to avoid blocking on a message that is exactly + * MAXMSGSIZE long. */ + for (;;) + { +#ifdef HAVE_SELECT + FD_ZERO(&rfds); + FD_SET(channel->ch_fd, &rfds); + tval.tv_sec = 0; + tval.tv_usec = 0; + if (select(channel->ch_fd + 1, &rfds, NULL, NULL, &tval) <= 0) + break; +#else +# ifdef HAVE_POLL + fds.fd = channel->ch_fd; + fds.events = POLLIN; + if (poll(&fds, 1, 0) <= 0) + break; +# endif +#endif + len = sock_read(channel->ch_fd, buf, MAXMSGSIZE); + if (len <= 0) + break; /* error or nothing more to read */ + + /* Store the read message in the queue. */ + channel_save(idx, buf, len); + readlen += len; + if (len < MAXMSGSIZE) + break; /* did read everything that's available */ + } + + /* Reading a socket disconnection (readlen == 0), or a socket error. */ + if (readlen <= 0) + { + /* Queue a "DETACH" netbeans message in the command queue in order to + * terminate the netbeans session later. Do not end the session here + * directly as we may be running in the context of a call to + * netbeans_parse_messages(): + * netbeans_parse_messages + * -> autocmd triggered while processing the netbeans cmd + * -> ui_breakcheck + * -> gui event loop or select loop + * -> channel_read() + */ + channel_save(idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG)); + + channel_close(idx); + if (channel->ch_close_cb != NULL) + (*channel->ch_close_cb)(); + + if (len < 0) + { + /* Todo: which channel? */ + CHERROR("%s(): cannot from channel\n", "channel_read"); + PERROR(_("E999: read from channel")); + } + } + +#if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK) + if (CH_HAS_GUI && gtk_main_level() > 0) + gtk_main_quit(); +#endif +} + +/* + * Write "buf" (NUL terminated string) to channel "idx". + * When "fun" is not NULL an error message might be given. + */ + void +channel_send(int idx, char_u *buf, char *fun) +{ + channel_T *channel = &channels[idx]; + int len = (int)STRLEN(buf); + + if (channel->ch_fd < 0) + { + if (!channel->ch_error && fun != NULL) + { + CHERROR(" %s(): write while not connected\n", fun); + EMSG2("E630: %s(): write while not connected", fun); + } + channel->ch_error = TRUE; + } + else if (sock_write(channel->ch_fd, buf, len) != len) + { + if (!channel->ch_error && fun != NULL) + { + CHERROR(" %s(): write failed\n", fun); + EMSG2("E631: %s(): write failed", fun); + } + channel->ch_error = TRUE; + } + else + channel->ch_error = FALSE; +} + +# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) /* * Add open channels to the poll struct. * Return the adjusted struct index. @@ -138,9 +727,9 @@ channel_poll_check(int ret_in, void *fds return ret; } -#endif /* UNIX && !HAVE_SELECT */ +# endif /* UNIX && !HAVE_SELECT */ -#if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO) +# if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO) /* * The type of "rfds" is hidden to avoid problems with the function proto. */ @@ -182,6 +771,6 @@ channel_select_check(int ret_in, void *r return ret; } -#endif /* UNIX && HAVE_SELECT */ +# endif /* UNIX && HAVE_SELECT */ #endif /* FEAT_CHANNEL */ diff --git a/src/gui.c b/src/gui.c --- a/src/gui.c +++ b/src/gui.c @@ -5004,8 +5004,8 @@ ex_gui(eap) * of the argument ending up after the shell prompt. */ msg_clr_eos_force(); gui_start(); -#ifdef FEAT_NETBEANS_INTG - netbeans_gui_register(); +#ifdef FEAT_CHANNEL + channel_gui_register_all(); #endif } if (!ends_excmd(*eap->arg)) diff --git a/src/gui_w48.c b/src/gui_w48.c --- a/src/gui_w48.c +++ b/src/gui_w48.c @@ -1779,9 +1779,10 @@ process_message(void) } #endif -#ifdef FEAT_NETBEANS_INTG +#ifdef FEAT_CHANNEL if (msg.message == WM_NETBEANS) { + /* TODO: channel_read(idx) */ netbeans_read(); return; } diff --git a/src/netbeans.c b/src/netbeans.c --- a/src/netbeans.c +++ b/src/netbeans.c @@ -27,36 +27,11 @@ #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) -/* Note: when making changes here also adjust configure.in. */ -#ifdef WIN32 -# ifdef DEBUG -# include /* for _T definition for TRACEn macros */ -# endif -/* WinSock API is separated from C API, thus we can't use read(), write(), - * errno... */ -# define SOCK_ERRNO errno = WSAGetLastError() -# undef ECONNREFUSED -# define ECONNREFUSED WSAECONNREFUSED -# ifdef EINTR -# undef EINTR -# endif -# define EINTR WSAEINTR -# define sock_write(sd, buf, len) send(sd, buf, len, 0) -# define sock_read(sd, buf, len) recv(sd, buf, len, 0) -# define sock_close(sd) closesocket(sd) -# define sleep(t) Sleep(t*1000) /* WinAPI Sleep() accepts milliseconds */ -#else +#ifndef WIN32 # include -# include - -# include # ifdef HAVE_LIBGEN_H # include # endif -# define SOCK_ERRNO -# define sock_write(sd, buf, len) write(sd, buf, len) -# define sock_read(sd, buf, len) read(sd, buf, len) -# define sock_close(sd) close(sd) #endif #include "version.h" @@ -83,35 +58,14 @@ static int getConnInfo __ARGS((char *fil static void nb_init_graphics __ARGS((void)); static void coloncmd __ARGS((char *cmd, ...)); static void nb_set_curbuf __ARGS((buf_T *buf)); -#ifdef FEAT_GUI_X11 -static void messageFromNetbeans __ARGS((XtPointer, int *, XtInputId *)); -#endif -#ifdef FEAT_GUI_GTK -static void messageFromNetbeans __ARGS((gpointer, gint, GdkInputCondition)); -#endif static void nb_parse_cmd __ARGS((char_u *)); static int nb_do_cmd __ARGS((int, char_u *, int, int, char_u *)); static void nb_send __ARGS((char *buf, char *fun)); static void nb_free __ARGS((void)); -/* TRUE when netbeans is running with a GUI. */ -#ifdef FEAT_GUI -# define NB_HAS_GUI (gui.in_use || gui.starting) -#endif - -static sock_T nbsock = -1; /* socket fd for Netbeans connection */ -#define NETBEANS_OPEN (nbsock != -1) - -#ifdef FEAT_GUI_X11 -static XtInputId inputHandler = (XtInputId)NULL; /* Cookie for input */ -#endif -#ifdef FEAT_GUI_GTK -static gint inputHandler = 0; /* Cookie for input */ -#endif -#ifdef FEAT_GUI_W32 -static int inputHandler = -1; /* simply ret.value of WSAAsyncSelect() */ -extern HWND s_hwnd; /* Gvim's Window handle */ -#endif +#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; @@ -126,48 +80,17 @@ static int needupdate = 0; static int inAtomic = 0; /* - * Close the socket and remove the input handlers. + * Callback invoked when the channel is closed. */ static void -nb_close_socket(void) +nb_channel_closed(void) { - buf_T *buf; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - buf->b_has_sign_column = FALSE; - -#ifdef FEAT_GUI_X11 - if (inputHandler != (XtInputId)NULL) - { - XtRemoveInput(inputHandler); - inputHandler = (XtInputId)NULL; - } -#else -# ifdef FEAT_GUI_GTK - if (inputHandler != 0) - { - gdk_input_remove(inputHandler); - inputHandler = 0; - } -# else -# ifdef FEAT_GUI_W32 - if (inputHandler == 0) - { - WSAAsyncSelect(nbsock, s_hwnd, 0, 0); - inputHandler = -1; - } -# endif -# endif -#endif - - sock_close(nbsock); - nbsock = -1; - channel_remove_netbeans(); + nb_channel_idx = -1; } /* * Close the connection and cleanup. - * May be called when nb_close_socket() was called earlier. + * May be called when the socket was closed earlier. */ static void netbeans_close(void) @@ -175,7 +98,10 @@ netbeans_close(void) if (NETBEANS_OPEN) { netbeans_send_disconnect(); - nb_close_socket(); + 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 @@ -209,14 +135,7 @@ netbeans_close(void) static int netbeans_connect(char *params, int doabort) { - struct sockaddr_in server; - struct hostent * host; -#ifdef FEAT_GUI_W32 - u_short port; -#else - int port; -#endif - int sd; + int port; char buf[32]; char *hostname = NULL; char *address = NULL; @@ -291,107 +210,29 @@ netbeans_connect(char *params, int doabo vim_free(password); password = (char *)vim_strsave((char_u *)NB_DEF_PASS); } - if (hostname == NULL || address == NULL || password == NULL) - goto theend; /* out of memory */ - -#ifdef FEAT_GUI_W32 - channel_init_winsock(); -#endif - - port = atoi(address); - - if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) + if (hostname != NULL && address != NULL && password != NULL) { - nbdebug(("error in socket() in netbeans_connect()\n")); - PERROR("socket() in netbeans_connect()"); - goto theend; - } - - /* Get the server internet address and put into addr structure */ - /* fill in the socket address structure and connect to server */ - vim_memset((char *)&server, '\0', sizeof(server)); - server.sin_family = AF_INET; - server.sin_port = htons(port); - if ((host = gethostbyname(hostname)) == NULL) - { - nbdebug(("error in gethostbyname() in netbeans_connect()\n")); - PERROR("gethostbyname() in netbeans_connect()"); - sock_close(sd); - goto theend; - } - memcpy((char *)&server.sin_addr, host->h_addr, host->h_length); - /* Connect to server */ - if (connect(sd, (struct sockaddr *)&server, sizeof(server))) - { - SOCK_ERRNO; - nbdebug(("netbeans_connect: Connect failed with errno %d\n", errno)); - if (errno == ECONNREFUSED) + port = atoi(address); + nb_channel_idx = channel_open(hostname, port, nb_channel_closed); + if (nb_channel_idx >= 0) { - sock_close(sd); - if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) - { - SOCK_ERRNO; - nbdebug(("socket()#2 in netbeans_connect()\n")); - PERROR("socket()#2 in netbeans_connect()"); - goto theend; - } - if (connect(sd, (struct sockaddr *)&server, sizeof(server))) - { - int retries = 36; - int success = FALSE; - - SOCK_ERRNO; - while (retries-- && ((errno == ECONNREFUSED) - || (errno == EINTR))) - { - nbdebug(("retrying...\n")); - mch_delay(3000L, TRUE); - ui_breakcheck(); - if (got_int) - { - errno = EINTR; - break; - } - if (connect(sd, (struct sockaddr *)&server, - sizeof(server)) == 0) - { - success = TRUE; - break; - } - SOCK_ERRNO; - } - if (!success) - { - /* Get here when the server can't be found. */ - nbdebug(("Cannot connect to Netbeans #2\n")); - PERROR(_("Cannot connect to Netbeans #2")); - sock_close(sd); - if (doabort) - getout(1); - goto theend; - } - } - } - else - { - nbdebug(("Cannot connect to Netbeans\n")); - PERROR(_("Cannot connect to Netbeans")); - sock_close(sd); - if (doabort) - getout(1); - goto theend; + /* success */ +# ifdef FEAT_BEVAL + bevalServers |= BEVAL_NETBEANS; +# endif + + /* success, login */ + vim_snprintf(buf, sizeof(buf), "AUTH %s\n", password); + nb_send(buf, "netbeans_connect"); + + sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion); + nb_send(buf, "externaleditor_version"); } } - nbsock = sd; - channel_add_netbeans(nbsock); - vim_snprintf(buf, sizeof(buf), "AUTH %s\n", password); - nb_send(buf, "netbeans_connect"); - - sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion); - nb_send(buf, "externaleditor_version"); - -theend: + if (nb_channel_idx < 0 && doabort) + getout(1); + vim_free(hostname); vim_free(address); vim_free(password); @@ -532,111 +373,31 @@ handle_key_queue(void) } -struct cmdqueue -{ - char_u *buffer; - struct cmdqueue *next; - struct cmdqueue *prev; -}; - -typedef struct cmdqueue queue_T; - -static queue_T head; /* dummy node, header for circular queue */ - - -/* - * Put the buffer on the work queue; possibly save it to a file as well. - */ - static void -save(char_u *buf, int len) -{ - queue_T *node; - - node = (queue_T *)alloc(sizeof(queue_T)); - if (node == NULL) - return; /* out of memory */ - node->buffer = alloc(len + 1); - if (node->buffer == NULL) - { - vim_free(node); - return; /* out of memory */ - } - mch_memmove(node->buffer, buf, (size_t)len); - node->buffer[len] = NUL; - - if (head.next == NULL) /* initialize circular queue */ - { - head.next = &head; - head.prev = &head; - } - - /* insert node at tail of queue */ - node->next = &head; - node->prev = head.prev; - head.prev->next = node; - head.prev = node; - -#ifdef NBDEBUG - { - static int outfd = -2; - - /* possibly write buffer out to a file */ - if (outfd == -3) - return; - - if (outfd == -2) - { - char *file = getenv("__NETBEANS_SAVE"); - if (file == NULL) - outfd = -3; - else - outfd = mch_open(file, O_WRONLY|O_CREAT|O_TRUNC, 0666); - } - - if (outfd >= 0) - write(outfd, buf, len); - } -#endif -} - - /* * While there's still a command in the work queue, parse and execute it. */ void netbeans_parse_messages(void) { + char_u *buffer; char_u *p; - queue_T *node; int own_node; - while (head.next != NULL && head.next != &head) + while (nb_channel_idx >= 0) { - node = head.next; + buffer = channel_peek(nb_channel_idx); + if (buffer == NULL) + break; /* nothing to read */ /* Locate the first line in the first buffer. */ - p = vim_strchr(node->buffer, '\n'); + p = vim_strchr(buffer, '\n'); if (p == NULL) { /* 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 (node->next == &head) + if (channel_collapse(nb_channel_idx) == FAIL) return; - p = alloc((unsigned)(STRLEN(node->buffer) - + STRLEN(node->next->buffer) + 1)); - if (p == NULL) - return; /* 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); } else { @@ -648,147 +409,30 @@ netbeans_parse_messages(void) if (*p == NUL) { own_node = TRUE; - head.next = node->next; - node->next->prev = node->prev; + channel_get(nb_channel_idx); } else own_node = FALSE; /* now, parse and execute the commands */ - nb_parse_cmd(node->buffer); + nb_parse_cmd(buffer); if (own_node) - { - /* buffer finished, dispose of the node and buffer */ - vim_free(node->buffer); - vim_free(node); - } - /* Check that "head" wasn't changed under our fingers, e.g. when a - * DETACH command was handled. */ - else if (head.next == node) - { - /* more follows, move to the start */ - STRMOVE(node->buffer, p); - } + /* buffer finished, dispose of it */ + vim_free(buffer); + else + /* more follows, move it to the start */ + STRMOVE(buffer, p); } } } -/* Buffer size for reading incoming messages. */ -#define MAXMSGSIZE 4096 - -/* - * Read a command from netbeans. - */ -#ifdef FEAT_GUI_X11 - static void -messageFromNetbeans(XtPointer clientData UNUSED, - int *unused1 UNUSED, - XtInputId *unused2 UNUSED) -{ - netbeans_read(); -} -#endif - -#ifdef FEAT_GUI_GTK - static void -messageFromNetbeans(gpointer clientData UNUSED, - gint unused1 UNUSED, - GdkInputCondition unused2 UNUSED) -{ - netbeans_read(); -} -#endif - -#define DETACH_MSG "DETACH\n" - +/* TODO: remove */ void netbeans_read() { - static char_u *buf = NULL; - int len = 0; - int readlen = 0; -#ifdef HAVE_SELECT - struct timeval tval; - fd_set rfds; -#else -# ifdef HAVE_POLL - struct pollfd fds; -# endif -#endif - - if (!NETBEANS_OPEN) - { - nbdebug(("messageFromNetbeans() called without a socket\n")); - return; - } - - /* Allocate a buffer to read into. */ - if (buf == NULL) - { - buf = alloc(MAXMSGSIZE); - if (buf == NULL) - return; /* out of memory! */ - } - - /* Keep on reading for as long as there is something to read. - * Use select() or poll() to avoid blocking on a message that is exactly - * MAXMSGSIZE long. */ - for (;;) - { -#ifdef HAVE_SELECT - FD_ZERO(&rfds); - FD_SET(nbsock, &rfds); - tval.tv_sec = 0; - tval.tv_usec = 0; - if (select(nbsock + 1, &rfds, NULL, NULL, &tval) <= 0) - break; -#else -# ifdef HAVE_POLL - fds.fd = nbsock; - fds.events = POLLIN; - if (poll(&fds, 1, 0) <= 0) - break; -# endif -#endif - len = sock_read(nbsock, buf, MAXMSGSIZE); - if (len <= 0) - break; /* error or nothing more to read */ - - /* Store the read message in the queue. */ - save(buf, len); - readlen += len; - if (len < MAXMSGSIZE) - break; /* did read everything that's available */ - } - - /* Reading a socket disconnection (readlen == 0), or a socket error. */ - if (readlen <= 0) - { - /* Queue a "DETACH" netbeans message in the command queue in order to - * terminate the netbeans session later. Do not end the session here - * directly as we may be running in the context of a call to - * netbeans_parse_messages(): - * netbeans_parse_messages - * -> autocmd triggered while processing the netbeans cmd - * -> ui_breakcheck - * -> gui event loop or select loop - * -> netbeans_read() - */ - save((char_u *)DETACH_MSG, (int)strlen(DETACH_MSG)); - nb_close_socket(); - - if (len < 0) - { - nbdebug(("read from Netbeans socket\n")); - PERROR(_("read from Netbeans socket")); - } - } - -#if defined(NB_HAS_GUI) && defined(FEAT_GUI_GTK) - if (NB_HAS_GUI && gtk_main_level() > 0) - gtk_main_quit(); -#endif + if (nb_channel_idx >= 0) + channel_read(nb_channel_idx); } /* @@ -825,8 +469,13 @@ nb_parse_cmd(char_u *cmd) /* NOTREACHED */ } - if (STRCMP(cmd, "DETACH") == 0) + if (STRCMP(cmd, "\"DETACH\"") == 0) { + buf_T *buf; + + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + buf->b_has_sign_column = FALSE; + /* The IDE is breaking the connection. */ netbeans_close(); return; @@ -923,7 +572,6 @@ static int curPCtype = -1; nb_free() { keyQ_T *key_node = keyHead.next; - queue_T *cmd_node = head.next; nbbuf_T buf; int i; @@ -960,19 +608,8 @@ nb_free() } /* free the queued netbeans commands */ - while (cmd_node != NULL && cmd_node != &head) - { - queue_T *next = cmd_node->next; - vim_free(cmd_node->buffer); - vim_free(cmd_node); - if (next == &head) - { - head.next = &head; - head.prev = &head; - break; - } - cmd_node = next; - } + if (nb_channel_idx >= 0) + channel_clear(nb_channel_idx); } /* @@ -1116,40 +753,19 @@ netbeans_end(void) sprintf(buf, "%d:killed=%d\n", i, r_cmdno); nbdebug(("EVT: %s", buf)); /* nb_send(buf, "netbeans_end"); avoid "write failed" messages */ - ignored = sock_write(nbsock, buf, (int)STRLEN(buf)); + nb_send(buf, NULL); } } /* * Send a message to netbeans. + * When "fun" is NULL no error is given. */ static void nb_send(char *buf, char *fun) { - /* Avoid giving pages full of error messages when the other side has - * exited, only mention the first error until the connection works again. */ - static int did_error = FALSE; - - if (!NETBEANS_OPEN) - { - if (!did_error) - { - nbdebug((" %s(): write while not connected\n", fun)); - EMSG2("E630: %s(): write while not connected", fun); - } - did_error = TRUE; - } - else if (sock_write(nbsock, buf, (int)STRLEN(buf)) != (int)STRLEN(buf)) - { - if (!did_error) - { - nbdebug((" %s(): write failed\n", fun)); - EMSG2("E631: %s(): write failed", fun); - } - did_error = TRUE; - } - else - did_error = FALSE; + if (nb_channel_idx >= 0) + channel_send(nb_channel_idx, (char_u *)buf, fun); } /* @@ -2924,52 +2540,6 @@ netbeans_active(void) return NETBEANS_OPEN; } -#if defined(FEAT_GUI) || defined(PROTO) -/* - * Register our file descriptor with the gui event handling system. - */ - void -netbeans_gui_register(void) -{ - if (!NB_HAS_GUI || !NETBEANS_OPEN) - return; - -# ifdef FEAT_GUI_X11 - /* tell notifier we are interested in being called - * when there is input on the editor connection socket - */ - if (inputHandler == (XtInputId)NULL) - inputHandler = XtAppAddInput((XtAppContext)app_context, nbsock, - (XtPointer)(XtInputReadMask + XtInputExceptMask), - messageFromNetbeans, NULL); -# else -# ifdef FEAT_GUI_GTK - /* - * Tell gdk we are interested in being called when there - * is input on the editor connection socket - */ - if (inputHandler == 0) - inputHandler = gdk_input_add((gint)nbsock, (GdkInputCondition) - ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION), - messageFromNetbeans, NULL); -# else -# ifdef FEAT_GUI_W32 - /* - * Tell Windows we are interested in receiving message when there - * is input on the editor connection socket - */ - if (inputHandler == -1) - inputHandler = WSAAsyncSelect(nbsock, s_hwnd, WM_NETBEANS, FD_READ); -# endif -# endif -# endif - -# ifdef FEAT_BEVAL - bevalServers |= BEVAL_NETBEANS; -# endif -} -#endif - /* * Tell netbeans that the window was opened, ready for commands. */ @@ -2986,9 +2556,6 @@ netbeans_open(char *params, int doabort) if (netbeans_connect(params, doabort) != OK) return; -#ifdef FEAT_GUI - netbeans_gui_register(); -#endif nbdebug(("EVT: %s", cmd)); nb_send(cmd, "netbeans_startup_done"); diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -1,6 +1,15 @@ /* channel.c */ -int channel_add_netbeans(sock_T fd); -void channel_remove_netbeans(void); +void channel_gui_register_all(void); +int channel_open(char *hostname, int port_in, void (*close_cb)(void)); +int channel_is_open(int idx); +void channel_close(int idx); +void channel_save(int idx, char_u *buf, int len); +char_u *channel_peek(int idx); +char_u *channel_get(int idx); +int channel_collapse(int idx); +void channel_clear(int idx); +void channel_read(int idx); +void 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); diff --git a/src/proto/netbeans.pro b/src/proto/netbeans.pro --- a/src/proto/netbeans.pro +++ b/src/proto/netbeans.pro @@ -9,7 +9,6 @@ void ex_nbkey(exarg_T *eap); void ex_nbstart(exarg_T *eap); void netbeans_beval_cb(BalloonEval *beval, int state); int netbeans_active(void); -void netbeans_gui_register(void); void netbeans_open(char *params, int doabort); void netbeans_send_disconnect(void); void netbeans_frame_moved(int new_x, int new_y); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1182, +/**/ 1181, /**/ 1180,