blob: 549c00fda5cbbd2a25abae6c2ca48d917c7f8dab [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.
210 * TODO: change WM_NETBEANS to something related to the channel index.
211 */
212 if (channel->ch_inputHandler == -1)
213 channel->ch_inputHandler =
214 WSAAsyncSelect(channel->ch_fd, s_hwnd, WM_NETBEANS, FD_READ);
215# endif
216# endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100217# endif
Bram Moolenaard04a0202016-01-26 23:30:18 +0100218}
219
220/*
221 * Register any of our file descriptors with the GUI event handling system.
222 * Called when the GUI has started.
223 */
224 void
225channel_gui_register_all(void)
226{
227 int i;
228
229 for (i = 0; i < channel_count; ++i)
230 if (channels[i].ch_fd >= 0)
231 channel_gui_register(i);
232}
233
234 static void
235channel_gui_unregister(int idx)
236{
237 channel_T *channel = &channels[idx];
238
239# ifdef FEAT_GUI_X11
240 if (channel->ch_inputHandler != (XtInputId)NULL)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100241 {
Bram Moolenaard04a0202016-01-26 23:30:18 +0100242 XtRemoveInput(channel->ch_inputHandler);
243 channel->ch_inputHandler = (XtInputId)NULL;
244 }
245# else
246# ifdef FEAT_GUI_GTK
247 if (channel->ch_inputHandler != 0)
248 {
249 gdk_input_remove(channel->ch_inputHandler);
250 channel->ch_inputHandler = 0;
251 }
252# else
253# ifdef FEAT_GUI_W32
254 if (channel->ch_inputHandler == 0)
255 {
256 WSAAsyncSelect(nbsock, s_hwnd, 0, 0);
257 channel->ch_inputHandler = -1;
258 }
259# endif
260# endif
261# endif
262}
263
264#endif
265
266/*
267 * Open a channel to "hostname":"port".
268 * Returns the channel number for success.
269 * Returns a negative number for failure.
270 */
271 int
272channel_open(char *hostname, int port_in, void (*close_cb)(void))
273{
274 int sd;
275 struct sockaddr_in server;
276 struct hostent * host;
277#ifdef FEAT_GUI_W32
278 u_short port = port_in;
279#else
280 int port = port_in;
281#endif
282 int idx;
283
284#ifdef FEAT_GUI_W32
285 channel_init_winsock();
286#endif
287
288 idx = add_channel();
289 if (idx < 0)
290 {
291 CHERROR("All channels are in use\n", "");
292 EMSG(_("E999: All channels are in use"));
293 return -1;
294 }
295
296 if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
297 {
298 CHERROR("error in socket() in channel_open()\n", "");
299 PERROR("E999: socket() in channel_open()");
300 return -1;
301 }
302
303 /* Get the server internet address and put into addr structure */
304 /* fill in the socket address structure and connect to server */
305 vim_memset((char *)&server, 0, sizeof(server));
306 server.sin_family = AF_INET;
307 server.sin_port = htons(port);
308 if ((host = gethostbyname(hostname)) == NULL)
309 {
310 CHERROR("error in gethostbyname() in channel_open()\n", "");
311 PERROR("E999: gethostbyname() in channel_open()");
312 sock_close(sd);
313 return -1;
314 }
315 memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
316
317 /* Connect to server */
318 if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
319 {
320 SOCK_ERRNO;
321 CHERROR("channel_open: Connect failed with errno %d\n", errno);
322 if (errno == ECONNREFUSED)
323 {
324 sock_close(sd);
325 if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1)
326 {
327 SOCK_ERRNO;
328 CHERROR("socket() retry in channel_open()\n", "");
329 PERROR("E999: socket() retry in channel_open()");
330 return -1;
331 }
332 if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
333 {
334 int retries = 36;
335 int success = FALSE;
336
337 SOCK_ERRNO;
338 while (retries-- && ((errno == ECONNREFUSED)
339 || (errno == EINTR)))
340 {
341 CHERROR("retrying...\n", "");
342 mch_delay(3000L, TRUE);
343 ui_breakcheck();
344 if (got_int)
345 {
346 errno = EINTR;
347 break;
348 }
349 if (connect(sd, (struct sockaddr *)&server,
350 sizeof(server)) == 0)
351 {
352 success = TRUE;
353 break;
354 }
355 SOCK_ERRNO;
356 }
357 if (!success)
358 {
359 /* Get here when the server can't be found. */
360 CHERROR("Cannot connect to port after retry\n", "");
361 PERROR(_("E999: Cannot connect to port after retry2"));
362 sock_close(sd);
363 return -1;
364 }
365 }
366 }
367 else
368 {
369 CHERROR("Cannot connect to port\n", "");
370 PERROR(_("E999: Cannot connect to port"));
371 sock_close(sd);
372 return -1;
373 }
374 }
375
376 channels[idx].ch_fd = sd;
377 channels[idx].ch_close_cb = close_cb;
378
379#ifdef FEAT_GUI
380 channel_gui_register(idx);
381#endif
382
383 return idx;
384}
385
386/*
387 * Return TRUE when channel "idx" is open.
388 */
389 int
390channel_is_open(int idx)
391{
392 return channels[idx].ch_fd >= 0;
393}
394
395/*
396 * Close channel "idx".
397 * This does not trigger the close callback.
398 */
399 void
400channel_close(int idx)
401{
402 channel_T *channel = &channels[idx];
403
404 if (channel->ch_fd >= 0)
405 {
406 sock_close(channel->ch_fd);
407 channel->ch_fd = -1;
408#ifdef FEAT_GUI
409 channel_gui_unregister(idx);
410#endif
Bram Moolenaare0874f82016-01-24 20:36:41 +0100411 }
412}
413
Bram Moolenaard04a0202016-01-26 23:30:18 +0100414/*
415 * Store "buf[len]" on channel "idx".
416 */
417 void
418channel_save(int idx, char_u *buf, int len)
419{
420 queue_T *node;
421 queue_T *head = &channels[idx].ch_head;
422
423 node = (queue_T *)alloc(sizeof(queue_T));
424 if (node == NULL)
425 return; /* out of memory */
426 node->buffer = alloc(len + 1);
427 if (node->buffer == NULL)
428 {
429 vim_free(node);
430 return; /* out of memory */
431 }
432 mch_memmove(node->buffer, buf, (size_t)len);
433 node->buffer[len] = NUL;
434
435 if (head->next == NULL) /* initialize circular queue */
436 {
437 head->next = head;
438 head->prev = head;
439 }
440
441 /* insert node at tail of queue */
442 node->next = head;
443 node->prev = head->prev;
444 head->prev->next = node;
445 head->prev = node;
446
447 if (debugfd != NULL)
448 {
449 fprintf(debugfd, "RECV on %d: ", idx);
450 fwrite(buf, len, 1, debugfd);
451 fprintf(debugfd, "\n");
452 }
453}
454
455/*
456 * Return the first buffer from the channel without removing it.
457 * Returns NULL if there is nothing.
458 */
459 char_u *
460channel_peek(int idx)
461{
462 queue_T *head = &channels[idx].ch_head;
463
464 if (head->next == head || head->next == NULL)
465 return NULL;
466 return head->next->buffer;
467}
468
469/*
470 * Return the first buffer from the channel and remove it.
471 * The caller must free it.
472 * Returns NULL if there is nothing.
473 */
474 char_u *
475channel_get(int idx)
476{
477 queue_T *head = &channels[idx].ch_head;
478 queue_T *node;
479 char_u *p;
480
481 if (head->next == head || head->next == NULL)
482 return NULL;
483 node = head->next;
484 /* dispose of the node but keep the buffer */
485 p = node->buffer;
486 head->next = node->next;
487 node->next->prev = node->prev;
488 vim_free(node);
489 return p;
490}
491
492/*
493 * Collapses the first and second buffer in the channel "idx".
494 * Returns FAIL if that is not possible.
495 */
496 int
497channel_collapse(int idx)
498{
499 queue_T *head = &channels[idx].ch_head;
500 queue_T *node = head->next;
501 char_u *p;
502
503 if (node == head || node == NULL || node->next == head)
504 return FAIL;
505
506 p = alloc((unsigned)(STRLEN(node->buffer)
507 + STRLEN(node->next->buffer) + 1));
508 if (p == NULL)
509 return FAIL; /* out of memory */
510 STRCPY(p, node->buffer);
511 STRCAT(p, node->next->buffer);
512 vim_free(node->next->buffer);
513 node->next->buffer = p;
514
515 /* dispose of the node and buffer */
516 head->next = node->next;
517 node->next->prev = node->prev;
518 vim_free(node->buffer);
519 vim_free(node);
520 return OK;
521}
522
523/*
524 * Clear the read buffer on channel "idx".
525 */
526 void
527channel_clear(int idx)
528{
529 queue_T *head = &channels[idx].ch_head;
530 queue_T *node = head->next;
531 queue_T *next;
532
533 while (node != NULL && node != head)
534 {
535 next = node->next;
536 vim_free(node->buffer);
537 vim_free(node);
538 if (next == head)
539 {
540 head->next = head;
541 head->prev = head;
542 break;
543 }
544 node = next;
545 }
546}
547
548/* Sent when the channel is found closed when reading. */
549#define DETACH_MSG "\"DETACH\"\n"
550
551/* Buffer size for reading incoming messages. */
552#define MAXMSGSIZE 4096
553
554/*
555 * Read from channel "idx". The data is put in the read queue.
556 */
557 void
558channel_read(int idx)
559{
560 static char_u *buf = NULL;
561 int len = 0;
562 int readlen = 0;
563#ifdef HAVE_SELECT
564 struct timeval tval;
565 fd_set rfds;
566#else
567# ifdef HAVE_POLL
568 struct pollfd fds;
569# endif
570#endif
571 channel_T *channel = &channels[idx];
572
573 if (channel->ch_fd < 0)
574 {
575 CHLOG(idx, FALSE, "channel_read() called while socket is closed\n");
576 return;
577 }
578
579 /* Allocate a buffer to read into. */
580 if (buf == NULL)
581 {
582 buf = alloc(MAXMSGSIZE);
583 if (buf == NULL)
584 return; /* out of memory! */
585 }
586
587 /* Keep on reading for as long as there is something to read.
588 * Use select() or poll() to avoid blocking on a message that is exactly
589 * MAXMSGSIZE long. */
590 for (;;)
591 {
592#ifdef HAVE_SELECT
593 FD_ZERO(&rfds);
594 FD_SET(channel->ch_fd, &rfds);
595 tval.tv_sec = 0;
596 tval.tv_usec = 0;
597 if (select(channel->ch_fd + 1, &rfds, NULL, NULL, &tval) <= 0)
598 break;
599#else
600# ifdef HAVE_POLL
601 fds.fd = channel->ch_fd;
602 fds.events = POLLIN;
603 if (poll(&fds, 1, 0) <= 0)
604 break;
605# endif
606#endif
607 len = sock_read(channel->ch_fd, buf, MAXMSGSIZE);
608 if (len <= 0)
609 break; /* error or nothing more to read */
610
611 /* Store the read message in the queue. */
612 channel_save(idx, buf, len);
613 readlen += len;
614 if (len < MAXMSGSIZE)
615 break; /* did read everything that's available */
616 }
617
618 /* Reading a socket disconnection (readlen == 0), or a socket error. */
619 if (readlen <= 0)
620 {
621 /* Queue a "DETACH" netbeans message in the command queue in order to
622 * terminate the netbeans session later. Do not end the session here
623 * directly as we may be running in the context of a call to
624 * netbeans_parse_messages():
625 * netbeans_parse_messages
626 * -> autocmd triggered while processing the netbeans cmd
627 * -> ui_breakcheck
628 * -> gui event loop or select loop
629 * -> channel_read()
630 */
631 channel_save(idx, (char_u *)DETACH_MSG, (int)STRLEN(DETACH_MSG));
632
633 channel_close(idx);
634 if (channel->ch_close_cb != NULL)
635 (*channel->ch_close_cb)();
636
637 if (len < 0)
638 {
639 /* Todo: which channel? */
640 CHERROR("%s(): cannot from channel\n", "channel_read");
641 PERROR(_("E999: read from channel"));
642 }
643 }
644
645#if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
646 if (CH_HAS_GUI && gtk_main_level() > 0)
647 gtk_main_quit();
648#endif
649}
650
651/*
652 * Write "buf" (NUL terminated string) to channel "idx".
653 * When "fun" is not NULL an error message might be given.
654 */
655 void
656channel_send(int idx, char_u *buf, char *fun)
657{
658 channel_T *channel = &channels[idx];
659 int len = (int)STRLEN(buf);
660
661 if (channel->ch_fd < 0)
662 {
663 if (!channel->ch_error && fun != NULL)
664 {
665 CHERROR(" %s(): write while not connected\n", fun);
666 EMSG2("E630: %s(): write while not connected", fun);
667 }
668 channel->ch_error = TRUE;
669 }
670 else if (sock_write(channel->ch_fd, buf, len) != len)
671 {
672 if (!channel->ch_error && fun != NULL)
673 {
674 CHERROR(" %s(): write failed\n", fun);
675 EMSG2("E631: %s(): write failed", fun);
676 }
677 channel->ch_error = TRUE;
678 }
679 else
680 channel->ch_error = FALSE;
681}
682
683# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100684/*
685 * Add open channels to the poll struct.
686 * Return the adjusted struct index.
687 * The type of "fds" is hidden to avoid problems with the function proto.
688 */
689 int
690channel_poll_setup(int nfd_in, void *fds_in)
691{
692 int nfd = nfd_in;
693 int i;
694 struct pollfd *fds = fds_in;
695
696 for (i = 0; i < channel_count; ++i)
697 if (channels[i].ch_fd >= 0)
698 {
699 channels[i].ch_idx = nfd;
700 fds[nfd].fd = channels[i].ch_fd;
701 fds[nfd].events = POLLIN;
702 nfd++;
703 }
704 else
705 channels[i].ch_idx = -1;
706
707 return nfd;
708}
709
710/*
711 * The type of "fds" is hidden to avoid problems with the function proto.
712 */
713 int
714channel_poll_check(int ret_in, void *fds_in)
715{
716 int ret = ret_in;
717 int i;
718 struct pollfd *fds = fds_in;
719
720 for (i = 0; i < channel_count; ++i)
721 if (ret > 0 && channels[i].ch_idx != -1
722 && fds[channels[i].ch_idx].revents & POLLIN)
723 {
724 channel_read(i);
725 --ret;
726 }
727
728 return ret;
729}
Bram Moolenaard04a0202016-01-26 23:30:18 +0100730# endif /* UNIX && !HAVE_SELECT */
Bram Moolenaare0874f82016-01-24 20:36:41 +0100731
Bram Moolenaard04a0202016-01-26 23:30:18 +0100732# if (defined(UNIX) && defined(HAVE_SELECT)) || defined(PROTO)
Bram Moolenaare0874f82016-01-24 20:36:41 +0100733/*
734 * The type of "rfds" is hidden to avoid problems with the function proto.
735 */
736 int
737channel_select_setup(int maxfd_in, void *rfds_in)
738{
739 int maxfd = maxfd_in;
740 int i;
741 fd_set *rfds = rfds_in;
742
743 for (i = 0; i < channel_count; ++i)
744 if (channels[i].ch_fd >= 0)
745 {
746 FD_SET(channels[i].ch_fd, rfds);
747 if (maxfd < channels[i].ch_fd)
748 maxfd = channels[i].ch_fd;
749 }
750
751 return maxfd;
752}
753
754/*
755 * The type of "rfds" is hidden to avoid problems with the function proto.
756 */
757 int
758channel_select_check(int ret_in, void *rfds_in)
759{
760 int ret = ret_in;
761 int i;
762 fd_set *rfds = rfds_in;
763
764 for (i = 0; i < channel_count; ++i)
765 if (ret > 0 && channels[i].ch_fd >= 0
766 && FD_ISSET(channels[i].ch_fd, rfds))
767 {
768 channel_read(i);
769 --ret;
770 }
771
772 return ret;
773}
Bram Moolenaard04a0202016-01-26 23:30:18 +0100774# endif /* UNIX && HAVE_SELECT */
Bram Moolenaare0874f82016-01-24 20:36:41 +0100775
776#endif /* FEAT_CHANNEL */