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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.36 - (show annotations)
Wed May 28 10:26:21 2025 UTC (9 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.35: +6 -0 lines
Content type: text/x-csrc
Refine ESC key handling

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 log_error("Output buffer is full, additional %d is required\n", ret);
62 }
63 }
64
65 return ret;
66 }
67
68 int outc(char c)
69 {
70 int ret;
71
72 if (stdout_buf_len + 1 > BUFSIZ)
73 {
74 iflush();
75 }
76
77 if (stdout_buf_len + 1 <= BUFSIZ)
78 {
79 stdout_buf[stdout_buf_len] = c;
80 stdout_buf_len++;
81 }
82 else
83 {
84 errno = EAGAIN;
85 ret = -1;
86 }
87
88 return ret;
89 }
90
91 int iflush()
92 {
93 int flags;
94 struct epoll_event ev, events[MAX_EVENTS];
95 int nfds, epollfd;
96 int retry;
97 int ret = 0;
98
99 epollfd = epoll_create1(0);
100 if (epollfd < 0)
101 {
102 log_error("epoll_create1() error (%d)\n", errno);
103 return -1;
104 }
105
106 ev.events = EPOLLOUT;
107 ev.data.fd = STDOUT_FILENO;
108 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
109 {
110 log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
111 if (close(epollfd) < 0)
112 {
113 log_error("close(epoll) error (%d)\n");
114 }
115 return -1;
116 }
117
118 // Set STDOUT as non-blocking
119 flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
120 fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
121
122 // Retry wait / flush for at most 3 times
123 retry = 3;
124 while (retry > 0 && !SYS_server_exit)
125 {
126 retry--;
127
128 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
129
130 if (nfds < 0)
131 {
132 if (errno != EINTR)
133 {
134 log_error("epoll_wait() error (%d)\n", errno);
135 break;
136 }
137 continue;
138 }
139 else if (nfds == 0) // timeout
140 {
141 continue;
142 }
143
144 for (int i = 0; i < nfds; i++)
145 {
146 if (events[i].data.fd == STDOUT_FILENO)
147 {
148 while (stdout_buf_offset < stdout_buf_len && !SYS_server_exit) // write until complete or error
149 {
150 ret = (int)write(STDOUT_FILENO, stdout_buf + stdout_buf_offset, (size_t)(stdout_buf_len - stdout_buf_offset));
151 if (ret < 0)
152 {
153 if (errno == EAGAIN || errno == EWOULDBLOCK)
154 {
155 break;
156 }
157 else if (errno == EINTR)
158 {
159 continue;
160 }
161 else
162 {
163 log_error("write(STDOUT) error (%d)\n", errno);
164 retry = 0;
165 break;
166 }
167 }
168 else if (ret == 0) // broken pipe
169 {
170 retry = 0;
171 break;
172 }
173 else
174 {
175 stdout_buf_offset += ret;
176 if (stdout_buf_offset >= stdout_buf_len) // flush buffer completely
177 {
178 ret = 0;
179 stdout_buf_offset = 0;
180 stdout_buf_len = 0;
181 retry = 0;
182 break;
183 }
184 continue;
185 }
186 }
187 }
188 }
189 }
190
191 // Restore STDOUT flags
192 fcntl(STDOUT_FILENO, F_SETFL, flags);
193
194 if (close(epollfd) < 0)
195 {
196 log_error("close(epoll) error (%d)\n");
197 }
198
199 return ret;
200 }
201
202 int igetch(int timeout)
203 {
204 // static input buffer
205 static unsigned char buf[LINE_BUFFER_LEN];
206 static int len = 0;
207 static int pos = 0;
208
209 struct epoll_event ev, events[MAX_EVENTS];
210 int nfds, epollfd;
211 int ret;
212 int loop;
213
214 unsigned char tmp[LINE_BUFFER_LEN];
215 int out = KEY_NULL;
216 int in_esc = 0;
217 int in_ascii = 0;
218 int in_control = 0;
219 int i = 0;
220 int flags;
221
222 epollfd = epoll_create1(0);
223 if (epollfd < 0)
224 {
225 log_error("epoll_create1() error (%d)\n", errno);
226 return -1;
227 }
228
229 ev.events = EPOLLIN;
230 ev.data.fd = STDIN_FILENO;
231 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
232 {
233 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
234
235 if (close(epollfd) < 0)
236 {
237 log_error("close(epoll) error (%d)\n");
238 }
239 return -1;
240 }
241
242 flags = fcntl(STDIN_FILENO, F_GETFL, 0);
243 fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
244
245 loop = 1;
246
247 while (loop && pos >= len && !SYS_server_exit)
248 {
249 len = 0;
250 pos = 0;
251
252 nfds = epoll_wait(epollfd, events, MAX_EVENTS, timeout);
253
254 if (nfds < 0)
255 {
256 if (errno != EINTR)
257 {
258 log_error("epoll_wait() error (%d)\n", errno);
259 break;
260 }
261 continue;
262 }
263 else if (nfds == 0) // timeout
264 {
265 out = KEY_TIMEOUT;
266 break;
267 }
268
269 for (int i = 0; i < nfds; i++)
270 {
271 if (events[i].data.fd == STDIN_FILENO)
272 {
273 while (len < sizeof(buf) && !SYS_server_exit) // read until complete or error
274 {
275 ret = (int)read(STDIN_FILENO, buf + len, sizeof(buf) - (size_t)len);
276 if (ret < 0)
277 {
278 if (errno == EAGAIN || errno == EWOULDBLOCK)
279 {
280 out = 0;
281 loop = 0;
282 break;
283 }
284 else if (errno == EINTR)
285 {
286 continue;
287 }
288 else
289 {
290 log_error("read(STDIN) error (%d)\n", errno);
291 loop = 0;
292 break;
293 }
294 }
295 else if (ret == 0) // broken pipe
296 {
297 loop = 0;
298 break;
299 }
300 else
301 {
302 len += ret;
303 continue;
304 }
305 }
306 }
307 }
308
309 // For debug
310 // for (int j = pos; j < len; j++)
311 // {
312 // log_std("Debug: <--[%u]\n", (buf[j] + 256) % 256);
313 // }
314 }
315
316 fcntl(STDIN_FILENO, F_SETFL, flags);
317
318 while (pos < len)
319 {
320 unsigned char c = buf[pos++];
321
322 if (c == KEY_CONTROL)
323 {
324 if (in_control == 0)
325 {
326 in_control = 1;
327 i = 0;
328 continue;
329 }
330 }
331
332 if (in_control)
333 {
334 tmp[i++] = c;
335 if (i >= 2)
336 {
337 out = (int)tmp[0] * 256 + tmp[1];
338 in_control = 0;
339 break;
340 }
341 continue;
342 }
343
344 if (c == ESC_KEY)
345 {
346 if (in_esc == 0)
347 {
348 in_esc = 1;
349 in_ascii = 1;
350 i = 0;
351 continue;
352 }
353 else
354 {
355 out = ESC_KEY;
356 in_esc = 0;
357 break;
358 }
359 }
360
361 in_esc = 0;
362
363 if (in_ascii)
364 {
365 tmp[i++] = c;
366 if (i == 2 && (tmp[0] == 79 || tmp[0] == 91))
367 {
368 in_ascii = 0;
369 switch (tmp[1])
370 {
371 case 65:
372 out = KEY_UP;
373 break;
374 case 66:
375 out = KEY_DOWN;
376 break;
377 case 67:
378 out = KEY_RIGHT;
379 break;
380 case 68:
381 out = KEY_LEFT;
382 break;
383 default:
384 in_ascii = 1;
385 }
386 if (!in_ascii)
387 {
388 break;
389 }
390 }
391 if (i == 2 && tmp[0] == 91) // Fterm
392 {
393 in_ascii = 0;
394 switch (tmp[1])
395 {
396 case 86:
397 out = KEY_SHIFT_F1;
398 break;
399 case 90:
400 out = KEY_SHIFT_F2;
401 break;
402 case 97:
403 out = KEY_SHIFT_F3;
404 break;
405 case 98:
406 out = KEY_SHIFT_F4;
407 break;
408 case 99:
409 out = KEY_SHIFT_F5;
410 break;
411 case 100:
412 out = KEY_SHIFT_F6;
413 break;
414 case 101:
415 out = KEY_SHIFT_F7;
416 break;
417 case 102:
418 out = KEY_SHIFT_F8;
419 break;
420 case 103:
421 out = KEY_SHIFT_F9;
422 break;
423 case 104:
424 out = KEY_SHIFT_F10;
425 break;
426 case 107:
427 out = KEY_CTRL_F1;
428 break;
429 case 108:
430 out = KEY_CTRL_F2;
431 break;
432 case 109:
433 out = KEY_CTRL_F3;
434 break;
435 case 112:
436 out = KEY_CTRL_F6;
437 break;
438 case 113:
439 out = KEY_CTRL_F7;
440 break;
441 case 114:
442 out = KEY_CTRL_F8;
443 break;
444 case 115:
445 out = KEY_CTRL_F9;
446 break;
447 case 116:
448 out = KEY_CTRL_F10;
449 break;
450 default:
451 in_ascii = 1;
452 }
453 if (!in_ascii)
454 {
455 break;
456 }
457 }
458 if (i == 2 && tmp[0] == 79) // Xterm
459 {
460 in_ascii = 0;
461 switch (tmp[1])
462 {
463 case 80:
464 out = KEY_F1;
465 break;
466 case 81:
467 out = KEY_F2;
468 break;
469 case 82:
470 out = KEY_F3;
471 break;
472 case 83:
473 out = KEY_F4;
474 break;
475 default:
476 in_ascii = 1;
477 }
478 if (!in_ascii)
479 {
480 break;
481 }
482 }
483 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
484 {
485 in_ascii = 0;
486 switch (tmp[1])
487 {
488 case 49:
489 out = KEY_HOME;
490 break;
491 case 51:
492 out = KEY_DEL;
493 break;
494 case 52:
495 out = KEY_END;
496 break;
497 case 53:
498 out = KEY_PGUP;
499 break;
500 case 54:
501 out = KEY_PGDN;
502 break;
503 default:
504 in_ascii = 1;
505 }
506 if (!in_ascii)
507 {
508 break;
509 }
510 }
511 if (i == 4 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 126) // Fterm
512 {
513 in_ascii = 0;
514 switch (tmp[2])
515 {
516 case 49:
517 out = KEY_F1;
518 break;
519 case 50:
520 out = KEY_F2;
521 break;
522 case 51:
523 out = KEY_F3;
524 break;
525 case 52:
526 out = KEY_F4;
527 break;
528 case 53:
529 out = KEY_F5;
530 break;
531 case 55:
532 out = KEY_F6;
533 break;
534 case 56:
535 out = KEY_F7;
536 break;
537 case 57:
538 out = KEY_F8;
539 break;
540 default:
541 in_ascii = 1;
542 }
543 if (!in_ascii)
544 {
545 break;
546 }
547 }
548 if (i == 4 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 126) // Fterm
549 {
550 in_ascii = 0;
551 switch (tmp[2])
552 {
553 case 48:
554 out = KEY_F9;
555 break;
556 case 49:
557 out = KEY_F10;
558 break;
559 case 50:
560 out = KEY_F11; // Fterm
561 break;
562 case 51:
563 out = KEY_F11; // Xterm
564 break;
565 case 52:
566 out = KEY_F12; // Xterm
567 break;
568 default:
569 in_ascii = 1;
570 }
571 if (!in_ascii)
572 {
573 break;
574 }
575 }
576 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 53) // Xterm
577 {
578 in_ascii = 0;
579 switch (tmp[4])
580 {
581 case 80:
582 out = KEY_CTRL_F1;
583 break;
584 case 81:
585 out = KEY_CTRL_F2;
586 break;
587 case 82:
588 out = KEY_CTRL_F3;
589 break;
590 default:
591 in_ascii = 1;
592 }
593 if (!in_ascii)
594 {
595 break;
596 }
597 }
598 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
599 {
600 in_ascii = 0;
601 switch (tmp[2])
602 {
603 case 53:
604 out = KEY_CTRL_F5;
605 break;
606 case 55:
607 out = KEY_CTRL_F6;
608 break;
609 case 56:
610 out = KEY_CTRL_F7;
611 break;
612 case 57:
613 out = KEY_CTRL_F8;
614 break;
615 default:
616 in_ascii = 1;
617 }
618 if (!in_ascii)
619 {
620 break;
621 }
622 }
623 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
624 {
625 in_ascii = 0;
626 switch (tmp[2])
627 {
628 case 48:
629 out = KEY_CTRL_F9;
630 break;
631 case 49:
632 out = KEY_CTRL_F10;
633 break;
634 case 51:
635 out = KEY_CTRL_F11;
636 break;
637 case 52:
638 out = KEY_CTRL_F12;
639 break;
640 default:
641 in_ascii = 1;
642 }
643 if (!in_ascii)
644 {
645 break;
646 }
647 }
648 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 50) // Xterm
649 {
650 in_ascii = 0;
651 switch (tmp[4])
652 {
653 case 80:
654 out = KEY_SHIFT_F1;
655 break;
656 case 81:
657 out = KEY_SHIFT_F2;
658 break;
659 case 82:
660 out = KEY_SHIFT_F3;
661 break;
662 case 83:
663 out = KEY_SHIFT_F4;
664 break;
665 default:
666 in_ascii = 1;
667 }
668 if (!in_ascii)
669 {
670 break;
671 }
672 }
673 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
674 {
675 in_ascii = 0;
676 switch (tmp[2])
677 {
678 case 53:
679 out = KEY_SHIFT_F5;
680 break;
681 case 55:
682 out = KEY_SHIFT_F6;
683 break;
684 case 56:
685 out = KEY_SHIFT_F7;
686 break;
687 case 57:
688 out = KEY_SHIFT_F8;
689 break;
690 default:
691 in_ascii = 1;
692 }
693 if (!in_ascii)
694 {
695 break;
696 }
697 }
698 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
699 {
700 in_ascii = 0;
701 switch (tmp[2])
702 {
703 case 48:
704 out = KEY_SHIFT_F9;
705 break;
706 case 49:
707 out = KEY_SHIFT_F10;
708 break;
709 case 51:
710 out = KEY_SHIFT_F11;
711 break;
712 case 52:
713 out = KEY_SHIFT_F12;
714 break;
715 default:
716 in_ascii = 1;
717 }
718 if (!in_ascii)
719 {
720 break;
721 }
722 }
723
724 if (c == 'm')
725 {
726 in_ascii = 0;
727 }
728 continue;
729 }
730
731 out = ((int)c + 256) % 256;
732 break;
733 }
734
735 if (close(epollfd) < 0)
736 {
737 log_error("close(epoll) error (%d)\n");
738 }
739
740 // For ESC key
741 if (out == 0 && in_esc)
742 {
743 out = KEY_ESC;
744 }
745
746 // for debug
747 // if (out != KEY_TIMEOUT && out != KEY_NULL)
748 // {
749 // log_std ("Debug: -->[0x %x]\n", out);
750 // }
751
752 return out;
753 }
754
755 int igetch_t(int sec)
756 {
757 int ch;
758 time_t t_begin = time(0);
759
760 do
761 {
762 ch = igetch(100);
763 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(0) - t_begin < sec));
764
765 return ch;
766 }
767
768 void igetch_reset()
769 {
770 int ch;
771 do
772 {
773 ch = igetch(0);
774 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
775 }

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