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

Annotation of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.36 - (hide annotations)
Wed May 28 10:26:21 2025 UTC (9 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.35: +6 -0 lines
Content type: text/x-csrc
Refine ESC key handling

1 sysadm 1.1 /***************************************************************************
2 sysadm 1.14 io.c - description
3     -------------------
4 sysadm 1.20 Copyright : (C) 2004-2025 by Leaflet
5     Email : leaflet@leafok.com
6 sysadm 1.1 ***************************************************************************/
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 sysadm 1.20 * the Free Software Foundation; either version 3 of the License, or *
13 sysadm 1.1 * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17     #include "io.h"
18 sysadm 1.16 #include "log.h"
19 sysadm 1.1 #include "common.h"
20 sysadm 1.22 #include <errno.h>
21 sysadm 1.1 #include <stdio.h>
22     #include <stdarg.h>
23 sysadm 1.28 #include <string.h>
24 sysadm 1.9 #include <time.h>
25     #include <fcntl.h>
26     #include <unistd.h>
27 sysadm 1.22 #include <sys/select.h>
28 sysadm 1.5 #include <sys/ioctl.h>
29 sysadm 1.29 #include <sys/epoll.h>
30 sysadm 1.1
31 sysadm 1.28 static char stdout_buf[BUFSIZ];
32     static int stdout_buf_len = 0;
33 sysadm 1.30 static int stdout_buf_offset = 0;
34 sysadm 1.1
35 sysadm 1.14 int prints(const char *format, ...)
36 sysadm 1.1 {
37 sysadm 1.28 char buf[BUFSIZ];
38 sysadm 1.14 va_list args;
39 sysadm 1.28 int ret;
40 sysadm 1.1
41 sysadm 1.14 va_start(args, format);
42 sysadm 1.28 ret = vsnprintf(buf, sizeof(buf), format, args);
43 sysadm 1.14 va_end(args);
44 sysadm 1.1
45 sysadm 1.30 if (ret > 0)
46 sysadm 1.28 {
47 sysadm 1.30 if (stdout_buf_len + ret > BUFSIZ)
48     {
49     iflush();
50     }
51 sysadm 1.31
52 sysadm 1.30 if (stdout_buf_len + ret <= BUFSIZ)
53     {
54     memcpy(stdout_buf + stdout_buf_len, buf, (size_t)ret);
55     stdout_buf_len += ret;
56     }
57     else
58     {
59     errno = EAGAIN;
60     ret = (BUFSIZ - stdout_buf_len - ret);
61 sysadm 1.35 log_error("Output buffer is full, additional %d is required\n", ret);
62 sysadm 1.30 }
63 sysadm 1.28 }
64 sysadm 1.30
65     return ret;
66     }
67    
68     int outc(char c)
69     {
70     int ret;
71    
72     if (stdout_buf_len + 1 > BUFSIZ)
73 sysadm 1.28 {
74 sysadm 1.30 iflush();
75     }
76    
77     if (stdout_buf_len + 1 <= BUFSIZ)
78     {
79     stdout_buf[stdout_buf_len] = c;
80     stdout_buf_len++;
81     }
82     else
83     {
84     errno = EAGAIN;
85     ret = -1;
86 sysadm 1.28 }
87    
88     return ret;
89 sysadm 1.1 }
90    
91 sysadm 1.14 int iflush()
92 sysadm 1.1 {
93 sysadm 1.28 int flags;
94 sysadm 1.29 struct epoll_event ev, events[MAX_EVENTS];
95     int nfds, epollfd;
96 sysadm 1.30 int retry;
97 sysadm 1.28 int ret = 0;
98    
99 sysadm 1.29 epollfd = epoll_create1(0);
100     if (epollfd < 0)
101     {
102     log_error("epoll_create1() error (%d)\n", errno);
103     return -1;
104     }
105    
106     ev.events = EPOLLOUT;
107     ev.data.fd = STDOUT_FILENO;
108     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
109     {
110     log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
111 sysadm 1.31 if (close(epollfd) < 0)
112     {
113     log_error("close(epoll) error (%d)\n");
114     }
115 sysadm 1.29 return -1;
116     }
117    
118 sysadm 1.28 // Set STDOUT as non-blocking
119     flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
120     fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
121    
122 sysadm 1.30 // Retry wait / flush for at most 3 times
123     retry = 3;
124     while (retry > 0 && !SYS_server_exit)
125 sysadm 1.28 {
126 sysadm 1.30 retry--;
127    
128 sysadm 1.29 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
129 sysadm 1.28
130 sysadm 1.29 if (nfds < 0)
131 sysadm 1.28 {
132 sysadm 1.29 if (errno != EINTR)
133     {
134     log_error("epoll_wait() error (%d)\n", errno);
135     break;
136     }
137 sysadm 1.28 continue;
138     }
139 sysadm 1.29 else if (nfds == 0) // timeout
140 sysadm 1.28 {
141 sysadm 1.29 continue;
142 sysadm 1.28 }
143 sysadm 1.29
144     for (int i = 0; i < nfds; i++)
145 sysadm 1.28 {
146 sysadm 1.29 if (events[i].data.fd == STDOUT_FILENO)
147 sysadm 1.28 {
148 sysadm 1.30 while (stdout_buf_offset < stdout_buf_len && !SYS_server_exit) // write until complete or error
149 sysadm 1.28 {
150 sysadm 1.29 ret = (int)write(STDOUT_FILENO, stdout_buf + stdout_buf_offset, (size_t)(stdout_buf_len - stdout_buf_offset));
151     if (ret < 0)
152     {
153     if (errno == EAGAIN || errno == EWOULDBLOCK)
154     {
155     break;
156     }
157     else if (errno == EINTR)
158     {
159     continue;
160     }
161     else
162     {
163     log_error("write(STDOUT) error (%d)\n", errno);
164 sysadm 1.30 retry = 0;
165 sysadm 1.29 break;
166     }
167     }
168     else if (ret == 0) // broken pipe
169     {
170 sysadm 1.30 retry = 0;
171 sysadm 1.29 break;
172     }
173     else
174     {
175     stdout_buf_offset += ret;
176 sysadm 1.30 if (stdout_buf_offset >= stdout_buf_len) // flush buffer completely
177 sysadm 1.29 {
178     ret = 0;
179 sysadm 1.30 stdout_buf_offset = 0;
180 sysadm 1.29 stdout_buf_len = 0;
181 sysadm 1.30 retry = 0;
182 sysadm 1.29 break;
183     }
184     continue;
185     }
186 sysadm 1.28 }
187     }
188     }
189     }
190 sysadm 1.6
191 sysadm 1.28 // Restore STDOUT flags
192     fcntl(STDOUT_FILENO, F_SETFL, flags);
193 sysadm 1.6
194 sysadm 1.31 if (close(epollfd) < 0)
195     {
196     log_error("close(epoll) error (%d)\n");
197     }
198    
199 sysadm 1.28 return ret;
200 sysadm 1.1 }
201    
202 sysadm 1.31 int igetch(int timeout)
203 sysadm 1.1 {
204 sysadm 1.18 // static input buffer
205 sysadm 1.22 static unsigned char buf[LINE_BUFFER_LEN];
206 sysadm 1.31 static int len = 0;
207 sysadm 1.18 static int pos = 0;
208    
209 sysadm 1.29 struct epoll_event ev, events[MAX_EVENTS];
210     int nfds, epollfd;
211 sysadm 1.31 int ret;
212     int loop;
213 sysadm 1.21
214 sysadm 1.22 unsigned char tmp[LINE_BUFFER_LEN];
215 sysadm 1.31 int out = KEY_NULL;
216 sysadm 1.18 int in_esc = 0;
217     int in_ascii = 0;
218     int in_control = 0;
219     int i = 0;
220 sysadm 1.26 int flags;
221 sysadm 1.1
222 sysadm 1.29 epollfd = epoll_create1(0);
223     if (epollfd < 0)
224     {
225     log_error("epoll_create1() error (%d)\n", errno);
226     return -1;
227     }
228    
229     ev.events = EPOLLIN;
230     ev.data.fd = STDIN_FILENO;
231     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
232 sysadm 1.14 {
233 sysadm 1.29 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
234 sysadm 1.31
235     if (close(epollfd) < 0)
236     {
237     log_error("close(epoll) error (%d)\n");
238     }
239 sysadm 1.29 return -1;
240     }
241 sysadm 1.9
242 sysadm 1.29 flags = fcntl(STDIN_FILENO, F_GETFL, 0);
243     fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
244 sysadm 1.9
245 sysadm 1.31 loop = 1;
246    
247     while (loop && pos >= len && !SYS_server_exit)
248 sysadm 1.29 {
249 sysadm 1.31 len = 0;
250     pos = 0;
251    
252     nfds = epoll_wait(epollfd, events, MAX_EVENTS, timeout);
253 sysadm 1.9
254 sysadm 1.29 if (nfds < 0)
255 sysadm 1.14 {
256 sysadm 1.21 if (errno != EINTR)
257     {
258 sysadm 1.29 log_error("epoll_wait() error (%d)\n", errno);
259 sysadm 1.31 break;
260 sysadm 1.21 }
261     continue;
262 sysadm 1.14 }
263 sysadm 1.31 else if (nfds == 0) // timeout
264 sysadm 1.14 {
265 sysadm 1.31 out = KEY_TIMEOUT;
266     break;
267 sysadm 1.14 }
268 sysadm 1.21
269 sysadm 1.29 for (int i = 0; i < nfds; i++)
270 sysadm 1.14 {
271 sysadm 1.29 if (events[i].data.fd == STDIN_FILENO)
272 sysadm 1.26 {
273 sysadm 1.31 while (len < sizeof(buf) && !SYS_server_exit) // read until complete or error
274 sysadm 1.26 {
275 sysadm 1.31 ret = (int)read(STDIN_FILENO, buf + len, sizeof(buf) - (size_t)len);
276     if (ret < 0)
277 sysadm 1.29 {
278     if (errno == EAGAIN || errno == EWOULDBLOCK)
279     {
280 sysadm 1.32 out = 0;
281 sysadm 1.31 loop = 0;
282     break;
283 sysadm 1.29 }
284     else if (errno == EINTR)
285     {
286     continue;
287     }
288     else
289     {
290     log_error("read(STDIN) error (%d)\n", errno);
291 sysadm 1.31 loop = 0;
292     break;
293 sysadm 1.29 }
294     }
295 sysadm 1.31 else if (ret == 0) // broken pipe
296 sysadm 1.29 {
297 sysadm 1.31 loop = 0;
298     break;
299     }
300     else
301     {
302     len += ret;
303     continue;
304 sysadm 1.29 }
305 sysadm 1.26 }
306     }
307 sysadm 1.14 }
308    
309     // For debug
310 sysadm 1.33 // for (int j = pos; j < len; j++)
311     // {
312     // log_std("Debug: <--[%u]\n", (buf[j] + 256) % 256);
313     // }
314 sysadm 1.11 }
315 sysadm 1.14
316 sysadm 1.29 fcntl(STDIN_FILENO, F_SETFL, flags);
317    
318 sysadm 1.14 while (pos < len)
319 sysadm 1.11 {
320 sysadm 1.18 unsigned char c = buf[pos++];
321 sysadm 1.5
322 sysadm 1.14 if (c == KEY_CONTROL)
323     {
324     if (in_control == 0)
325     {
326     in_control = 1;
327     i = 0;
328     continue;
329     }
330     }
331 sysadm 1.1
332 sysadm 1.14 if (in_control)
333     {
334     tmp[i++] = c;
335     if (i >= 2)
336     {
337     out = (int)tmp[0] * 256 + tmp[1];
338     in_control = 0;
339     break;
340     }
341     continue;
342     }
343 sysadm 1.6
344 sysadm 1.14 if (c == ESC_KEY)
345     {
346     if (in_esc == 0)
347     {
348     in_esc = 1;
349     in_ascii = 1;
350     i = 0;
351     continue;
352     }
353     else
354     {
355     out = ESC_KEY;
356     in_esc = 0;
357     break;
358     }
359     }
360 sysadm 1.3
361 sysadm 1.14 in_esc = 0;
362 sysadm 1.1
363 sysadm 1.14 if (in_ascii)
364     {
365     tmp[i++] = c;
366 sysadm 1.33 if (i == 2 && (tmp[0] == 79 || tmp[0] == 91))
367 sysadm 1.14 {
368     in_ascii = 0;
369 sysadm 1.33 switch (tmp[1])
370 sysadm 1.14 {
371 sysadm 1.33 case 65:
372 sysadm 1.14 out = KEY_UP;
373     break;
374 sysadm 1.33 case 66:
375 sysadm 1.14 out = KEY_DOWN;
376     break;
377 sysadm 1.33 case 67:
378 sysadm 1.14 out = KEY_RIGHT;
379     break;
380 sysadm 1.33 case 68:
381 sysadm 1.14 out = KEY_LEFT;
382     break;
383 sysadm 1.33 default:
384     in_ascii = 1;
385     }
386     if (!in_ascii)
387     {
388     break;
389     }
390     }
391     if (i == 2 && tmp[0] == 91) // Fterm
392     {
393     in_ascii = 0;
394     switch (tmp[1])
395     {
396     case 86:
397     out = KEY_SHIFT_F1;
398     break;
399     case 90:
400     out = KEY_SHIFT_F2;
401     break;
402     case 97:
403     out = KEY_SHIFT_F3;
404     break;
405     case 98:
406     out = KEY_SHIFT_F4;
407     break;
408     case 99:
409     out = KEY_SHIFT_F5;
410     break;
411     case 100:
412     out = KEY_SHIFT_F6;
413     break;
414     case 101:
415     out = KEY_SHIFT_F7;
416     break;
417     case 102:
418     out = KEY_SHIFT_F8;
419     break;
420     case 103:
421     out = KEY_SHIFT_F9;
422     break;
423     case 104:
424     out = KEY_SHIFT_F10;
425     break;
426     case 107:
427     out = KEY_CTRL_F1;
428     break;
429     case 108:
430     out = KEY_CTRL_F2;
431     break;
432     case 109:
433     out = KEY_CTRL_F3;
434     break;
435     case 112:
436     out = KEY_CTRL_F6;
437     break;
438     case 113:
439     out = KEY_CTRL_F7;
440     break;
441     case 114:
442     out = KEY_CTRL_F8;
443     break;
444     case 115:
445     out = KEY_CTRL_F9;
446     break;
447     case 116:
448     out = KEY_CTRL_F10;
449     break;
450     default:
451     in_ascii = 1;
452     }
453     if (!in_ascii)
454     {
455     break;
456     }
457     }
458     if (i == 2 && tmp[0] == 79) // Xterm
459     {
460     in_ascii = 0;
461     switch (tmp[1])
462     {
463     case 80:
464     out = KEY_F1;
465     break;
466     case 81:
467     out = KEY_F2;
468     break;
469     case 82:
470     out = KEY_F3;
471     break;
472     case 83:
473     out = KEY_F4;
474     break;
475     default:
476     in_ascii = 1;
477     }
478     if (!in_ascii)
479     {
480     break;
481 sysadm 1.14 }
482     }
483     if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
484     {
485     in_ascii = 0;
486     switch (tmp[1])
487     {
488     case 49:
489     out = KEY_HOME;
490     break;
491     case 51:
492     out = KEY_DEL;
493     break;
494     case 52:
495     out = KEY_END;
496     break;
497     case 53:
498     out = KEY_PGUP;
499     break;
500     case 54:
501     out = KEY_PGDN;
502     break;
503 sysadm 1.33 default:
504     in_ascii = 1;
505     }
506     if (!in_ascii)
507     {
508     break;
509     }
510     }
511     if (i == 4 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 126) // Fterm
512     {
513     in_ascii = 0;
514     switch (tmp[2])
515     {
516     case 49:
517     out = KEY_F1;
518     break;
519     case 50:
520     out = KEY_F2;
521     break;
522     case 51:
523     out = KEY_F3;
524     break;
525     case 52:
526     out = KEY_F4;
527     break;
528     case 53:
529     out = KEY_F5;
530     break;
531     case 55:
532     out = KEY_F6;
533     break;
534     case 56:
535     out = KEY_F7;
536     break;
537     case 57:
538     out = KEY_F8;
539     break;
540     default:
541     in_ascii = 1;
542     }
543     if (!in_ascii)
544     {
545     break;
546     }
547     }
548     if (i == 4 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 126) // Fterm
549     {
550     in_ascii = 0;
551     switch (tmp[2])
552     {
553     case 48:
554     out = KEY_F9;
555     break;
556     case 49:
557     out = KEY_F10;
558     break;
559     case 50:
560     out = KEY_F11; // Fterm
561     break;
562     case 51:
563     out = KEY_F11; // Xterm
564     break;
565     case 52:
566     out = KEY_F12; // Xterm
567     break;
568     default:
569     in_ascii = 1;
570     }
571     if (!in_ascii)
572     {
573     break;
574     }
575     }
576     if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 53) // Xterm
577     {
578     in_ascii = 0;
579     switch (tmp[4])
580     {
581     case 80:
582     out = KEY_CTRL_F1;
583     break;
584     case 81:
585     out = KEY_CTRL_F2;
586     break;
587     case 82:
588     out = KEY_CTRL_F3;
589     break;
590     default:
591     in_ascii = 1;
592     }
593     if (!in_ascii)
594     {
595     break;
596     }
597     }
598     if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
599     {
600     in_ascii = 0;
601     switch (tmp[2])
602     {
603     case 53:
604     out = KEY_CTRL_F5;
605     break;
606     case 55:
607     out = KEY_CTRL_F6;
608     break;
609     case 56:
610     out = KEY_CTRL_F7;
611     break;
612     case 57:
613     out = KEY_CTRL_F8;
614     break;
615     default:
616     in_ascii = 1;
617     }
618     if (!in_ascii)
619     {
620     break;
621     }
622     }
623     if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
624     {
625     in_ascii = 0;
626     switch (tmp[2])
627     {
628     case 48:
629     out = KEY_CTRL_F9;
630     break;
631     case 49:
632     out = KEY_CTRL_F10;
633     break;
634     case 51:
635     out = KEY_CTRL_F11;
636     break;
637     case 52:
638     out = KEY_CTRL_F12;
639     break;
640     default:
641     in_ascii = 1;
642     }
643     if (!in_ascii)
644     {
645     break;
646     }
647     }
648     if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 50) // Xterm
649     {
650     in_ascii = 0;
651     switch (tmp[4])
652     {
653     case 80:
654     out = KEY_SHIFT_F1;
655     break;
656     case 81:
657     out = KEY_SHIFT_F2;
658     break;
659     case 82:
660     out = KEY_SHIFT_F3;
661     break;
662     case 83:
663     out = KEY_SHIFT_F4;
664     break;
665     default:
666     in_ascii = 1;
667     }
668     if (!in_ascii)
669     {
670     break;
671 sysadm 1.14 }
672 sysadm 1.33 }
673     if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
674     {
675     in_ascii = 0;
676     switch (tmp[2])
677     {
678     case 53:
679     out = KEY_SHIFT_F5;
680     break;
681     case 55:
682     out = KEY_SHIFT_F6;
683     break;
684     case 56:
685     out = KEY_SHIFT_F7;
686     break;
687     case 57:
688     out = KEY_SHIFT_F8;
689     break;
690     default:
691     in_ascii = 1;
692     }
693     if (!in_ascii)
694     {
695     break;
696     }
697     }
698     if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
699     {
700     in_ascii = 0;
701     switch (tmp[2])
702     {
703     case 48:
704     out = KEY_SHIFT_F9;
705     break;
706     case 49:
707     out = KEY_SHIFT_F10;
708     break;
709     case 51:
710     out = KEY_SHIFT_F11;
711     break;
712     case 52:
713     out = KEY_SHIFT_F12;
714     break;
715     default:
716     in_ascii = 1;
717     }
718     if (!in_ascii)
719     {
720     break;
721     }
722     }
723    
724     if (c == 'm')
725     {
726     in_ascii = 0;
727 sysadm 1.14 }
728     continue;
729     }
730 sysadm 1.1
731 sysadm 1.14 out = ((int)c + 256) % 256;
732     break;
733 sysadm 1.1 }
734    
735 sysadm 1.31 if (close(epollfd) < 0)
736     {
737     log_error("close(epoll) error (%d)\n");
738     }
739    
740 sysadm 1.36 // For ESC key
741     if (out == 0 && in_esc)
742     {
743     out = KEY_ESC;
744     }
745    
746 sysadm 1.33 // for debug
747 sysadm 1.34 // if (out != KEY_TIMEOUT && out != KEY_NULL)
748     // {
749     // log_std ("Debug: -->[0x %x]\n", out);
750     // }
751 sysadm 1.33
752 sysadm 1.14 return out;
753 sysadm 1.1 }
754 sysadm 1.5
755 sysadm 1.31 int igetch_t(int sec)
756 sysadm 1.9 {
757 sysadm 1.14 int ch;
758     time_t t_begin = time(0);
759 sysadm 1.10
760 sysadm 1.14 do
761     {
762 sysadm 1.31 ch = igetch(100);
763 sysadm 1.25 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(0) - t_begin < sec));
764 sysadm 1.9
765 sysadm 1.14 return ch;
766 sysadm 1.9 }
767 sysadm 1.31
768     void igetch_reset()
769     {
770     int ch;
771     do
772     {
773     ch = igetch(0);
774     } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
775     }

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