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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.77 - (show annotations)
Thu Dec 18 11:18:29 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.76: +78 -24 lines
Content type: text/x-csrc
Refine and fix bugs

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

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