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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.60 - (show annotations)
Mon Oct 20 01:55:50 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.59: +9 -4 lines
Content type: text/x-csrc
Do not apply //IGNORE or //TRANSLIT if tocode and fromcode is the same

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

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