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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.83 - (show annotations)
Sun Jan 4 13:22:58 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.82: +6 -0 lines
Content type: text/x-csrc
Mapping key Backspace (0x7f) to \b (0x8)

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

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