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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.54 - (show annotations)
Sat Oct 18 01:50:48 2025 UTC (4 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.53: +65 -0 lines
Content type: text/x-csrc
Move io_buf_conv() to io.c(.h)

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

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