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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.66 - (show annotations)
Mon Nov 17 02:32:42 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.65: +97 -69 lines
Content type: text/x-csrc
Optimize epoll handling to avoid repeated create / close
Modify several extern variables to static
Cleanup more server-side variables after fork of client process

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

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