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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.74 - (show annotations)
Thu Dec 18 02:17:39 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.73: +37 -7 lines
Content type: text/x-csrc
Add handling of error events (EPOLLRDHUP | EPOLLHUP | EPOLLERR)

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * io
4 * - basic terminal-based user input / output features
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "common.h"
14 #include "io.h"
15 #include "log.h"
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <sys/ioctl.h>
24 #include <sys/select.h>
25 #include <libssh/callbacks.h>
26 #include <libssh/libssh.h>
27 #include <libssh/server.h>
28
29 #ifdef HAVE_SYS_EPOLL_H
30 #include <sys/epoll.h>
31 #else
32 #include <poll.h>
33 #endif
34
35 enum _io_constant_t
36 {
37 OUTPUT_BUF_SIZE = 8192,
38 };
39
40 const char BBS_default_charset[CHARSET_MAX_LEN + 1] = "UTF-8";
41 char stdio_charset[CHARSET_MAX_LEN + 1] = "UTF-8";
42
43 #ifdef HAVE_SYS_EPOLL_H
44 // epoll for STDIO
45 static int stdin_epollfd = -1;
46 static int stdout_epollfd = -1;
47 #endif
48
49 static int stdin_flags = 0;
50 static int stdout_flags = 0;
51
52 // static input / output buffer
53 static char stdin_buf[LINE_BUFFER_LEN];
54 static char stdout_buf[OUTPUT_BUF_SIZE];
55 static int stdin_buf_len = 0;
56 static int stdout_buf_len = 0;
57 static int stdin_buf_offset = 0;
58 static int stdout_buf_offset = 0;
59
60 static char stdin_conv[LINE_BUFFER_LEN * 2];
61 static char stdout_conv[OUTPUT_BUF_SIZE * 2];
62 static int stdin_conv_len = 0;
63 static int stdout_conv_len = 0;
64 static int stdin_conv_offset = 0;
65 static int stdout_conv_offset = 0;
66
67 static iconv_t stdin_cd = (iconv_t)(-1);
68 static iconv_t stdout_cd = (iconv_t)(-1);
69
70 int io_init(void)
71 {
72 #ifdef HAVE_SYS_EPOLL_H
73 struct epoll_event ev;
74
75 if (stdin_epollfd == -1)
76 {
77 stdin_epollfd = epoll_create1(0);
78 if (stdin_epollfd == -1)
79 {
80 log_error("epoll_create1() error (%d)\n", errno);
81 return -1;
82 }
83
84 ev.events = EPOLLIN;
85 ev.data.fd = STDIN_FILENO;
86 if (epoll_ctl(stdin_epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
87 {
88 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
89 if (close(stdin_epollfd) < 0)
90 {
91 log_error("close(stdin_epollfd) error (%d)\n");
92 }
93 stdin_epollfd = -1;
94 return -1;
95 }
96
97 stdin_flags = fcntl(STDIN_FILENO, F_GETFL, 0);
98 fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK);
99 }
100
101 if (stdout_epollfd == -1)
102 {
103 stdout_epollfd = epoll_create1(0);
104 if (stdout_epollfd == -1)
105 {
106 log_error("epoll_create1() error (%d)\n", errno);
107 return -1;
108 }
109
110 ev.events = EPOLLOUT;
111 ev.data.fd = STDOUT_FILENO;
112 if (epoll_ctl(stdout_epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
113 {
114 log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
115 if (close(stdout_epollfd) < 0)
116 {
117 log_error("close(stdout_epollfd) error (%d)\n");
118 }
119 stdout_epollfd = -1;
120 return -1;
121 }
122
123 stdout_flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
124 fcntl(STDOUT_FILENO, F_SETFL, stdout_flags | O_NONBLOCK);
125 }
126 #else
127 if (stdin_flags == 0)
128 {
129 stdin_flags = fcntl(STDIN_FILENO, F_GETFL, 0);
130 fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK);
131 }
132
133 if (stdout_flags == 0)
134 {
135 stdout_flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
136 fcntl(STDOUT_FILENO, F_SETFL, stdout_flags | O_NONBLOCK);
137 }
138 #endif
139
140 return 0;
141 }
142
143 void io_cleanup(void)
144 {
145 #ifdef HAVE_SYS_EPOLL_H
146 if (stdin_epollfd != -1)
147 {
148 fcntl(STDIN_FILENO, F_SETFL, stdin_flags);
149 stdin_flags = 0;
150
151 if (close(stdin_epollfd) < 0)
152 {
153 log_error("close(stdin_epollfd) error (%d)\n");
154 }
155 stdin_epollfd = -1;
156 }
157
158 if (stdout_epollfd != -1)
159 {
160 fcntl(STDOUT_FILENO, F_SETFL, stdout_flags);
161 stdout_flags = 0;
162
163 if (close(stdout_epollfd) < 0)
164 {
165 log_error("close(stdout_epollfd) error (%d)\n");
166 }
167 stdout_epollfd = -1;
168 }
169 #else
170 if (stdin_flags != 0)
171 {
172 fcntl(STDIN_FILENO, F_SETFL, stdin_flags);
173 stdin_flags = 0;
174 }
175
176 if (stdout_flags != 0)
177 {
178 fcntl(STDOUT_FILENO, F_SETFL, stdout_flags);
179 stdout_flags = 0;
180 }
181 #endif
182 }
183
184 int prints(const char *format, ...)
185 {
186 char buf[OUTPUT_BUF_SIZE];
187 va_list args;
188 int ret;
189
190 va_start(args, format);
191 ret = vsnprintf(buf, sizeof(buf), format, args);
192 va_end(args);
193
194 if (ret > 0)
195 {
196 if (stdout_buf_len + ret > OUTPUT_BUF_SIZE)
197 {
198 iflush();
199 }
200
201 if (stdout_buf_len + ret <= OUTPUT_BUF_SIZE)
202 {
203 memcpy(stdout_buf + stdout_buf_len, buf, (size_t)ret);
204 stdout_buf_len += ret;
205 }
206 else
207 {
208 errno = EAGAIN;
209 ret = (OUTPUT_BUF_SIZE - stdout_buf_len - ret);
210 log_error("Output buffer is full, additional %d is required\n", ret);
211 }
212 }
213
214 return ret;
215 }
216
217 int outc(char c)
218 {
219 int ret;
220
221 if (stdout_buf_len + 1 > OUTPUT_BUF_SIZE)
222 {
223 iflush();
224 }
225
226 if (stdout_buf_len + 1 <= OUTPUT_BUF_SIZE)
227 {
228 stdout_buf[stdout_buf_len] = c;
229 stdout_buf_len++;
230 }
231 else
232 {
233 errno = EAGAIN;
234 ret = -1;
235 }
236
237 return ret;
238 }
239
240 int iflush(void)
241 {
242 #ifdef HAVE_SYS_EPOLL_H
243 struct epoll_event events[MAX_EVENTS];
244 #else
245 struct pollfd pfds[1];
246 #endif
247
248 int nfds;
249 int ret = 0;
250
251 // Retry wait / flush for at most 3 times
252 for (int retry = 3; retry > 0 && !SYS_server_exit; retry--)
253 {
254 #ifdef HAVE_SYS_EPOLL_H
255 nfds = epoll_wait(stdout_epollfd, events, MAX_EVENTS, 100); // 0.1 second
256 ret = nfds;
257 #else
258 pfds[0].fd = STDOUT_FILENO;
259 pfds[0].events = POLLOUT;
260 nfds = 1;
261 ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
262 #endif
263
264 if (ret < 0)
265 {
266 if (errno != EINTR)
267 {
268 #ifdef HAVE_SYS_EPOLL_H
269 log_error("epoll_wait() error (%d)\n", errno);
270 #else
271 log_error("poll() error (%d)\n", errno);
272 #endif
273 break;
274 }
275 continue;
276 }
277 else if (ret == 0) // timeout
278 {
279 continue;
280 }
281
282 for (int i = 0; i < nfds; i++)
283 {
284 #ifdef HAVE_SYS_EPOLL_H
285 if (events[i].data.fd == STDOUT_FILENO && (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)))
286 #else
287 if (pfds[i].fd == STDOUT_FILENO && (pfds[i].revents & (POLLRDHUP | POLLHUP | POLLERR)))
288 #endif
289 {
290 #ifdef _DEBUG
291 #ifdef HAVE_SYS_EPOLL_H
292 log_error("STDOUT error events (%d)\n", events[i].events);
293 #else
294 log_error("STDOUT error events (%d)\n", pfds[i].revents);
295 #endif
296 #endif
297 retry = 0;
298 break;
299 }
300
301 #ifdef HAVE_SYS_EPOLL_H
302 if (events[i].data.fd == STDOUT_FILENO && (events[i].events & EPOLLOUT))
303 #else
304 if (pfds[i].fd == STDOUT_FILENO && (pfds[i].revents & POLLOUT))
305 #endif
306 {
307 if (stdout_buf_offset < stdout_buf_len)
308 {
309 ret = io_buf_conv(stdout_cd, stdout_buf, &stdout_buf_len, &stdout_buf_offset, stdout_conv, sizeof(stdout_conv), &stdout_conv_len);
310 if (ret < 0)
311 {
312 log_error("io_buf_conv(stdout, %d, %d, %d) error\n", stdout_buf_len, stdout_buf_offset, stdout_conv_len);
313 stdout_buf_len = stdout_buf_offset; // Discard invalid sequence
314 }
315 }
316
317 while (stdout_conv_offset < stdout_conv_len && !SYS_server_exit) // write until complete or error
318 {
319 if (SSH_v2)
320 {
321 ret = ssh_channel_write(SSH_channel, stdout_conv + stdout_conv_offset, (uint32_t)(stdout_conv_len - stdout_conv_offset));
322 if (ret == SSH_ERROR)
323 {
324 #ifdef _DEBUG
325 log_error("ssh_channel_write() error: %s\n", ssh_get_error(SSH_session));
326 #endif
327 retry = 0;
328 break;
329 }
330 }
331 else
332 {
333 ret = (int)write(STDOUT_FILENO, stdout_conv + stdout_conv_offset, (size_t)(stdout_conv_len - stdout_conv_offset));
334 }
335 if (ret < 0)
336 {
337 if (errno == EAGAIN || errno == EWOULDBLOCK)
338 {
339 break;
340 }
341 else if (errno == EINTR)
342 {
343 continue;
344 }
345 else
346 {
347 #ifdef _DEBUG
348 log_error("write(STDOUT) error (%d)\n", errno);
349 #endif
350 retry = 0;
351 break;
352 }
353 }
354 else if (ret == 0) // broken pipe
355 {
356 retry = 0;
357 break;
358 }
359 else
360 {
361 stdout_conv_offset += ret;
362 if (stdout_conv_offset >= stdout_conv_len) // flush buffer completely
363 {
364 ret = 0;
365 stdout_conv_offset = 0;
366 stdout_conv_len = 0;
367 retry = 0;
368 break;
369 }
370 continue;
371 }
372 }
373 }
374 }
375 }
376
377 return ret;
378 }
379
380 int igetch(int timeout)
381 {
382 static int stdin_read_wait = 0;
383
384 #ifdef HAVE_SYS_EPOLL_H
385 struct epoll_event events[MAX_EVENTS];
386 #else
387 struct pollfd pfds[1];
388 #endif
389
390 int nfds;
391 int ret;
392 int loop;
393
394 unsigned char tmp[LINE_BUFFER_LEN];
395 int out = KEY_NULL;
396 int in_esc = 0;
397 int in_ascii = 0;
398 int in_control = 0;
399 int i = 0;
400
401 if (stdin_conv_offset >= stdin_conv_len)
402 {
403 stdin_conv_len = 0;
404 stdin_conv_offset = 0;
405
406 for (loop = 1; loop && stdin_buf_len < sizeof(stdin_buf) && stdin_conv_offset >= stdin_conv_len && !SYS_server_exit;)
407 {
408 if (SSH_v2 && ssh_channel_is_closed(SSH_channel))
409 {
410 #ifdef _DEBUG
411 log_error("SSH channel is closed\n");
412 #endif
413 loop = 0;
414 break;
415 }
416
417 if (!stdin_read_wait)
418 {
419 #ifdef HAVE_SYS_EPOLL_H
420 nfds = epoll_wait(stdin_epollfd, events, MAX_EVENTS, timeout);
421 ret = nfds;
422 #else
423 pfds[0].fd = STDIN_FILENO;
424 pfds[0].events = POLLIN;
425 nfds = 1;
426 ret = poll(pfds, (nfds_t)nfds, timeout);
427 #endif
428
429 if (ret < 0)
430 {
431 if (errno != EINTR)
432 {
433 #ifdef HAVE_SYS_EPOLL_H
434 log_error("epoll_wait() error (%d)\n", errno);
435 #else
436 log_error("poll() error (%d)\n", errno);
437 #endif
438 break;
439 }
440 continue;
441 }
442 else if (ret == 0) // timeout
443 {
444 out = KEY_TIMEOUT;
445 break;
446 }
447
448 for (int i = 0; i < nfds; i++)
449 {
450 #ifdef HAVE_SYS_EPOLL_H
451 if (events[i].data.fd == STDIN_FILENO && (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)))
452 #else
453 if (pfds[i].fd == STDIN_FILENO && (pfds[i].revents & (POLLRDHUP | POLLHUP | POLLERR)))
454 #endif
455 {
456 #ifdef _DEBUG
457 #ifdef HAVE_SYS_EPOLL_H
458 log_error("STDIN error events (%d)\n", events[i].events);
459 #else
460 log_error("STDIN error events (%d)\n", pfds[i].revents);
461 #endif
462 #endif
463 loop = 0;
464 break;
465 }
466
467 #ifdef HAVE_SYS_EPOLL_H
468 if (events[i].data.fd == STDIN_FILENO && (events[i].events & EPOLLIN))
469 #else
470 if (pfds[i].fd == STDIN_FILENO && (pfds[i].revents & POLLIN))
471 #endif
472 {
473 stdin_read_wait = 1;
474 }
475 }
476 }
477
478 if (stdin_read_wait)
479 {
480 while (stdin_buf_len < sizeof(stdin_buf) && !SYS_server_exit) // read until complete or error
481 {
482 if (SSH_v2)
483 {
484 ret = ssh_channel_read_nonblocking(SSH_channel, stdin_buf + stdin_buf_len, sizeof(stdin_buf) - (uint32_t)stdin_buf_len, 0);
485 if (ret == SSH_ERROR)
486 {
487 #ifdef _DEBUG
488 log_error("ssh_channel_read_nonblocking() error: %s\n", ssh_get_error(SSH_session));
489 #endif
490 loop = 0;
491 break;
492 }
493 else if (ret == SSH_EOF)
494 {
495 stdin_read_wait = 0;
496 loop = 0;
497 break;
498 }
499 else if (ret == 0)
500 {
501 out = 0;
502 stdin_read_wait = 0;
503 loop = 0;
504 break;
505 }
506 }
507 else
508 {
509 ret = (int)read(STDIN_FILENO, stdin_buf + stdin_buf_len, sizeof(stdin_buf) - (size_t)stdin_buf_len);
510 }
511 if (ret < 0)
512 {
513 if (errno == EAGAIN || errno == EWOULDBLOCK)
514 {
515 out = 0;
516 stdin_read_wait = 0;
517 loop = 0;
518 break;
519 }
520 else if (errno == EINTR)
521 {
522 continue;
523 }
524 else
525 {
526 #ifdef _DEBUG
527 log_error("read(STDIN) error (%d)\n", errno);
528 #endif
529 loop = 0;
530 break;
531 }
532 }
533 else if (ret == 0) // broken pipe
534 {
535 stdin_read_wait = 0;
536 loop = 0;
537 break;
538 }
539 else
540 {
541 stdin_buf_len += ret;
542 continue;
543 }
544 }
545 }
546
547 // For debug
548 #ifdef _DEBUG
549 for (int j = stdin_buf_offset; j < stdin_buf_len; j++)
550 {
551 log_error("Debug input: <--[%u]\n", (stdin_buf[j] + 256) % 256);
552 }
553 #endif
554 }
555
556 if (stdin_buf_offset < stdin_buf_len)
557 {
558 ret = io_buf_conv(stdin_cd, stdin_buf, &stdin_buf_len, &stdin_buf_offset, stdin_conv, sizeof(stdin_conv), &stdin_conv_len);
559 if (ret < 0)
560 {
561 log_error("io_buf_conv(stdin, %d, %d, %d) error\n", stdin_buf_len, stdin_buf_offset, stdin_conv_len);
562 stdin_buf_len = stdin_buf_offset; // Discard invalid sequence
563 }
564
565 // For debug
566 #ifdef _DEBUG
567 for (int j = stdin_conv_offset; j < stdin_conv_len; j++)
568 {
569 log_error("Debug input_conv: <--[%u]\n", (stdin_conv[j] + 256) % 256);
570 }
571 #endif
572 }
573 }
574
575 while (stdin_conv_offset < stdin_conv_len)
576 {
577 unsigned char c = (unsigned char)stdin_conv[stdin_conv_offset++];
578
579 // Convert \r\n to \r
580 if (c == CR && stdin_conv_offset < stdin_conv_len && stdin_conv[stdin_conv_offset] == LF)
581 {
582 stdin_conv_offset++;
583 }
584
585 // Convert single \n to \r
586 if (c == LF)
587 {
588 c = CR;
589 }
590
591 if (c == KEY_CONTROL)
592 {
593 if (in_control == 0)
594 {
595 in_control = 1;
596 i = 0;
597 continue;
598 }
599 }
600
601 if (in_control)
602 {
603 tmp[i++] = c;
604 if (i >= 2)
605 {
606 out = (int)tmp[0] * 256 + tmp[1];
607 in_control = 0;
608 break;
609 }
610 continue;
611 }
612
613 if (c == KEY_ESC)
614 {
615 if (in_esc == 0)
616 {
617 in_esc = 1;
618 in_ascii = 1;
619 i = 0;
620 continue;
621 }
622 else
623 {
624 out = KEY_CSI;
625 in_esc = 0;
626 break;
627 }
628 }
629
630 in_esc = 0;
631
632 if (in_ascii)
633 {
634 tmp[i++] = c;
635 if (i == 2 && (tmp[0] == 79 || tmp[0] == 91))
636 {
637 in_ascii = 0;
638 switch (tmp[1])
639 {
640 case 65:
641 out = KEY_UP;
642 break;
643 case 66:
644 out = KEY_DOWN;
645 break;
646 case 67:
647 out = KEY_RIGHT;
648 break;
649 case 68:
650 out = KEY_LEFT;
651 break;
652 default:
653 in_ascii = 1;
654 }
655 if (!in_ascii)
656 {
657 break;
658 }
659 }
660 if (i == 2 && tmp[0] == 91) // Fterm
661 {
662 in_ascii = 0;
663 switch (tmp[1])
664 {
665 case 86:
666 out = KEY_SHIFT_F1;
667 break;
668 case 90:
669 out = KEY_SHIFT_F2;
670 break;
671 case 97:
672 out = KEY_SHIFT_F3;
673 break;
674 case 98:
675 out = KEY_SHIFT_F4;
676 break;
677 case 99:
678 out = KEY_SHIFT_F5;
679 break;
680 case 100:
681 out = KEY_SHIFT_F6;
682 break;
683 case 101:
684 out = KEY_SHIFT_F7;
685 break;
686 case 102:
687 out = KEY_SHIFT_F8;
688 break;
689 case 103:
690 out = KEY_SHIFT_F9;
691 break;
692 case 104:
693 out = KEY_SHIFT_F10;
694 break;
695 case 107:
696 out = KEY_CTRL_F1;
697 break;
698 case 108:
699 out = KEY_CTRL_F2;
700 break;
701 case 109:
702 out = KEY_CTRL_F3;
703 break;
704 case 112:
705 out = KEY_CTRL_F6;
706 break;
707 case 113:
708 out = KEY_CTRL_F7;
709 break;
710 case 114:
711 out = KEY_CTRL_F8;
712 break;
713 case 115:
714 out = KEY_CTRL_F9;
715 break;
716 case 116:
717 out = KEY_CTRL_F10;
718 break;
719 default:
720 in_ascii = 1;
721 }
722 if (!in_ascii)
723 {
724 break;
725 }
726 }
727 if (i == 2 && tmp[0] == 79) // Xterm
728 {
729 in_ascii = 0;
730 switch (tmp[1])
731 {
732 case 80:
733 out = KEY_F1;
734 break;
735 case 81:
736 out = KEY_F2;
737 break;
738 case 82:
739 out = KEY_F3;
740 break;
741 case 83:
742 out = KEY_F4;
743 break;
744 default:
745 in_ascii = 1;
746 }
747 if (!in_ascii)
748 {
749 break;
750 }
751 }
752 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
753 {
754 in_ascii = 0;
755 switch (tmp[1])
756 {
757 case 49:
758 out = KEY_HOME;
759 break;
760 case 50:
761 out = KEY_INS;
762 break;
763 case 51:
764 out = KEY_DEL;
765 break;
766 case 52:
767 out = KEY_END;
768 break;
769 case 53:
770 out = KEY_PGUP;
771 break;
772 case 54:
773 out = KEY_PGDN;
774 break;
775 default:
776 in_ascii = 1;
777 }
778 if (!in_ascii)
779 {
780 break;
781 }
782 }
783 if (i == 4 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 126) // Fterm
784 {
785 in_ascii = 0;
786 switch (tmp[2])
787 {
788 case 49:
789 out = KEY_F1;
790 break;
791 case 50:
792 out = KEY_F2;
793 break;
794 case 51:
795 out = KEY_F3;
796 break;
797 case 52:
798 out = KEY_F4;
799 break;
800 case 53:
801 out = KEY_F5;
802 break;
803 case 55:
804 out = KEY_F6;
805 break;
806 case 56:
807 out = KEY_F7;
808 break;
809 case 57:
810 out = KEY_F8;
811 break;
812 default:
813 in_ascii = 1;
814 }
815 if (!in_ascii)
816 {
817 break;
818 }
819 }
820 if (i == 4 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 126) // Fterm
821 {
822 in_ascii = 0;
823 switch (tmp[2])
824 {
825 case 48:
826 out = KEY_F9;
827 break;
828 case 49:
829 out = KEY_F10;
830 break;
831 case 50:
832 out = KEY_F11; // Fterm
833 break;
834 case 51:
835 out = KEY_F11; // Xterm
836 break;
837 case 52:
838 out = KEY_F12; // Xterm
839 break;
840 default:
841 in_ascii = 1;
842 }
843 if (!in_ascii)
844 {
845 break;
846 }
847 }
848 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 53) // Xterm
849 {
850 in_ascii = 0;
851 switch (tmp[4])
852 {
853 case 65:
854 out = KEY_CTRL_UP;
855 break;
856 case 66:
857 out = KEY_CTRL_DOWN;
858 break;
859 case 67:
860 out = KEY_CTRL_RIGHT;
861 break;
862 case 68:
863 out = KEY_CTRL_LEFT;
864 break;
865 case 70:
866 out = KEY_CTRL_END;
867 break;
868 case 72:
869 out = KEY_CTRL_HOME;
870 break;
871 case 80:
872 out = KEY_CTRL_F1;
873 break;
874 case 81:
875 out = KEY_CTRL_F2;
876 break;
877 case 82:
878 out = KEY_CTRL_F3;
879 break;
880 default:
881 in_ascii = 1;
882 }
883 if (!in_ascii)
884 {
885 break;
886 }
887 }
888 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
889 {
890 in_ascii = 0;
891 switch (tmp[2])
892 {
893 case 53:
894 out = KEY_CTRL_F5;
895 break;
896 case 55:
897 out = KEY_CTRL_F6;
898 break;
899 case 56:
900 out = KEY_CTRL_F7;
901 break;
902 case 57:
903 out = KEY_CTRL_F8;
904 break;
905 default:
906 in_ascii = 1;
907 }
908 if (!in_ascii)
909 {
910 break;
911 }
912 }
913 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
914 {
915 in_ascii = 0;
916 switch (tmp[2])
917 {
918 case 48:
919 out = KEY_CTRL_F9;
920 break;
921 case 49:
922 out = KEY_CTRL_F10;
923 break;
924 case 51:
925 out = KEY_CTRL_F11;
926 break;
927 case 52:
928 out = KEY_CTRL_F12;
929 break;
930 default:
931 in_ascii = 1;
932 }
933 if (!in_ascii)
934 {
935 break;
936 }
937 }
938 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 50) // Xterm
939 {
940 in_ascii = 0;
941 switch (tmp[4])
942 {
943 case 80:
944 out = KEY_SHIFT_F1;
945 break;
946 case 81:
947 out = KEY_SHIFT_F2;
948 break;
949 case 82:
950 out = KEY_SHIFT_F3;
951 break;
952 case 83:
953 out = KEY_SHIFT_F4;
954 break;
955 default:
956 in_ascii = 1;
957 }
958 if (!in_ascii)
959 {
960 break;
961 }
962 }
963 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
964 {
965 in_ascii = 0;
966 switch (tmp[2])
967 {
968 case 53:
969 out = KEY_SHIFT_F5;
970 break;
971 case 55:
972 out = KEY_SHIFT_F6;
973 break;
974 case 56:
975 out = KEY_SHIFT_F7;
976 break;
977 case 57:
978 out = KEY_SHIFT_F8;
979 break;
980 default:
981 in_ascii = 1;
982 }
983 if (!in_ascii)
984 {
985 break;
986 }
987 }
988 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
989 {
990 in_ascii = 0;
991 switch (tmp[2])
992 {
993 case 48:
994 out = KEY_SHIFT_F9;
995 break;
996 case 49:
997 out = KEY_SHIFT_F10;
998 break;
999 case 51:
1000 out = KEY_SHIFT_F11;
1001 break;
1002 case 52:
1003 out = KEY_SHIFT_F12;
1004 break;
1005 default:
1006 in_ascii = 1;
1007 }
1008 if (!in_ascii)
1009 {
1010 break;
1011 }
1012 }
1013
1014 if (c == 'm')
1015 {
1016 in_ascii = 0;
1017 }
1018 continue;
1019 }
1020
1021 out = ((int)c + 256) % 256;
1022 break;
1023 }
1024
1025 // For ESC key
1026 if (out == 0 && in_esc)
1027 {
1028 out = KEY_ESC;
1029 }
1030
1031 // for debug
1032 #ifdef _DEBUG
1033 if (out != KEY_TIMEOUT && out != KEY_NULL)
1034 {
1035 log_error("Debug: -->[0x %x]\n", out);
1036 }
1037 #endif
1038
1039 return out;
1040 }
1041
1042 int igetch_t(int sec)
1043 {
1044 int ch;
1045 time_t t_begin = time(NULL);
1046
1047 do
1048 {
1049 ch = igetch(100);
1050 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(NULL) - t_begin < sec));
1051
1052 return ch;
1053 }
1054
1055 void igetch_reset()
1056 {
1057 int ch;
1058 do
1059 {
1060 ch = igetch(100);
1061 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
1062 }
1063
1064 int io_buf_conv(iconv_t cd, char *p_buf, int *p_buf_len, int *p_buf_offset, char *p_conv, size_t conv_size, int *p_conv_len)
1065 {
1066 char *in_buf;
1067 char *out_buf;
1068 size_t in_bytes;
1069 size_t out_bytes;
1070 int ret;
1071 int in_control = 0;
1072 size_t i = 0;
1073 int skip_current = 0;
1074
1075 if (cd == NULL || p_buf == NULL || p_buf_len == NULL || p_buf_offset == NULL || p_conv == NULL || p_conv_len == NULL)
1076 {
1077 log_error("NULL pointer error\n");
1078 return -1;
1079 }
1080
1081 in_buf = p_buf + *p_buf_offset;
1082 in_bytes = (size_t)(*p_buf_len - *p_buf_offset);
1083 out_buf = p_conv + *p_conv_len;
1084 out_bytes = conv_size - (size_t)(*p_conv_len);
1085
1086 while (in_bytes > 0)
1087 {
1088 if ((unsigned char)(*in_buf) == KEY_CONTROL)
1089 {
1090 if (in_control == 0)
1091 {
1092 in_control = 1;
1093 i = 0;
1094 }
1095 }
1096
1097 if (in_control || skip_current)
1098 {
1099 skip_current = 0;
1100
1101 if (out_bytes <= 0)
1102 {
1103 log_error("No enough free space in p_conv, conv_len=%d, conv_size=%d\n", *p_conv_len, conv_size);
1104 return -2;
1105 }
1106
1107 *out_buf = *in_buf;
1108 in_buf++;
1109 out_buf++;
1110 in_bytes--;
1111 out_bytes--;
1112
1113 (*p_buf_offset)++;
1114 (*p_conv_len)++;
1115
1116 i++;
1117 if (i >= 2)
1118 {
1119 in_control = 0;
1120 }
1121 continue;
1122 }
1123
1124 ret = (int)iconv(cd, &in_buf, &in_bytes, &out_buf, &out_bytes);
1125 if (ret == -1)
1126 {
1127 if (errno == EINVAL) // Incomplete
1128 {
1129 #ifdef _DEBUG
1130 log_error("iconv(inbytes=%d, outbytes=%d) error: EINVAL, in_buf[0]=%d\n", in_bytes, out_bytes, in_buf[0]);
1131 #endif
1132 if (p_buf != in_buf)
1133 {
1134 *p_buf_len -= (int)(in_buf - p_buf);
1135 *p_buf_offset = 0;
1136 *p_conv_len = (int)(conv_size - out_bytes);
1137 memmove(p_buf, in_buf, (size_t)(*p_buf_len));
1138 }
1139
1140 break;
1141 }
1142 else if (errno == E2BIG)
1143 {
1144 log_error("iconv(inbytes=%d, outbytes=%d) error: E2BIG\n", in_bytes, out_bytes);
1145 return -1;
1146 }
1147 else if (errno == EILSEQ)
1148 {
1149 if (in_bytes > out_bytes || out_bytes <= 0)
1150 {
1151 log_error("iconv(inbytes=%d, outbytes=%d) error: EILSEQ and E2BIG\n", in_bytes, out_bytes);
1152 return -2;
1153 }
1154
1155 // reset in_bytes when "//IGNORE" is applied
1156 if (in_bytes == 0)
1157 {
1158 in_bytes = (size_t)(*p_buf_len - *p_buf_offset);
1159 #ifdef _DEBUG
1160 log_error("Reset in_bytes from 0 to %d\n", in_bytes);
1161 #endif
1162 }
1163
1164 #ifdef _DEBUG
1165 log_error("iconv(in_bytes=%d, out_bytes=%d) error: EILSEQ, in_buf[0]=%d\n",
1166 in_bytes, out_bytes, in_buf[0]);
1167 #endif
1168 skip_current = 1;
1169 }
1170 else // something strange
1171 {
1172 #ifdef _DEBUG
1173 log_error("iconv(in_bytes=%d, out_bytes=%d) error: %d, in_buf[0]=%d\n",
1174 in_bytes, out_bytes, errno, in_buf[0]);
1175 #endif
1176 *p_buf_offset = (int)(in_buf - p_buf);
1177 *p_conv_len = (int)(conv_size - out_bytes);
1178 skip_current = 1;
1179 }
1180 }
1181 else
1182 {
1183 *p_buf_offset = (int)(in_buf - p_buf);
1184 *p_conv_len = (int)(conv_size - out_bytes);
1185 }
1186 }
1187
1188 if (*p_buf_offset >= *p_buf_len)
1189 {
1190 *p_buf_len = 0;
1191 *p_buf_offset = 0;
1192 }
1193
1194 return 0;
1195 }
1196
1197 int io_conv_init(const char *charset)
1198 {
1199 char tocode[CHARSET_MAX_LEN + 20];
1200
1201 if (charset == NULL)
1202 {
1203 log_error("NULL pointer error\n");
1204 return -1;
1205 }
1206
1207 io_conv_cleanup();
1208
1209 strncpy(stdio_charset, charset, sizeof(stdio_charset) - 1);
1210 stdio_charset[sizeof(stdio_charset) - 1] = '\0';
1211
1212 snprintf(tocode, sizeof(tocode), "%s%s", BBS_default_charset,
1213 (strcasecmp(stdio_charset, BBS_default_charset) == 0 ? "" : "//IGNORE"));
1214 stdin_cd = iconv_open(tocode, stdio_charset);
1215 if (stdin_cd == (iconv_t)(-1))
1216 {
1217 log_error("iconv_open(%s->%s) error: %d\n", stdio_charset, tocode, errno);
1218 return -2;
1219 }
1220
1221 snprintf(tocode, sizeof(tocode), "%s%s", stdio_charset,
1222 (strcasecmp(BBS_default_charset, stdio_charset) == 0 ? "" : "//TRANSLIT"));
1223 stdout_cd = iconv_open(tocode, BBS_default_charset);
1224 if (stdout_cd == (iconv_t)(-1))
1225 {
1226 log_error("iconv_open(%s->%s) error: %d\n", BBS_default_charset, tocode, errno);
1227 iconv_close(stdin_cd);
1228 stdin_cd = (iconv_t)(-1);
1229 return -2;
1230 }
1231
1232 return 0;
1233 }
1234
1235 int io_conv_cleanup(void)
1236 {
1237 if (stdin_cd != (iconv_t)(-1))
1238 {
1239 iconv_close(stdin_cd);
1240 stdin_cd = (iconv_t)(-1);
1241 }
1242 if (stdout_cd != (iconv_t)(-1))
1243 {
1244 iconv_close(stdout_cd);
1245 stdout_cd = (iconv_t)(-1);
1246 }
1247
1248 return 0;
1249 }

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