1
0
forked from aniani/vim

patch 7.4.1182

Problem:    Still socket code intertwined with netbeans.
Solution:   Move code from netbeans.c to channel.c
This commit is contained in:
Bram Moolenaar
2016-01-26 23:30:18 +01:00
parent 6650a69454
commit d04a020a8a
7 changed files with 696 additions and 529 deletions

View File

@@ -14,17 +14,106 @@
#if defined(FEAT_CHANNEL) || defined(PROTO) #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 { typedef struct {
sock_T ch_fd; sock_T ch_fd; /* the socket, -1 for a closed channel */
int ch_idx; 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; } channel_T;
/*
* Information about all channels.
* There can be gaps for closed channels, they will be reused later.
*/
static channel_T *channels = NULL; static channel_T *channels = NULL;
static int channel_count = 0; static int channel_count = 0;
/*
* TODO: open debug file when desired.
*/
FILE *debugfd = NULL;
/* /*
* Add a new channel slot, return the index. * 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 static int
add_channel(void) add_channel(void)
@@ -39,59 +128,559 @@ add_channel(void)
return idx; return idx;
if (channel_count == MAX_OPEN_CHANNELS) if (channel_count == MAX_OPEN_CHANNELS)
return -1; 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) if (new_channels == NULL)
return -1; return -1;
if (channels != NULL) if (channels != NULL)
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
channels = new_channels; channels = new_channels;
(void)vim_memset(&channels[channel_count], 0, sizeof(channel_T));
channels[channel_count].ch_fd = (sock_T)-1; 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++; return channel_count++;
} }
#if defined(FEAT_NETBEANS_INTG) || defined(PROTO) #if defined(FEAT_GUI) || defined(PROTO)
static int netbeans_channel = -1;
/* /*
* Add the netbeans socket to the channels. * Read a command from netbeans.
* Return the channel index.
*/ */
int #ifdef FEAT_GUI_X11
channel_add_netbeans(sock_T fd) static void
messageFromNetbeans(XtPointer clientData,
int *unused1 UNUSED,
XtInputId *unused2 UNUSED)
{ {
int idx = add_channel(); channel_read((int)(long)clientData);
if (idx >= 0)
{
channels[idx].ch_fd = fd;
netbeans_channel = idx;
}
return idx;
} }
#endif
void #ifdef FEAT_GUI_GTK
channel_remove_netbeans() static void
messageFromNetbeans(gpointer clientData,
gint unused1 UNUSED,
GdkInputCondition unused2 UNUSED)
{ {
channels[netbeans_channel].ch_fd = (sock_T)-1; channel_read((int)(long)clientData);
netbeans_channel = -1;
} }
#endif #endif
static void static void
channel_read(int idx) channel_gui_register(int idx)
{ {
# ifdef FEAT_NETBEANS_INTG channel_T *channel = &channels[idx];
if (idx == netbeans_channel)
netbeans_read(); if (!CH_HAS_GUI)
else 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 # 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)
{ {
; /* TODO: read */ 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
} }
} }
#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. * Add open channels to the poll struct.
* Return the adjusted struct index. * Return the adjusted struct index.
@@ -138,9 +727,9 @@ channel_poll_check(int ret_in, void *fds_in)
return ret; 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. * 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; return ret;
} }
#endif /* UNIX && HAVE_SELECT */ # endif /* UNIX && HAVE_SELECT */
#endif /* FEAT_CHANNEL */ #endif /* FEAT_CHANNEL */

View File

@@ -5004,8 +5004,8 @@ ex_gui(eap)
* of the argument ending up after the shell prompt. */ * of the argument ending up after the shell prompt. */
msg_clr_eos_force(); msg_clr_eos_force();
gui_start(); gui_start();
#ifdef FEAT_NETBEANS_INTG #ifdef FEAT_CHANNEL
netbeans_gui_register(); channel_gui_register_all();
#endif #endif
} }
if (!ends_excmd(*eap->arg)) if (!ends_excmd(*eap->arg))

View File

@@ -1779,9 +1779,10 @@ process_message(void)
} }
#endif #endif
#ifdef FEAT_NETBEANS_INTG #ifdef FEAT_CHANNEL
if (msg.message == WM_NETBEANS) if (msg.message == WM_NETBEANS)
{ {
/* TODO: channel_read(idx) */
netbeans_read(); netbeans_read();
return; return;
} }

View File

