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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.32 - (show annotations)
Mon May 19 13:03:14 2025 UTC (9 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.31: +1 -0 lines
Content type: text/x-csrc
Fix bug

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 out = 0;
280 loop = 0;
281 break;
282 }
283 else if (errno == EINTR)
284 {
285 continue;
286 }
287 else
288 {
289 log_error("read(STDIN) error (%d)\n", errno);
290 loop = 0;
291 break;
292 }
293 }
294 else if (ret == 0) // broken pipe
295 {
296 loop = 0;
297 break;
298 }
299 else
300 {
301 len += ret;
302 continue;
303 }
304 }
305 }
306 }
307
308 // For debug
309 // for (j = 0; j < len; j++)
310 // log_std ("<--[%u]\n", (buf[j] + 256) % 256);
311 }
312
313 fcntl(STDIN_FILENO, F_SETFL, flags);
314
315 while (pos < len)
316 {
317 unsigned char c = buf[pos++];
318
319 if (c == KEY_CONTROL)
320 {
321 if (in_control == 0)
322 {
323 in_control = 1;
324 i = 0;
325 continue;
326 }
327 }
328
329 if (in_control)
330 {
331 tmp[i++] = c;
332 if (i >= 2)
333 {
334 out = (int)tmp[0] * 256 + tmp[1];
335 in_control = 0;
336 break;
337 }
338 continue;
339 }
340
341 if (c == ESC_KEY)
342 {
343 if (in_esc == 0)
344 {
345 in_esc = 1;
346 in_ascii = 1;
347 i = 0;
348 continue;
349 }
350 else
351 {
352 out = ESC_KEY;
353 in_esc = 0;
354 break;
355 }
356 }
357
358 in_esc = 0;
359
360 if (in_ascii)
361 {
362 tmp[i++] = c;
363 if (c == 'm')
364 {
365 in_ascii = 0;
366 continue;
367 }
368 if (i == 2 && c >= 'A' && c <= 'D')
369 {
370 in_ascii = 0;
371 switch (c)
372 {
373 case 'A':
374 out = KEY_UP;
375 break;
376 case 'B':
377 out = KEY_DOWN;
378 break;
379 case 'C':
380 out = KEY_RIGHT;
381 break;
382 case 'D':
383 out = KEY_LEFT;
384 break;
385 }
386 break;
387 }
388 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
389 {
390 in_ascii = 0;
391 switch (tmp[1])
392 {
393 case 49:
394 out = KEY_HOME;
395 break;
396 case 51:
397 out = KEY_DEL;
398 break;
399 case 52:
400 out = KEY_END;
401 break;
402 case 53:
403 out = KEY_PGUP;
404 break;
405 case 54:
406 out = KEY_PGDN;
407 break;
408 }
409 break;
410 }
411 continue;
412 }
413
414 out = ((int)c + 256) % 256;
415 break;
416 }
417
418 // for debug
419 // log_std ("-->[%u]\n", out);
420
421 if (close(epollfd) < 0)
422 {
423 log_error("close(epoll) error (%d)\n");
424 }
425
426 return out;
427 }
428
429 int igetch_t(int sec)
430 {
431 int ch;
432 time_t t_begin = time(0);
433
434 do
435 {
436 ch = igetch(100);
437 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(0) - t_begin < sec));
438
439 return ch;
440 }
441
442 void igetch_reset()
443 {
444 int ch;
445 do
446 {
447 ch = igetch(0);
448 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
449 }

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