/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* Implements communication through a socket or any file handle.
*/
#include "vim.h"
#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 readq_T;
struct jsonqueue
{
typval_T *value;
struct jsonqueue *next;
struct jsonqueue *prev;
};
typedef struct jsonqueue jsonq_T;
typedef struct {
sock_T ch_fd; /* the socket, -1 for a closed channel */
int ch_idx; /* used by channel_poll_setup() */
readq_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 WIN32
int ch_inputHandler; /* simply ret.value of WSAAsyncSelect() */
#endif
void (*ch_close_cb)(void); /* callback for when channel is closed */
char_u *ch_callback; /* function to call when a msg is not handled */
char_u *ch_req_callback; /* function to call for current request */
int ch_json_mode; /* TRUE for a json channel */
jsonq_T ch_json_head; /* dummy node, header for circular queue */
} 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)
{
int idx;
channel_T *new_channels;
channel_T *ch;
if (channels != NULL)
for (idx = 0; idx < channel_count; ++idx)
if (channels[idx].ch_fd < 0)
/* re-use a closed channel slot */
return idx;
if (channel_count == MAX_OPEN_CHANNELS)
return -1;
new_channels = (channel_T *)