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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.64 - (show annotations)
Wed Nov 5 02:48:48 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.63: +8 -7 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers
Use const char * instead of macro define for constant strings

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

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