To: vim_dev@googlegroups.com Subject: Patch 7.4.1182 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1182 Problem: Still socket code intertwined with netbeans. Solution: Move code from netbeans.c to channel.c Files: src/channel.c, src/netbeans.c, src/proto/channel.pro, src/proto/netbeans.pro, src/gui.c, src/gui_w48.c *** ../vim-7.4.1181/src/channel.c 2016-01-24 20:36:18.850082515 +0100 --- src/channel.c 2016-01-26 23:20:41.536676106 +0100 *************** *** 14,30 **** #if defined(FEAT_CHANNEL) || defined(PROTO) typedef struct { ! sock_T ch_fd; ! int ch_idx; } channel_T; static channel_T *channels = NULL; static int channel_count = 0; /* * Add a new channel slot, return the index. ! * Returns -1 if out of space. */ static int add_channel(void) --- 14,119 ---- #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; /* 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. ! * 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,97 **** return idx; if (channel_count == MAX_OPEN_CHANNELS) return -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; channels[channel_count].ch_fd = (sock_T)-1; return channel_count++; } ! #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) ! static int netbeans_channel = -1; /* ! * Add the netbeans socket to the channels. ! * Return the channel index. */ int ! channel_add_netbeans(sock_T fd) { ! int idx = add_channel(); ! if (idx >= 0) { ! channels[idx].ch_fd = fd; ! netbeans_channel = idx; } return idx; } ! void ! channel_remove_netbeans() { ! channels[netbeans_channel].ch_fd = (sock_T)-1; ! netbeans_channel = -1; } #endif ! static void channel_read(int idx) { ! # ifdef FEAT_NETBEANS_INTG ! if (idx == netbeans_channel) ! netbeans_read(); ! else # endif { ! ; /* TODO: read */ } } ! #if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) /* * Add open channels to the poll struct. * Return the adjusted struct index. --- 128,686 ---- return idx; if (channel_count == MAX_OPEN_CHANNELS) return -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_GUI) || defined(PROTO) ! /* ! * Read a command from netbeans. ! */ ! #ifdef FEAT_GUI_X11 ! static void ! messageFromNetbeans(XtPointer clientData, ! int *unused1 UNUSED, ! XtInputId *unused2 UNUSED) ! { ! channel_read((int)(long)clientData); ! } ! #endif ! ! #ifdef FEAT_GUI_GTK ! static void ! messageFromNetbeans(gpointer clientData, ! gint unused1 UNUSED, ! GdkInputCondition unused2 UNUSED) ! { ! channel_read((int)(long)clientData); ! } ! #endif ! ! static void ! 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)) { ! 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))) ! { ! 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 + } + } ! /* ! * 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,146 **** return ret; } ! #endif /* UNIX && !HAVE_SELECT */ ! #if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO) /* * The type of "rfds" is hidden to avoid problems with the function proto. */ --- 727,735 ---- return ret; } ! # endif /* UNIX && !HAVE_SELECT */ ! # if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO) /* * The type of "rfds" is hidden to avoid problems with the function proto. */ *************** *** 182,187 **** return ret; } ! #endif /* UNIX && HAVE_SELECT */ #endif /* FEAT_CHANNEL */ --- 771,776 ---- return ret; } ! # endif /* UNIX && HAVE_SELECT */ #endif /* FEAT_CHANNEL */ *** ../vim-7.4.1181/src/netbeans.c 2016-01-24 22:16:58.462876040 +0100 --- src/netbeans.c 2016-01-26 23:25:14.061818941 +0100 *************** *** 27,62 **** #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 # 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" --- 27,37 ---- #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) ! #ifndef WIN32 # include # ifdef HAVE_LIBGEN_H # include # endif #endif #include "version.h" *************** *** 83,117 **** 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 static int r_cmdno; /* current command number for reply */ static int dosetvisible = FALSE; --- 58,71 ---- static void nb_init_graphics __ARGS((void)); static void coloncmd __ARGS((char *cmd, ...)); static void nb_set_curbuf __ARGS((buf_T *buf)); 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)); ! #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,173 **** static int inAtomic = 0; /* ! * Close the socket and remove the input handlers. */ static void ! nb_close_socket(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(); } /* * Close the connection and cleanup. ! * May be called when nb_close_socket() was called earlier. */ static void netbeans_close(void) --- 80,96 ---- static int inAtomic = 0; /* ! * Callback invoked when the channel is closed. */ static void ! nb_channel_closed(void) { ! nb_channel_idx = -1; } /* * Close the connection and cleanup. ! * May be called when the socket was closed earlier. */ static void netbeans_close(void) *************** *** 175,181 **** if (NETBEANS_OPEN) { netbeans_send_disconnect(); ! nb_close_socket(); } #ifdef FEAT_BEVAL --- 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 *************** *** 209,222 **** 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; char buf[32]; char *hostname = NULL; char *address = NULL; --- 135,141 ---- static int netbeans_connect(char *params, int doabort) { ! int port; char buf[32]; char *hostname = NULL; char *address = NULL; *************** *** 291,397 **** 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) ! { ! 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) { ! 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; } } ! 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: vim_free(hostname); vim_free(address); vim_free(password); --- 210,238 ---- vim_free(password); password = (char *)vim_strsave((char_u *)NB_DEF_PASS); } ! if (hostname != NULL && address != NULL && password != NULL) { ! port = atoi(address); ! nb_channel_idx = channel_open(hostname, port, nb_channel_closed); ! if (nb_channel_idx >= 0) { ! /* 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"); } } ! if (nb_channel_idx < 0 && doabort) ! getout(1); vim_free(hostname); vim_free(address); vim_free(password); *************** *** 532,642 **** } - 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 *p; - queue_T *node; int own_node; ! while (head.next != NULL && head.next != &head) { ! node = head.next; /* Locate the first line in the first buffer. */ ! p = vim_strchr(node->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) 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 { --- 373,403 ---- } /* * 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; int own_node; ! while (nb_channel_idx >= 0) { ! buffer = channel_peek(nb_channel_idx); ! if (buffer == NULL) ! break; /* nothing to read */ /* Locate the first line in the first buffer. */ ! 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 (channel_collapse(nb_channel_idx) == FAIL) return; } else { *************** *** 648,794 **** if (*p == NUL) { own_node = TRUE; ! head.next = node->next; ! node->next->prev = node->prev; } else own_node = FALSE; /* now, parse and execute the commands */ ! nb_parse_cmd(node->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 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" ! 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 } /* --- 409,438 ---- if (*p == NUL) { own_node = TRUE; ! channel_get(nb_channel_idx); } else own_node = FALSE; /* now, parse and execute the commands */ ! nb_parse_cmd(buffer); if (own_node) ! /* buffer finished, dispose of it */ ! vim_free(buffer); ! else ! /* more follows, move it to the start */ ! STRMOVE(buffer, p); } } } ! /* TODO: remove */ void netbeans_read() { ! if (nb_channel_idx >= 0) ! channel_read(nb_channel_idx); } /* *************** *** 825,832 **** /* NOTREACHED */ } ! if (STRCMP(cmd, "DETACH") == 0) { /* The IDE is breaking the connection. */ netbeans_close(); return; --- 469,481 ---- /* NOTREACHED */ } ! 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,929 **** nb_free() { keyQ_T *key_node = keyHead.next; - queue_T *cmd_node = head.next; nbbuf_T buf; int i; --- 572,577 ---- *************** *** 960,978 **** } /* 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; ! } } /* --- 608,615 ---- } /* free the queued netbeans commands */ ! if (nb_channel_idx >= 0) ! channel_clear(nb_channel_idx); } /* *************** *** 1116,1155 **** 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)); } } /* * Send a message to netbeans. */ 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; } /* --- 753,771 ---- sprintf(buf, "%d:killed=%d\n", i, r_cmdno); nbdebug(("EVT: %s", buf)); /* nb_send(buf, "netbeans_end"); avoid "write failed" messages */ ! 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) { ! if (nb_channel_idx >= 0) ! channel_send(nb_channel_idx, (char_u *)buf, fun); } /* *************** *** 2924,2975 **** 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. */ --- 2540,2545 ---- *************** *** 2986,2994 **** if (netbeans_connect(params, doabort) != OK) return; - #ifdef FEAT_GUI - netbeans_gui_register(); - #endif nbdebug(("EVT: %s", cmd)); nb_send(cmd, "netbeans_startup_done"); --- 2556,2561 ---- *** ../vim-7.4.1181/src/proto/channel.pro 2016-01-24 20:36:18.854082474 +0100 --- src/proto/channel.pro 2016-01-26 23:17:36.222619137 +0100 *************** *** 1,6 **** /* channel.c */ ! int channel_add_netbeans(sock_T fd); ! void channel_remove_netbeans(void); 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); --- 1,15 ---- /* channel.c */ ! 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); *** ../vim-7.4.1181/src/proto/netbeans.pro 2016-01-24 20:36:18.854082474 +0100 --- src/proto/netbeans.pro 2016-01-26 23:25:18.129776295 +0100 *************** *** 9,15 **** 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); --- 9,14 ---- *** ../vim-7.4.1181/src/gui.c 2016-01-02 22:25:40.670710107 +0100 --- src/gui.c 2016-01-26 22:21:23.945990461 +0100 *************** *** 5004,5011 **** * of the argument ending up after the shell prompt. */ msg_clr_eos_force(); gui_start(); ! #ifdef FEAT_NETBEANS_INTG ! netbeans_gui_register(); #endif } if (!ends_excmd(*eap->arg)) --- 5004,5011 ---- * of the argument ending up after the shell prompt. */ msg_clr_eos_force(); gui_start(); ! #ifdef FEAT_CHANNEL ! channel_gui_register_all(); #endif } if (!ends_excmd(*eap->arg)) *** ../vim-7.4.1181/src/gui_w48.c 2016-01-17 20:53:07.962014779 +0100 --- src/gui_w48.c 2016-01-26 23:19:51.565200047 +0100 *************** *** 1779,1787 **** } #endif ! #ifdef FEAT_NETBEANS_INTG if (msg.message == WM_NETBEANS) { netbeans_read(); return; } --- 1779,1788 ---- } #endif ! #ifdef FEAT_CHANNEL if (msg.message == WM_NETBEANS) { + /* TODO: channel_read(idx) */ netbeans_read(); return; } *** ../vim-7.4.1181/src/version.c 2016-01-26 19:59:04.571324075 +0100 --- src/version.c 2016-01-26 23:21:40.248060546 +0100 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 1182, /**/ -- hundred-and-one symptoms of being an internet addict: 47. You are so familiar with the WWW that you find the search engines useless. /// 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 ///