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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.73 - (show annotations)
Wed Dec 17 03:56:39 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.72: +6 -0 lines
Content type: text/x-csrc
Refine debug log

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

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