]> granicus.if.org Git - vim/commitdiff
patch 7.4.1182 v7.4.1182
authorBram Moolenaar <Bram@vim.org>
Tue, 26 Jan 2016 22:30:18 +0000 (23:30 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 26 Jan 2016 22:30:18 +0000 (23:30 +0100)
Problem:    Still socket code intertwined with netbeans.
Solution:   Move code from netbeans.c to channel.c

src/channel.c
src/gui.c
src/gui_w48.c
src/netbeans.c
src/proto/channel.pro
src/proto/netbeans.pro
src/version.c

index a108d7e7c291e2c4792a0377b62113e06f2318e2..bb7db7e49a060982f4fb6b3e9746413cdb21637c 100644 (file)
 
 #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 <netdb.h>
+# include <netinet/in.h>
+
+# include <sys/socket.h>
+# ifdef HAVE_LIBGEN_H
+#  include <libgen.h>
+# 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)
+/*
+ * 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
+}
 
 /*
- * Add the netbeans socket to the channels.
- * Return the channel index.
+ * 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_add_netbeans(sock_T fd)
+channel_open(char *hostname, int port_in, void (*close_cb)(void))
 {
-    int idx = add_channel();
+    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 (idx >= 0)
+    if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
     {
-       channels[idx].ch_fd = fd;
-       netbeans_channel = idx;
+       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;
 }
 
-    void
-channel_remove_netbeans()
+/*
+ * Return TRUE when channel "idx" is open.
+ */
+    int
+channel_is_open(int idx)
 {
-    channels[netbeans_channel].ch_fd = (sock_T)-1;
-    netbeans_channel = -1;
+    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
+    }
+}
 
-    static void
+/*
+ * 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)
 {
-# ifdef FEAT_NETBEANS_INTG
-    if (idx == netbeans_channel)
-       netbeans_read();
-    else
+    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)
     {
-       ; /* TODO: read */
+       /* 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)
+# 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_in)
 
     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 *rfds_in)
 
     return ret;
 }
-#endif /* UNIX && HAVE_SELECT */
+# endif /* UNIX && HAVE_SELECT */
 
 #endif /* FEAT_CHANNEL */
index ec732e001ce99d927cedc6e8fe80c8a0900b3cd6..f416e540ae40edf0540aece2b41c30b066d0aa86 100644 (file)
--- 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))
index 1f3822be93ca6d55e1c19dee17b659a2a667aebd..6da1c636cb012eed6e3e8f7b55aa214bc215c5b9 100644 (file)
@@ -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;
     }
index 0b9ea134f3bcc5b2f0f8eb96a2a5d89e8f1b84f9..09d3d9bc0cf7c4b8c298f644c51ca14eea0cf6ba 100644 (file)
 
 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
 
-/* Note: when making changes here also adjust configure.in. */
-#ifdef WIN32
-# ifdef DEBUG
-#  include <tchar.h>   /* 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 <netdb.h>
-# include <netinet/in.h>
-
-# include <sys/socket.h>
 # ifdef HAVE_LIBGEN_H
 #  include <libgen.h>
 # 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 *file, char **host, char **port, char **pass
 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)
+#define NETBEANS_OPEN (nb_channel_idx >= 0 && channel_is_open(nb_channel_idx))
+static int nb_channel_idx = -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;
 
@@ -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 doabort)
        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)))
+    if (hostname != NULL && address != NULL && password != NULL)
     {
-       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;
+           /* success */
+# ifdef FEAT_BEVAL
+           bevalServers |= BEVAL_NETBEANS;
+# endif
 
-               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, 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");
+    if (nb_channel_idx < 0 && doabort)
+       getout(1);
 
-    sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion);
-    nb_send(buf, "externaleditor_version");
-
-theend:
     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");
index d2e9b3a3705743bd2e55ab2b1a6bf1f5c855f526..fa1b9e29b0a47766a85ad3e727602af2e7be871c 100644 (file)
@@ -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);
index c7e1f068893e523cd13d722ed4939689fd993ee5..b01bf02ed19cc1edb69c2c42801eae0b0a6b917c 100644 (file)
@@ -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);
index 53f5dedb46a1f002d4a3c7b1e6e73edb93c5e0d3..503d94107b7c1e52364905cd37af247e6e70f06d 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1182,
 /**/
     1181,
 /**/