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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.76 - (show annotations)
Thu Dec 18 03:23:48 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.75: +4 -4 lines
Content type: text/x-csrc
Do not monitor EPOLLRDHUP / POLLRDHUP, since POLLRDHUP is not supported by MSYS2.

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

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