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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.65 - (show annotations)
Tue Nov 11 00:28:05 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.64: +4 -0 lines
Content type: text/x-csrc
Use config.h

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

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