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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.63 - (show annotations)
Tue Nov 4 14:58:56 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.62: +1 -1 lines
Content type: text/x-csrc
Refine file header information comments

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

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