blob: fc738c522df10ba9fdc189ebd7cb70add13cc074 [file] [log] [blame]
Bram Moolenaare0874f82016-01-24 20:36:41 +01001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 */
8
9/*
10 * Implements communication through a socket or any file handle.
11 */
12
13#include "vim.h"
14
15#if defined(FEAT_CHANNEL) || defined(PROTO)
16
Bram Moolenaard04a0202016-01-26 23:30:18 +010017/*
18 * Change the zero to 1 to enable debugging.
19 * This will write a file "channel_debug.log".
20 */
21#if 0
22# define CHERROR(fmt, arg) cherror(fmt, arg)
23# define CHLOG(idx, send, buf) chlog(idx, send, buf)
24# define CHFILE "channel_debug.log"
25
26static void cherror(char *fmt, char *arg);
27static void chlog(int send, char_u *buf);
28#else
29# define CHERROR(fmt, arg)
30# define CHLOG(idx, send, buf)
31#endif
32
33/* TRUE when netbeans is running with a GUI. */
34#ifdef FEAT_GUI
35# define CH_HAS_GUI (gui.in_use || gui.starting)
36#endif
37
38/* Note: when making changes here also adjust configure.in. */
39#ifdef WIN32
40/* WinSock API is separated from C API, thus we can't use read(), write(),
41 * errno... */
42# define SOCK_ERRNO errno = WSAGetLastError()
43# undef ECONNREFUSED
44# define ECONNREFUSED WSAECONNREFUSED
45# ifdef EINTR
46# undef EINTR
47# endif
48# define EINTR WSAEINTR
49# define sock_write(sd, buf, len) send(sd, buf, len, 0)
50# define sock_read(sd, buf, len) recv(sd, buf, len, 0)
51# define sock_close(sd) closesocket(sd)
52# define sleep(t) Sleep(t*1000) /* WinAPI Sleep() accepts milliseconds */
53#else
54# include <netdb.h>
55# include <netinet/in.h>
56
57# include <sys/socket.h>
58# ifdef HAVE_LIBGEN_H
59# include <libgen.h>
60# endif
61# define SOCK_ERRNO
62# define sock_write(sd, buf, len) write(sd, buf, len)
63# define sock_read(sd, buf, len) read(sd, buf, len)
64# define sock_close(sd) close(sd)
65#endif
66
67#ifdef FEAT_GUI_W32
68extern HWND s_hwnd; /* Gvim's Window handle */
69#endif
70
71struct readqueue
72{
73 char_u *buffer;
74 struct readqueue *next;
75 struct readqueue *prev;
76};
77typedef struct readqueue queue_T;
78
Bram Moolenaare0874f82016-01-24 20:36:41 +010079typedef struct {
Bram Moolenaard04a0202016-01-26 23:30:18 +010080 sock_T ch_fd; /* the socket, -1 for a closed channel */
81 int ch_idx; /* used by channel_poll_setup() */
82 queue_T ch_head; /* dummy node, header for circular queue */
83
84 int ch_error; /* When TRUE an error was reported. Avoids giving
85 * pages full of error messages when the other side
86 * has exited, only mention the first error until the
87 * connection works again. */
88#ifdef FEAT_GUI_X11
89 XtInputId ch_inputHandler; /* Cookie for input */
90#endif
91#ifdef FEAT_GUI_GTK
92 gint ch_inputHandler; /* Cookie for input */
93#endif
94#ifdef FEAT_GUI_W32
Bram Moolenaarfb4194e2016-01-26 23:39:55 +010095 int ch_inputHandler; /* simply ret.value of WSAAsyncSelect() */
Bram Moolenaard04a0202016-01-26 23:30:18 +010096#endif
97
98 void (*ch_close_cb)(void); /* callback invoked when channel is closed */
Bram Moolenaare0874f82016-01-24 20:36:41 +010099} channel_T;
100
Bram Moolenaard04a0202016-01-26 23:30:18 +0100101/*
102 * Information about all channels.
103 * There can be gaps for closed channels, they will be reused later.
104 */
Bram Moolenaare0874f82016-01-24 20:36:41 +0100105static channel_T *channels = NULL;
106static int channel_count = 0;
107
108/*
Bram Moolenaard04a0202016-01-26 23:30:18 +0100109 * TODO: open debug file when desired.
110 */
111FILE *debugfd = NULL;
112
113/*
Bram Moolenaare0874f82016-01-24 20:36:41 +0100114 * Add a new channel slot, return the index.
Bram Moolenaard04a0202016-01-26 23:30:18 +0100115 * The channel isn't actually used into ch_fd is set >= 0;
116 * Returns -1 if all channels are in use.
Bram Moolenaare0874f82016-01-24 20:36:41 +0100117 */
118 static int
119add_channel(void)
120{
121 int idx;
122 channel_T *new_channels;
123
124 if (channels != NULL)
125 for (idx = 0; idx < channel_count; ++idx)
126 if (channels[idx].ch_fd < 0)
127 /* re-use a closed channel slot */
128 return idx;
129 if (channel_count == MAX_OPEN_CHANNELS)
130 return -1;
Bram Moolenaard04a0202016-01-26 23:30:18 +0100131 new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1));
Bram Moolenaare0874f82016-01-24 20:36:41 +0100132 if (new_channels == NULL)
133 return -1;
134 if (channels != NULL)
135 mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
136 channels = new_channels;
Bram Moolenaard04a0202016-01-26 23:30:18 +0100137 (void)vim_memset(&channels[channel_count], 0, sizeof(channel_T));
138
Bram Moolenaare0874f82016-01-24 20:36:41 +0100139 channels[channel_count].ch_fd = (sock_T)-1;
Bram Moolenaard04a0202016-01-26 23:30:18 +0100140#ifdef FEAT_GUI_X11
141 channels[channel_count].ch_inputHandler = (XtInputId)NULL;
142#endif
143#ifdef FEAT_GUI_GTK
144 channels[channel_count].ch_inputHandler = 0;
145#endif
146#ifdef FEAT_GUI_W32
147 channels[channel_count].ch_inputHandler = -1;
148#endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100149
150 return channel_count++;
151}
152
Bram Moolenaard04a0202016-01-26 23:30:18 +0100153#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100154/*
Bram Moolenaard04a0202016-01-26 23:30:18 +0100155 * Read a command from netbeans.
Bram Moolenaare0874f82016-01-24 20:36:41 +0100156 */
Bram Moolenaard04a0202016-01-26 23:30:18 +0100157#ifdef FEAT_GUI_X11
158 static void
159messageFromNetbeans(XtPointer clientData,
160 int *unused1 UNUSED,
161 XtInputId *unused2 UNUSED)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100162{
Bram Moolenaard04a0202016-01-26 23:30:18 +0100163 channel_read((int)(long)clientData);
Bram Moolenaare0874f82016-01-24 20:36:41 +0100164}
Bram Moolenaard04a0202016-01-26 23:30:18 +0100165#endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100166
Bram Moolenaard04a0202016-01-26 23:30:18 +0100167#ifdef FEAT_GUI_GTK
168 static void
169messageFromNetbeans(gpointer clientData,
170 gint unused1 UNUSED,
171 GdkInputCondition unused2 UNUSED)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100172{
Bram Moolenaard04a0202016-01-26 23:30:18 +0100173 channel_read((int)(long)clientData);
Bram Moolenaare0874f82016-01-24 20:36:41 +0100174}
175#endif
176
177 static void
Bram Moolenaard04a0202016-01-26 23:30:18 +0100178channel_gui_register(int idx)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100179{
Bram Moolenaard04a0202016-01-26 23:30:18 +0100180 channel_T *channel = &channels[idx];
181
182 if (!CH_HAS_GUI)
183 return;
184
185# ifdef FEAT_GUI_X11
186 /* tell notifier we are interested in being called
187 * when there is input on the editor connection socket
188 */
189 if (channel->ch_inputHandler == (XtInputId)NULL)
190 channel->ch_inputHandler =
191 XtAppAddInput((XtAppContext)app_context, channel->ch_fd,
192 (XtPointer)(XtInputReadMask + XtInputExceptMask),
193 messageFromNetbeans, (XtPointer)idx);
194# else
195# ifdef FEAT_GUI_GTK
196 /*
197 * Tell gdk we are interested in being called when there
198 * is input on the editor connection socket
199 */
200 if (channel->ch_inputHandler == 0)
201 channel->ch_inputHandler =
202 gdk_input_add((gint)channel->ch_fd, (GdkInputCondition)
203 ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
204 messageFromNetbeans, (gpointer)(long)idx);
205# else
206# ifdef FEAT_GUI_W32
207 /*
208 * Tell Windows we are interested in receiving message when there
209 * is input on the editor connection socket.
Bram Moolenaard04a0202016-01-26 23:30:18 +0100210 */
211 if (channel->ch_inputHandler == -1)
212 channel->ch_inputHandler =
213 WSAAsyncSelect(channel->ch_fd, s_hwnd, WM_NETBEANS, FD_READ);
214# endif
215# endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100216# endif
Bram Moolenaard04a0202016-01-26 23:30:18 +0100217}
218
219/*
220 * Register any of our file descriptors with the GUI event handling system.
221 * Called when the GUI has started.
222 */
223 void
224channel_gui_register_all(void)
225{
226 int i;
227
228 for (i = 0; i < channel_count; ++i)
229 if (channels[i].ch_fd >= 0)
230 channel_gui_register(i);
231}
232
233 static void
234channel_gui_unregister(int idx)
235{
236 channel_T *channel = &channels[idx];
237
238# ifdef FEAT_GUI_X11
239 if (channel->ch_inputHandler != (XtInputId)NULL)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100240 {
Bram Moolenaard04a0202016-01-26 23:30:18 +0100241 XtRemoveInput(channel->ch_inputHandler);
242 channel->ch_inputHandler = (XtInputId)NULL;
243 }
244# else
245# ifdef FEAT_GUI_GTK
246 if (channel->ch_inputHandler != 0)
247 {
248 gdk_input_remove(channel->ch_inputHandler);
249 channel->ch_inputHandler = 0;
250 }
251# else
252# ifdef FEAT_GUI_W32
253 if (channel->ch_inputHandler == 0)
254 {
Bram Moolenaar54e09e72016-01-26 23:49:31 +0100255 WSAAsyncSelect(channel->ch_fd, s_hwnd, 0, 0);
Bram Moolenaard04a0202016-01-26 23:30:18 +0100256 channel->ch_inputHandler = -1;
257 }
258# endif
259# endif
260# endif
261}
262
263#endif
264
265/*
266 * Open a channel to "hostname":"port".
267 * Returns the channel number for success.
268 * Returns a negative number for failure.
269 */
270 int
271channel_open(char *hostname, int port_in, void (*close_cb)(void))
272{
273 int sd;
274 struct sockaddr_in server;
275 struct hostent * host;
276#ifdef FEAT_GUI_W32
277 u_short port = port_in;
278#else
279 int port = port_in;
280#endif
281 int idx;
282
283#ifdef FEAT_GUI_W32
284 channel_init_winsock();
285#endif
286
287 idx = add_channel();
288 if (idx < 0)
289 {
290 CHERROR("All channels are in use\n", "");
291 EMSG(_("E999: All channels are in use"));
292 return -1;
293 }
294
295 if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
296 {
297 CHERROR("error in socket() in channel_open()\n", "");
298 PERROR("E999: socket() in channel_open()");
299 return -1;
300 }
301
302 /* Get the server internet address and put into addr structure */
303 /* fill in the socket address structure and connect to server */
304 vim_memset((char *)&server, 0, sizeof(server));
305 server.sin_family = AF_INET;
306 server.sin_port = htons(port);
307 if ((host = gethostbyname(hostname)) == NULL)
308 {
309 CHERROR("error in gethostbyname() in channel_open()\n", "");
310 PERROR("E999: gethostbyname() in channel_open()");
311 sock_close(sd);
312 return -1;
313 }
314 memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
315
316 /* Connect to server */
317 if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
318 {
319 SOCK_ERRNO;
320 CHERROR("channel_open: Connect failed with errno %d\n", errno);
321 if (errno == ECONNREFUSED)
322 {
323 sock_close(sd);
324 if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
325 {
326 SOCK_ERRNO;
327 CHERROR("socket() retry in channel_open()\n", "");
328 PERROR("E999: socket() retry in channel_open()");
329 return -1;
330 }
331 if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
332 {
333 int retries = 36;
334 int success = FALSE;
335
336 SOCK_ERRNO;
337 while (retries-- && ((errno == ECONNREFUSED)
338 || (errno == EINTR)))
339 {
340 CHERROR("retrying...\n", "");
341 mch_delay(3000L, TRUE);
342 ui_breakcheck();
343 if (got_int)
344 {
345 errno = EINTR;
346 break;
347 }
348 if (connect(sd, (struct sockaddr *)&server,
349 sizeof(server)) == 0)
350 {
351 success = TRUE;
352 break;
353 }
354 SOCK_ERRNO;
355 }
356 if (!success)
357 {
358 /* Get here when the server can't be found. */
359 CHERROR("Cannot connect to port after retry\n", "");
360 PERROR(_("E999: Cannot connect to port after retry2"));
361 sock_close(sd);
362 return -1;
363 }
364 }
365 }
366 else
367 {
368 CHERROR("Cannot connect to port\n", "");
369 PERROR(_("E999: Cannot connect to port"));
370 sock_close(sd);
371 return -1;
372 }
373 }
374
375 channels[idx].ch_fd = sd;
376 channels[idx].ch_close_cb = close_cb;
377
378#ifdef FEAT_GUI
379 channel_gui_register(idx);
380#endif
381
382 return idx;
383}
384
385/*
386 * Return TRUE when channel "idx" is open.
387 */
388 int
389channel_is_open(int idx)
390{
391 return channels[idx].ch_fd >= 0;
392}
393
394/*
395 * Close channel "idx".
396 * This does not trigger the close callback.
397 */
398 void
399channel_close(int idx)
400{
401 channel_T *channel = &channels[idx];
402
403 if (channel->ch_fd >= 0)
404 {
405 sock_close(channel->ch_fd);
406 channel->ch_fd = -1;
407#ifdef FEAT_GUI
408 channel_gui_unregister(idx);
409#endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100410 }
411}
412
Bram Moolenaard04a0202016-01-26 23:30:18 +0100413/*
414 * Store "buf[len]" on channel "idx".
415 */
416 void
417channel_save(int idx, char_u *buf, int len)
418{
419 queue_T *node;
420 queue_T *head = &channels[idx].ch_head;
421
422 node = (queue_T *)alloc(sizeof(queue_T));
423 if (node == NULL)
424 return; /* out of memory */
425 node->buffer = alloc(len + 1);
426 if (node->buffer == NULL)
427 {
428 vim_free(node);
429 return; /* out of memory */
430 }
431 mch_memmove(node->buffer, buf, (size_t)len);
432 node->buffer[len] = NUL;
433
434 if (head->next == NULL) /* initialize circular queue */
435 {
436 head->next = head;
437 head->prev = head;
438 }
439
440 /* insert node at tail of queue */
441 node->next = head;
442 node->prev = head->prev;
443 head->prev->next = node;
444 head->prev = node;
445
446 if (debugfd != NULL)
447 {
448 fprintf(debugfd, "RECV on %d: ", idx);
449 fwrite(buf, len, 1, debugfd);
450 fprintf(debugfd, "\n");
451 }
452}
453
454/*
455 * Return the first buffer from the channel without removing it.
456 * Returns NULL if there is nothing.
457 */
458 char_u *
459channel_peek(int idx)
460{
461 queue_T *head = &channels[idx].ch_head;
462
463 if (head->next == head || head->next == NULL)
464 return NULL;
465 return head->next->buffer;
466}
467
468/*
469 * Return the first buffer from the channel and remove it.
470 * The caller must free it.
471 * Returns NULL if there is nothing.
472 */
473 char_u *
474channel_get(int idx)
475{
476 queue_T *head = &channels[idx].ch_head;
477 queue_T *node;
478 char_u *p;
479
480 if (head->next == head || head->next == NULL)
481 return NULL;
482 node = head->next;
483 /* dispose of the node but keep the buffer */
484 p = node->buffer;
485 head->next = node->next;
486 node->next->prev = node->prev;
487 vim_free(node);
488 return p;
489}
490
491/*
492 * Collapses the first and second buffer in the channel "idx".
493 * Returns FAIL if that is not possible.
494 */
495 int
496channel_collapse(int idx)
497{
498 queue_T *head = &channels[idx].ch_head;
499 queue_T *node = head->next;
500 char_u *p;
501
502 if (node == head || node == NULL || node->next == head)
503 return FAIL;
504
505 p = alloc((unsigned)(STRLEN(node->buffer)
506 + STRLEN(node->next->buffer) + 1));
507 if (p == NULL)
508 return FAIL; /* out of memory */
509 STRCPY(p, node->buffer);
510 STRCAT(p, node->next->buffer);
511 vim_free(node->next->buffer);
512 node->next->buffer = p;
513
514 /* dispose of the node and buffer */
515 head->next = node->next;
516 node->next->prev = node->prev;
517 vim_free(node->buffer);
518 vim_free(node);
519 return OK;
520}
521
522/*
523 * Clear the read buffer on channel "idx".
524 */
525 void
526channel_clear(int idx)
527{
528 queue_T *head = &channels[idx].ch_head;
529 queue_T *node = head->next;
530 queue_T *next;
531
532 while (node != NULL && node != head)
533 {
534 next = node->next;
535 vim_free(node->buffer);
536 vim_free(node);
537 if (next == head)
538 {
539 head->next = head;
540 head->prev = head;
541 break;
542 }
543 node = next;
544 }
545}
546
547/* Sent when the channel is found closed when reading. */
548#define DETACH_MSG "\"DETACH\"\n"
549
550/* Buffer size for reading incoming messages. */
551#define MAXMSGSIZE 4096
552
553/*
554 * Read from channel "idx". The data is put in the read queue.
555 */
556 void
557channel_read(int idx)
558{
559 static char_u *buf = NULL;
560 int len = 0;
561 int readlen = 0;
562#ifdef HAVE_SELECT
563 struct timeval tval;
564 fd_set rfds;
565#else
566# ifdef HAVE_POLL
567 struct pollfd fds;
568# endif
569#endif
570 channel_T *channel = &channels[idx];
571
572 if (channel->ch_fd < 0)
573 {
574 CHLOG(idx, FALSE, "channel_read() called while socket is closed\n");
575 return;
576 }
577
578 /* Allocate a buffer to read into. */
579 if (buf == NULL)
580 {
581 buf = alloc(MAXMSGSIZE);
582 if (buf == NULL)
583 return; /* out of memory! */
584 }
585
586 /* Keep on reading for as long as there is something to read.
587 * Use select() or poll() to avoid blocking on a message that is exactly
588 * MAXMSGSIZE long. */
589 for (;;)
590 {
591#ifdef HAVE_SELECT
592 FD_ZERO(&rfds);
593 FD_SET(channel->ch_fd, &rfds);
594 tval.tv_sec = 0;
595 tval.tv_usec = 0;
596 if (select(channel->ch_fd + 1, &rfds, NULL, NULL, &tval) <= 0)
597 break;
598#else
599# ifdef HAVE_POLL
600 fds.fd = channel->ch_fd;
601 fds.events = POLLIN;
602 if (poll(&fds, 1, 0) <= 0)
603 break;
604# endif
605#endif
606 len = sock_read(channel->ch_fd, buf, MAXMSGSIZE);
607 if (len <= 0)
608 break; /* error or nothing more to read */
609
610 /* Store the read message in the queue. */
611 channel_save(idx, buf, len);
612 readlen += len;
613 if (len < MAXMSGSIZE)
614 break; /* did read everything that's available */
615 }
616
617 /* Reading a socket disconnection (readlen == 0), or a socket error. */
618 if (readlen <= 0)
619 {
620 /* Queue a "DETACH" netbeans message in the command queue in order to
621 * terminate the netbeans session later. Do not end the session here
622 * directly as we may be running in the context of a call to
623 * netbeans_parse_messages():
624 * netbeans_parse_messages
625 * -> autocmd triggered while processing the netbeans cmd
626 * -> ui_breakcheck
627 * -> gui event loop or select loop
628 * -> channel_read()
629 */
630 channel_save(idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG));
631
632 channel_close(idx);
633 if (channel->ch_close_cb != NULL)
634 (*channel->ch_close_cb)();
635
636 if (len < 0)
637 {
638 /* Todo: which channel? */
639 CHERROR("%s(): cannot from channel\n", "channel_read");
640 PERROR(_("E999: read from channel"));
641 }
642 }
643
644#if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
645 if (CH_HAS_GUI && gtk_main_level() > 0)
646 gtk_main_quit();
647#endif
648}
649
Bram Moolenaar85be35f2016-01-27 21:08:18 +0100650# if defined(FEAT_GUI_W32) || defined(PROTO)
651/*
652 * Lookup the channel index from the socket.
653 * Returns -1 when the socket isn't found.
654 */
655 int
656channel_socket2idx(sock_T fd)
657{
658 int i;
659
660 if (fd >= 0)
661 for (i = 0; i < channel_count; ++i)
662 if (channels[i].ch_fd == fd)
663 return i;
664 return -1;
665}
666# endif
667
Bram Moolenaard04a0202016-01-26 23:30:18 +0100668/*
669 * Write "buf" (NUL terminated string) to channel "idx".
670 * When "fun" is not NULL an error message might be given.
671 */
672 void
673channel_send(int idx, char_u *buf, char *fun)
674{
675 channel_T *channel = &channels[idx];
676 int len = (int)STRLEN(buf);
677
678 if (channel->ch_fd < 0)
679 {
680 if (!channel->ch_error && fun != NULL)
681 {
682 CHERROR(" %s(): write while not connected\n", fun);
683 EMSG2("E630: %s(): write while not connected", fun);
684 }
685 channel->ch_error = TRUE;
686 }
687 else if (sock_write(channel->ch_fd, buf, len) != len)
688 {
689 if (!channel->ch_error && fun != NULL)
690 {
691 CHERROR(" %s(): write failed\n", fun);
692 EMSG2("E631: %s(): write failed", fun);
693 }
694 channel->ch_error = TRUE;
695 }
696 else
697 channel->ch_error = FALSE;
698}
699
700# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100701/*
702 * Add open channels to the poll struct.
703 * Return the adjusted struct index.
704 * The type of "fds" is hidden to avoid problems with the function proto.
705 */
706 int
707channel_poll_setup(int nfd_in, void *fds_in)
708{
709 int nfd = nfd_in;
710 int i;
711 struct pollfd *fds = fds_in;
712
713 for (i = 0; i < channel_count; ++i)
714 if (channels[i].ch_fd >= 0)
715 {
716 channels[i].ch_idx = nfd;
717 fds[nfd].fd = channels[i].ch_fd;
718 fds[nfd].events = POLLIN;
719 nfd++;
720 }
721 else
722 channels[i].ch_idx = -1;
723
724 return nfd;
725}
726
727/*
728 * The type of "fds" is hidden to avoid problems with the function proto.
729 */
730 int
731channel_poll_check(int ret_in, void *fds_in)
732{
733 int ret = ret_in;
734 int i;
735 struct pollfd *fds = fds_in;
736
737 for (i = 0; i < channel_count; ++i)
738 if (ret > 0 && channels[i].ch_idx != -1
739 && fds[channels[i].ch_idx].revents & POLLIN)
740 {
741 channel_read(i);
742 --ret;
743 }
744
745 return ret;
746}
Bram Moolenaard04a0202016-01-26 23:30:18 +0100747# endif /* UNIX && !HAVE_SELECT */
Bram Moolenaare0874f82016-01-24 20:36:41 +0100748
Bram Moolenaard04a0202016-01-26 23:30:18 +0100749# if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100750/*
751 * The type of "rfds" is hidden to avoid problems with the function proto.
752 */
753 int
754channel_select_setup(int maxfd_in, void *rfds_in)
755{
756 int maxfd = maxfd_in;
757 int i;
758 fd_set *rfds = rfds_in;
759
760 for (i = 0; i < channel_count; ++i)
761 if (channels[i].ch_fd >= 0)
762 {
763 FD_SET(channels[i].ch_fd, rfds);
764 if (maxfd < channels[i].ch_fd)
765 maxfd = channels[i].ch_fd;
766 }
767
768 return maxfd;
769}
770
771/*
772 * The type of "rfds" is hidden to avoid problems with the function proto.
773 */
774 int
775channel_select_check(int ret_in, void *rfds_in)
776{
777 int ret = ret_in;
778 int i;
779 fd_set *rfds = rfds_in;
780
781 for (i = 0; i < channel_count; ++i)
782 if (ret > 0 && channels[i].ch_fd >= 0
783 && FD_ISSET(channels[i].ch_fd, rfds))
784 {
785 channel_read(i);
786 --ret;
787 }
788
789 return ret;
790}
Bram Moolenaard04a0202016-01-26 23:30:18 +0100791# endif /* UNIX && HAVE_SELECT */
Bram Moolenaare0874f82016-01-24 20:36:41 +0100792
793#endif /* FEAT_CHANNEL */