/[LeafOK_CVS]/lbbs/src/io.c
ViewVC logotype

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.31 - (show annotations)
Tue May 13 07:28:51 2025 UTC (10 months ago) by sysadm
Branch: MAIN
Changes since 1.30: +62 -34 lines
Content type: text/x-csrc
Refine igetch/igetch_t

1 /***************************************************************************
2 io.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "io.h"
18 #include "log.h"
19 #include "common.h"
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/select.h>
28 #include <sys/ioctl.h>
29 #include <sys/epoll.h>
30
31 static char stdout_buf[BUFSIZ];
32 static int stdout_buf_len = 0;
33 static int stdout_buf_offset = 0;
34
35 int prints(const char *format, ...)
36 {
37 char buf[BUFSIZ];
38 va_list args;
39 int ret;
40
41 va_start(args, format);
42 ret = vsnprintf(buf, sizeof(buf), format, args);
43 va_end(args);
44
45 if (ret > 0)
46 {
47 if (stdout_buf_len + ret > BUFSIZ)
48 {
49 iflush();
50 }
51
52 if (stdout_buf_len + ret <= BUFSIZ)
53 {
54 memcpy(stdout_buf + stdout_buf_len, buf, (size_t)ret);
55 stdout_buf_len += ret;
56 }
57 else
58 {
59 errno = EAGAIN;
60 ret = (BUFSIZ - stdout_buf_len - ret);
61 }
62 }
63
64 return ret;
65 }
66
67 int outc(char c)
68 {
69 int ret;
70
71 if (stdout_buf_len + 1 > BUFSIZ)
72 {
73 iflush();
74 }
75
76 if (stdout_buf_len + 1 <= BUFSIZ)
77 {
78 stdout_buf[stdout_buf_len] = c;
79 stdout_buf_len++;
80 }
81 else
82 {
83 errno = EAGAIN;
84 ret = -1;
85 }
86
87 return ret;
88 }
89
90 int iflush()
91 {
92 int flags;
93 struct epoll_event ev, events[MAX_EVENTS];
94 int nfds, epollfd;
95 int retry;
96 int ret = 0;
97
98 epollfd = epoll_create1(0);
99 if (epollfd < 0)
100 {
101 log_error("epoll_create1() error (%d)\n", errno);
102 return -1;
103 }
104
105 ev.events = EPOLLOUT;
106 ev.data.fd = STDOUT_FILENO;
107 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
108 {
109 log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
110 if (close(epollfd) < 0)
111 {
112 log_error("close(epoll) error (%d)\n");
113 }
114 return -1;
115 }
116
117 // Set STDOUT as non-blocking
118 flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
119 fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
120
121 // Retry wait / flush for at most 3 times
122 retry = 3;
123 while (retry > 0 && !SYS_server_exit)
124 {
125 retry--;
126
127 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
128
129 if (nfds < 0)
130 {
131 if (errno != EINTR)
132 {
133 log_error("epoll_wait() error (%d)\n", errno);
134 break;
135 }
136 continue;
137 }
138 else if (nfds == 0) // timeout
139 {
140 continue;
141 }
142
143 for (int i = 0; i < nfds; i++)
144 {
145 if (events[i].data.fd == STDOUT_FILENO)
146 {
147 while (stdout_buf_offset < stdout_buf_len && !SYS_server_exit) // write until complete or error
148 {
149 ret = (int)write(STDOUT_FILENO, stdout_buf + stdout_buf_offset, (size_t)(stdout_buf_len - stdout_buf_offset));
150 if (ret < 0)
151 {
152 if (errno == EAGAIN || errno == EWOULDBLOCK)
153 {
154 break;
155 }
156 else if (errno == EINTR)
157 {
158 continue;
159 }
160 else
161 {
162 log_error("write(STDOUT) error (%d)\n", errno);
163 retry = 0;
164 break;
165 }
166 }
167 else if (ret == 0) // broken pipe
168 {
169 retry = 0;
170 break;
171 }
172 else
173 {
174 stdout_buf_offset += ret;
175 if (stdout_buf_offset >= stdout_buf_len) // flush buffer completely
176 {
177 ret = 0;
178 stdout_buf_offset = 0;
179 stdout_buf_len = 0;
180 retry = 0;
181 break;
182 }
183 continue;
184 }
185 }
186 }
187 }
188 }
189
190 // Restore STDOUT flags
191 fcntl(STDOUT_FILENO, F_SETFL, flags);
192
193 if (close(epollfd) < 0)
194 {
195 log_error("close(epoll) error (%d)\n");
196 }
197
198 return ret;
199 }
200
201 int igetch(int timeout)
202 {
203 // static input buffer
204 static unsigned char buf[LINE_BUFFER_LEN];
205 static int len = 0;
206 static int pos = 0;
207
208 struct epoll_event ev, events[MAX_EVENTS];
209 int nfds, epollfd;
210 int ret;
211 int loop;
212
213 unsigned char tmp[LINE_BUFFER_LEN];
214 int out = KEY_NULL;
215 int in_esc = 0;
216 int in_ascii = 0;
217 int in_control = 0;
218 int i = 0;
219 int flags;
220
221 epollfd = epoll_create1(0);
222 if (epollfd < 0)
223 {
224 log_error("epoll_create1() error (%d)\n", errno);
225 return -1;
226 }
227
228 ev.events = EPOLLIN;
229 ev.data.fd = STDIN_FILENO;
230 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
231 {
232 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
233
234 if (close(epollfd) < 0)
235 {
236 log_error("close(epoll) error (%d)\n");
237 }
238 return -1;
239 }
240
241 flags = fcntl(STDIN_FILENO, F_GETFL, 0);
242 fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
243
244 loop = 1;
245
246 while (loop && pos >= len && !SYS_server_exit)
247 {
248 len = 0;
249 pos = 0;
250
251 nfds = epoll_wait(epollfd, events, MAX_EVENTS, timeout);
252
253 if (nfds < 0)
254 {
255 if (errno != EINTR)
256 {
257 log_error("epoll_wait() error (%d)\n", errno);
258 break;
259 }
260 continue;
261 }
262 else if (nfds == 0) // timeout
263 {
264 out = KEY_TIMEOUT;
265 break;
266 }
267
268 for (int i = 0; i < nfds; i++)
269 {
270 if (events[i].data.fd == STDIN_FILENO)
271 {
272 while (len < sizeof(buf) && !SYS_server_exit) // read until complete or error
273 {
274 ret = (int)read(STDIN_FILENO, buf + len, sizeof(buf) - (size_t)len);
275 if (ret < 0)
276 {
277 if (errno == EAGAIN || errno == EWOULDBLOCK)
278 {
279 loop = 0;
280 break;
281 }
282 else if (errno == EINTR)
283 {
284 continue;
285 }
286 else
287 {
288 log_error("read(STDIN) error (%d)\n", errno);
289 loop = 0;
290 break;
291 }
292 }
293 else if (ret == 0) // broken pipe
294 {
295 loop = 0;
296 break;
297 }
298 else
299 {
300 len += ret;
301 continue;
302 }
303 }
304 }
305 }
306
307 // For debug
308 // for (j = 0; j < len; j++)
309 // log_std ("<--[%u]\n", (buf[j] + 256) % 256);
310 }
311
312 fcntl(STDIN_FILENO, F_SETFL, flags);
313
314 while (pos < len)
315 {
316 unsigned char c = buf[pos++];
317
318 if (c == KEY_CONTROL)
319 {
320 if (in_control == 0)
321 {
322 in_control = 1;
323 i = 0;
324 continue;
325 }
326 }
327
328 if (in_control)
329 {
330 tmp[i++] = c;
331 if (i >= 2)
332 {
333 out = (int)tmp[0] * 256 + tmp[1];
334 in_control = 0;
335 break;
336 }
337 continue;
338 }
339
340 if (c == ESC_KEY)
341 {
342 if (in_esc == 0)
343 {
344 in_esc = 1;
345 in_ascii = 1;
346 i = 0;
347 continue;
348 }
349 else
350 {
351 out = ESC_KEY;
352 in_esc = 0;
353 break;
354 }
355 }
356
357 in_esc = 0;
358
359 if (in_ascii)
360 {
361 tmp[i++] = c;
362 if (c == 'm')
363 {
364 in_ascii = 0;
365 continue;
366 }
367 if (i == 2 && c >= 'A' && c <= 'D')
368 {
369 in_ascii = 0;
370 switch (c)
371 {
372 case 'A':
373 out = KEY_UP;
374 break;
375 case 'B':
376 out = KEY_DOWN;
377 break;
378 case 'C':
379 out = KEY_RIGHT;
380 break;
381 case 'D':
382 out = KEY_LEFT;
383 break;
384 }
385 break;
386 }
387 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
388 {
389 in_ascii = 0;
390 switch (tmp[1])
391 {
392 case 49:
393 out = KEY_HOME;
394 break;
395 case 51:
396 out = KEY_DEL;
397 break;
398 case 52:
399 out = KEY_END;
400 break;
401 case 53:
402 out = KEY_PGUP;
403 break;
404 case 54:
405 out = KEY_PGDN;
406 break;
407 }
408 break;
409 }
410 continue;
411 }
412
413 out = ((int)c + 256) % 256;
414 break;
415 }
416
417 // for debug
418 // log_std ("-->[%u]\n", out);
419
420 if (close(epollfd) < 0)
421 {
422 log_error("close(epoll) error (%d)\n");
423 }
424
425 return out;
426 }
427
428 int igetch_t(int sec)
429 {
430 int ch;
431 time_t t_begin = time(0);
432
433 do
434 {
435 ch = igetch(100);
436 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(0) - t_begin < sec));
437
438 return ch;
439 }
440
441 void igetch_reset()
442 {
443 int ch;
444 do
445 {
446 ch = igetch(0);
447 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
448 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1