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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.71 - (show annotations)
Sun Nov 23 06:43:51 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.70: +9 -17 lines
Content type: text/x-csrc
Refine

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

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