Cast to (const char *) in strrchr calls
[elinks.git] / src / main / select.c
blob48d289211f0e084ed08c3932fad5561bf67d7c70
1 /* File descriptors managment and switching */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <signal.h>
9 #include <string.h> /* FreeBSD FD_ZERO() macro calls bzero() */
10 #ifdef HAVE_SYS_SIGNAL_H
11 #include <sys/signal.h>
12 #endif
13 #ifdef __GNU__ /* For GNU Hurd bug workaround in set_handlers() */
14 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
15 #endif
16 #include <sys/types.h>
17 #ifdef HAVE_SYS_WAIT_H
18 #include <sys/wait.h>
19 #endif
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
24 /* This must be here, thanks to BSD. */
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h> /* OMG */
27 #endif
28 #ifdef HAVE_SYS_SELECT_H
29 #include <sys/select.h>
30 #endif
32 #include "elinks.h"
34 #include "intl/gettext/libintl.h"
35 #include "main/main.h"
36 #include "main/select.h"
37 #include "main/timer.h"
38 #include "osdep/signals.h"
39 #include "terminal/terminal.h"
40 #include "util/error.h"
41 #include "util/memory.h"
42 #include "util/time.h"
45 #ifndef FD_SETSIZE
46 #define FD_SETSIZE 1024
47 #endif
49 struct thread {
50 select_handler_T read_func;
51 select_handler_T write_func;
52 select_handler_T error_func;
53 void *data;
56 #ifdef CONFIG_OS_WIN32
57 /* CreatePipe produces big numbers for handles */
58 #undef FD_SETSIZE
59 #define FD_SETSIZE 4096
60 #endif
62 static struct thread threads[FD_SETSIZE];
64 static fd_set w_read;
65 static fd_set w_write;
66 static fd_set w_error;
68 static fd_set x_read;
69 static fd_set x_write;
70 static fd_set x_error;
72 static int w_max;
74 int
75 get_file_handles_count(void)
77 int i = 0, j;
79 for (j = 0; j < FD_SETSIZE; j++)
80 if (threads[j].read_func
81 || threads[j].write_func
82 || threads[j].error_func)
83 i++;
84 return i;
87 struct bottom_half {
88 LIST_HEAD(struct bottom_half);
90 select_handler_T fn;
91 void *data;
94 static INIT_LIST_OF(struct bottom_half, bottom_halves);
96 int
97 register_bottom_half_do(select_handler_T fn, void *data)
99 struct bottom_half *bh;
101 foreach (bh, bottom_halves)
102 if (bh->fn == fn && bh->data == data)
103 return 0;
105 bh = mem_alloc(sizeof(*bh));
106 if (!bh) return -1;
107 bh->fn = fn;
108 bh->data = data;
109 add_to_list(bottom_halves, bh);
111 return 0;
114 void
115 check_bottom_halves(void)
117 while (!list_empty(bottom_halves)) {
118 struct bottom_half *bh = bottom_halves.prev;
119 select_handler_T fn = bh->fn;
120 void *data = bh->data;
122 del_from_list(bh);
123 mem_free(bh);
124 fn(data);
128 select_handler_T
129 get_handler(int fd, enum select_handler_type tp)
131 #ifndef CONFIG_OS_WIN32
132 assertm(fd >= 0 && fd < FD_SETSIZE,
133 "get_handler: handle %d >= FD_SETSIZE %d",
134 fd, FD_SETSIZE);
135 if_assert_failed return NULL;
136 #endif
137 switch (tp) {
138 case SELECT_HANDLER_READ: return threads[fd].read_func;
139 case SELECT_HANDLER_WRITE: return threads[fd].write_func;
140 case SELECT_HANDLER_ERROR: return threads[fd].error_func;
141 case SELECT_HANDLER_DATA: return threads[fd].data;
144 INTERNAL("get_handler: bad type %d", tp);
145 return NULL;
148 void
149 set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
150 select_handler_T error_func, void *data)
152 #ifndef CONFIG_OS_WIN32
153 assertm(fd >= 0 && fd < FD_SETSIZE,
154 "set_handlers: handle %d >= FD_SETSIZE %d",
155 fd, FD_SETSIZE);
156 if_assert_failed return;
157 #endif
158 #ifdef __GNU__
159 /* GNU Hurd pflocal bug <http://savannah.gnu.org/bugs/?22861>:
160 * If ELinks does a select() where the initial exceptfds set
161 * includes a pipe that is not listed in the other fd_sets,
162 * then select() always reports an exception there. That
163 * makes Elinks think the pipe has failed and close it.
164 * To work around this bug, do not monitor exceptions for
165 * pipes on the Hurd. */
166 if (error_func) {
167 struct stat st;
169 if (fstat(fd, &st) == 0 && S_ISFIFO(st.st_mode))
170 error_func = NULL;
172 #endif /* __GNU__ */
173 threads[fd].read_func = read_func;
174 threads[fd].write_func = write_func;
175 threads[fd].error_func = error_func;
176 threads[fd].data = data;
178 if (read_func) {
179 FD_SET(fd, &w_read);
180 } else {
181 FD_CLR(fd, &w_read);
182 FD_CLR(fd, &x_read);
185 if (write_func) {
186 FD_SET(fd, &w_write);
187 } else {
188 FD_CLR(fd, &w_write);
189 FD_CLR(fd, &x_write);
192 if (error_func) {
193 FD_SET(fd, &w_error);
194 } else {
195 FD_CLR(fd, &w_error);
196 FD_CLR(fd, &x_error);
199 if (read_func || write_func || error_func) {
200 if (fd >= w_max) w_max = fd + 1;
201 } else if (fd == w_max - 1) {
202 int i;
204 for (i = fd - 1; i >= 0; i--)
205 if (FD_ISSET(i, &w_read)
206 || FD_ISSET(i, &w_write)
207 || FD_ISSET(i, &w_error))
208 break;
209 w_max = i + 1;
213 void
214 select_loop(void (*init)(void))
216 timeval_T last_time;
217 int select_errors = 0;
219 clear_signal_mask_and_handlers();
220 FD_ZERO(&w_read);
221 FD_ZERO(&w_write);
222 FD_ZERO(&w_error);
223 w_max = 0;
224 timeval_now(&last_time);
225 #ifdef SIGPIPE
226 signal(SIGPIPE, SIG_IGN);
227 #endif
228 init();
229 check_bottom_halves();
231 while (!program.terminate) {
232 struct timeval *timeout = NULL;
233 int n, i, has_timer;
234 timeval_T t;
236 check_signals();
237 check_timers(&last_time);
238 redraw_all_terminals();
240 memcpy(&x_read, &w_read, sizeof(fd_set));
241 memcpy(&x_write, &w_write, sizeof(fd_set));
242 memcpy(&x_error, &w_error, sizeof(fd_set));
244 if (program.terminate) break;
246 has_timer = get_next_timer_time(&t);
247 if (!w_max && !has_timer) break;
248 critical_section = 1;
250 if (check_signals()) {
251 critical_section = 0;
252 continue;
254 #if 0
256 int i;
258 printf("\nR:");
259 for (i = 0; i < 256; i++)
260 if (FD_ISSET(i, &x_read)) printf("%d,", i);
261 printf("\nW:");
262 for (i = 0; i < 256; i++)
263 if (FD_ISSET(i, &x_write)) printf("%d,", i);
264 printf("\nE:");
265 for (i = 0; i < 256; i++)
266 if (FD_ISSET(i, &x_error)) printf("%d,", i);
267 fflush(stdout);
269 #endif
270 if (has_timer) {
271 /* Be sure timeout is not negative. */
272 timeval_limit_to_zero_or_one(&t);
273 timeout = (struct timeval *) &t;
276 n = select(w_max, &x_read, &x_write, &x_error, timeout);
277 if (n < 0) {
278 /* The following calls (especially gettext)
279 * might change errno. */
280 const int errno_from_select = errno;
282 critical_section = 0;
283 uninstall_alarm();
284 if (errno_from_select != EINTR) {
285 ERROR(gettext("The call to %s failed: %d (%s)"),
286 "select()", errno_from_select, (unsigned char *) strerror(errno_from_select));
287 if (++select_errors > 10) /* Infinite loop prevention. */
288 INTERNAL(gettext("%d select() failures."),
289 select_errors);
291 continue;
294 select_errors = 0;
295 critical_section = 0;
296 uninstall_alarm();
297 check_signals();
298 /*printf("sel: %d\n", n);*/
299 check_timers(&last_time);
301 i = -1;
302 while (n > 0 && ++i < w_max) {
303 int k = 0;
305 #if 0
306 printf("C %d : %d,%d,%d\n", i, FD_ISSET(i, &w_read),
307 FD_ISSET(i, &w_write), FD_ISSET(i, &w_error));
308 printf("A %d : %d,%d,%d\n", i, FD_ISSET(i, &x_read),
309 FD_ISSET(i, &x_write), FD_ISSET(i, &x_error));
310 #endif
311 if (FD_ISSET(i, &x_read)) {
312 if (threads[i].read_func) {
313 threads[i].read_func(threads[i].data);
314 check_bottom_halves();
316 k = 1;
319 if (FD_ISSET(i, &x_write)) {
320 if (threads[i].write_func) {
321 threads[i].write_func(threads[i].data);
322 check_bottom_halves();
324 k = 1;
327 if (FD_ISSET(i, &x_error)) {
328 if (threads[i].error_func) {
329 threads[i].error_func(threads[i].data);
330 check_bottom_halves();
332 k = 1;
335 n -= k;
340 static int
341 can_read_or_write(int fd, int write)
343 struct timeval tv = {0, 0};
344 fd_set fds;
345 fd_set *rfds = NULL;
346 fd_set *wfds = NULL;
348 FD_ZERO(&fds);
349 FD_SET(fd, &fds);
351 if (write)
352 wfds = &fds;
353 else
354 rfds = &fds;
356 return select(fd + 1, rfds, wfds, NULL, &tv);
360 can_read(int fd)
362 return can_read_or_write(fd, 0);
366 can_write(int fd)
368 return can_read_or_write(fd, 1);