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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.67 - (show annotations)
Mon Nov 17 06:41:18 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.66: +87 -8 lines
Content type: text/x-csrc
Add --with-epoll to configure to switch between epoll and poll

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

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