@@ -27,36 +27,11 @@
#if defined(FEAT_NETBEANS_INTG) || defined(PROTO) #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
/* Note: when making changes here also adjust configure.in. */ #ifndef WIN32
#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
# include <netdb.h> # include <netdb.h>
# include <netinet/in.h>
# include <sys/socket.h>
# ifdef HAVE_LIBGEN_H # ifdef HAVE_LIBGEN_H
# include <libgen.h> # include <libgen.h>
# endif # 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 #endif
#include "version.h" #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 nb_init_graphics __ARGS((void));
static void coloncmd __ARGS((char *cmd, ...)); static void coloncmd __ARGS((char *cmd, ...));
static void nb_set_curbuf __ARGS((buf_T *buf)); 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 void nb_parse_cmd __ARGS((char_u *));
static int nb_do_cmd __ARGS((int, char_u *, int, int, 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_send __ARGS((char *buf, char *fun));
static void nb_free __ARGS((void)); static void nb_free __ARGS((void));
/* TRUE when netbeans is running with a GUI. */ #define NETBEANS_OPEN (nb_channel_idx >= 0 && channel_is_open(nb_channel_idx))
#ifdef FEAT_GUI static int nb_channel_idx = -1;
# 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 r_cmdno; /* current command number for reply */
static int dosetvisible = FALSE; static int dosetvisible = FALSE;
@@ -126,48 +80,17 @@ static int needupdate = 0;
static int inAtomic = 0; static int inAtomic = 0;
/* /*
* Close the socket and remove the input handlers. * Callback invoked when the channel is closed.
*/ */
static void static void
nb_close_socket(void) nb_channel_closed(void)
{ {
buf_T *buf; nb_channel_idx = -1;
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. * 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 static void
netbeans_close(void) netbeans_close(void)
@@ -175,7 +98,10 @@ netbeans_close(void)
if (NETBEANS_OPEN) if (NETBEANS_OPEN)
{ {
netbeans_send_disconnect(); 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 #ifdef FEAT_BEVAL
@@ -209,14 +135,7 @@ netbeans_close(void)
static int static int
netbeans_connect(char *params, int doabort) netbeans_connect(char *params, int doabort)
{ {
struct sockaddr_in server; int port;
struct hostent * host;
#ifdef FEAT_GUI_W32
u_short port;
#else
int port;
#endif
int sd;
char buf[32]; char buf[32];
char *hostname = NULL; char *hostname = NULL;
char *address = NULL; char *address = NULL;
@@ -291,107 +210,29 @@ netbeans_connect(char *params, int doabort)
vim_free(password); vim_free(password);
password = (char *)vim_strsave((char_u *)NB_DEF_PASS); password = (char *)vim_strsave((char_u *)NB_DEF_PASS);
} }
if (hostname == NULL || address == NULL || password == NULL) 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")); port = atoi(address);
PERROR("socket() in netbeans_connect()"); nb_channel_idx = channel_open(hostname, port, nb_channel_closed);
goto theend; if (nb_channel_idx >= 0)
}
/* 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); /* success */
if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) # ifdef FEAT_BEVAL
{ bevalServers |= BEVAL_NETBEANS;
SOCK_ERRNO; # endif
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; /* success, login */
while (retries-- && ((errno == ECONNREFUSED) vim_snprintf(buf, sizeof(buf), "AUTH %s\n", password);
|| (errno == EINTR))) nb_send(buf, "netbeans_connect");
{
nbdebug(("retrying...\n")); sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion);
mch_delay(3000L, TRUE); nb_send(buf, "externaleditor_version");
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; if (nb_channel_idx < 0 && doabort)
channel_add_netbeans(nbsock); getout(1);
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(hostname);
vim_free(address); vim_free(address);
vim_free(password); 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. * While there's still a command in the work queue, parse and execute it.
*/ */
void void
netbeans_parse_messages(void) netbeans_parse_messages(void)
{ {
char_u *buffer;
char_u *p; char_u *p;
queue_T *node;
int own_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. */ /* Locate the first line in the first buffer. */
p = vim_strchr(node->buffer, '\n'); p = vim_strchr(buffer, '\n');
if (p == NULL) if (p == NULL)
{ {
/* Command isn't complete. If there is no following buffer, /* Command isn't complete. If there is no following buffer,
* return (wait for more). If there is another buffer following, * return (wait for more). If there is another buffer following,
* prepend the text to that buffer and delete this one. */ * prepend the text to that buffer and delete this one. */
if (node->next == &head) if (channel_collapse(nb_channel_idx) == FAIL)
return; 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 else
{ {
@@ -648,147 +409,30 @@ netbeans_parse_messages(void)
if (*p == NUL) if (*p == NUL)
{ {
own_node = TRUE; own_node = TRUE;
head.next = node->next; channel_get(nb_channel_idx);
node->next->prev = node->prev;
} }
else else
own_node = FALSE; own_node = FALSE;
/* now, parse and execute the commands */ /* now, parse and execute the commands */
nb_parse_cmd(node->buffer); nb_parse_cmd(buffer);
if (own_node) if (own_node)
{ /* buffer finished, dispose of it */
/* buffer finished, dispose of the node and buffer */ vim_free(buffer);
vim_free(node->buffer); else
vim_free(node); /* more follows, move it to the start */
} STRMOVE(buffer, p);
/* 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. */ /* TODO: remove */
#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 void
netbeans_read() netbeans_read()
{ {
static char_u *buf = NULL; if (nb_channel_idx >= 0)
int len = 0; channel_read(nb_channel_idx);
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
} }
/* /*
@@ -825,8 +469,13 @@ nb_parse_cmd(char_u *cmd)
/* NOTREACHED */ /* 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. */ /* The IDE is breaking the connection. */
netbeans_close(); netbeans_close();
return; return;
@@ -923,7 +572,6 @@ static int curPCtype = -1;
nb_free() nb_free()
{ {
keyQ_T *key_node = keyHead.next; keyQ_T *key_node = keyHead.next;
queue_T *cmd_node = head.next;
nbbuf_T buf; nbbuf_T buf;
int i; int i;
@@ -960,19 +608,8 @@ nb_free()
} }
/* free the queued netbeans commands */ /* free the queued netbeans commands */
while (cmd_node != NULL && cmd_node != &head) if (nb_channel_idx >= 0)
{ channel_clear(nb_channel_idx);
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;
}
} }
/* /*
@@ -1116,40 +753,19 @@ netbeans_end(void)
sprintf(buf, "%d:killed=%d\n", i, r_cmdno); sprintf(buf, "%d:killed=%d\n", i, r_cmdno);
nbdebug(("EVT: %s", buf)); nbdebug(("EVT: %s", buf));
/* nb_send(buf, "netbeans_end"); avoid "write failed" messages */ /* 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. * Send a message to netbeans.
* When "fun" is NULL no error is given.
*/ */
static void static void
nb_send(char *buf, char *fun) nb_send(char *buf, char *fun)
{ {
/* Avoid giving pages full of error messages when the other side has if (nb_channel_idx >= 0)
* exited, only mention the first error until the connection works again. */ channel_send(nb_channel_idx, (char_u *)buf, fun);
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;
} }
/* /*
@@ -2924,52 +2540,6 @@ netbeans_active(void)
return NETBEANS_OPEN; 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. * 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) if (netbeans_connect(params, doabort) != OK)
return; return;
#ifdef FEAT_GUI
netbeans_gui_register();
#endif
nbdebug(("EVT: %s", cmd)); nbdebug(("EVT: %s", cmd));
nb_send(cmd, "netbeans_startup_done"); nb_send(cmd, "netbeans_startup_done");

View File

@@ -1,6 +1,15 @@
/* channel.c */ /* channel.c */
int channel_add_netbeans(sock_T fd); void channel_gui_register_all(void);
void channel_remove_netbeans(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_setup(int nfd_in, void *fds_in);
int channel_poll_check(int ret_in, void *fds_in); int channel_poll_check(int ret_in, void *fds_in);
int channel_select_setup(int maxfd_in, void *rfds_in); int channel_select_setup(int maxfd_in, void *rfds_in);

View File

@@ -9,7 +9,6 @@ void ex_nbkey(exarg_T *eap);
void ex_nbstart(exarg_T *eap); void ex_nbstart(exarg_T *eap);
void netbeans_beval_cb(BalloonEval *beval, int state); void netbeans_beval_cb(BalloonEval *beval, int state);
int netbeans_active(void); int netbeans_active(void);
void netbeans_gui_register(void);
void netbeans_open(char *params, int doabort); void netbeans_open(char *params, int doabort);
void netbeans_send_disconnect(void); void netbeans_send_disconnect(void);
void netbeans_frame_moved(int new_x, int new_y); void netbeans_frame_moved(int new_x, int new_y);

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1182,
/**/ /**/
1181, 1181,
/**/ /**/