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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.61 - (show annotations)
Tue Nov 4 05:24:09 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.60: +71 -57 lines
Content type: text/x-csrc
Fix bug: remaining input data in SSH read buffer could not be detected by epoll_wait on STDIN
Add stdin_read_wait to indicate the last read state in igetch()
When stdin_read_wait = 1, ssh_channel_read_nonblocking / read will be performed directly without check with epoll_wait

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

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