Change of vterm handling logic on backend chardev input event (#40)
[lunaix-os.git] / lunaix-os / hal / term / lcntls / lcntl.c
1 /**
2  * @file lcntl.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief Line controller master that handle all POSIX-control code
5  * @version 0.1
6  * @date 2023-11-25
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11 #include <hal/term.h>
12 #include <usr/lunaix/term.h>
13
14 #include "lcntl.h"
15
16 #include <lunaix/process.h>
17 #include <lunaix/spike.h>
18
19 static inline void
20 init_lcntl_state(struct lcntl_state* state, 
21                  struct term* tdev, enum lcntl_dir direction)
22 {
23     *state = (struct lcntl_state) {
24         .tdev = tdev,
25         ._lf  = tdev->lflags,
26         ._cf  = tdev->cflags,
27         .direction = direction,
28         .echobuf = tdev->line_out.current,
29     };
30
31     struct linebuffer* lb;
32     if (direction == INBOUND) {
33         state->_if = tdev->iflags;
34         lb = &tdev->line_in;
35     }
36     else {
37         state->_of = tdev->oflags;
38         lb = &tdev->line_out;
39     }
40
41     state->inbuf = lb->current;
42     state->outbuf = lb->next;
43     state->active_line = lb;
44 }
45
46 static inline void
47 raise_sig(struct lcntl_state* state, int sig)
48 {
49     term_sendsig(state->tdev, sig);
50     lcntl_raise_line_event(state, LEVT_SIGRAISE);
51 }
52
53 static inline char
54 __remap_character(struct lcntl_state* state, char c)
55 {
56     if (c == '\r') {
57         if ((state->_if & _ICRNL) || (state->_of & _OCRNL)) {
58             return '\n';
59         }
60     } 
61     else if (c == '\n') {
62         if ((state->_if & _INLCR) || (state->_of & (_ONLRET))) {
63             return '\r';
64         }
65     }
66     else if ('a' <= c && c <= 'z') {
67         if ((state->_if & _IUCLC)) {
68             return c | 0b100000;
69         } else if ((state->_of & _OLCUC)) {
70             return c & ~0b100000;
71         }
72     }
73
74     return c;
75 }
76
77 static inline void
78 lcntl_echo_char(struct lcntl_state* state, char c)
79 {
80     rbuffer_put(state->echobuf, c);
81 }
82
83 int
84 __ansi_actcontrol(struct lcntl_state* state, char chr);
85
86 static inline int must_inline
87 lcntl_transform_seq(struct lcntl_state* state)
88 {
89     struct term* tdev = state->tdev;
90     char c;
91     int i = 0;
92     bool full = false;
93
94 #define EOL tdev->cc[_VEOL]
95 #define EOF tdev->cc[_VEOF]
96 #define ERASE tdev->cc[_VERASE]
97 #define KILL tdev->cc[_VKILL]
98 #define INTR tdev->cc[_VINTR]
99 #define QUIT tdev->cc[_VQUIT]
100 #define SUSP tdev->cc[_VSUSP]
101
102     while (!lcntl_test_flag(state, LCNTLF_STOP)) 
103     {
104         lcntl_unset_flag(state, LCNTLF_SPECIAL_CHAR);
105
106         if (!rbuffer_get(state->inbuf, &c)) {
107             break;
108         }
109
110         c = __remap_character(state, c);
111
112         if (c == '\0') {
113             if ((state->_if & _IGNBRK)) {
114                 continue;
115             }
116
117             if ((state->_if & _BRKINT)) {
118                 raise_sig(state, SIGINT);
119                 break;
120             }
121         }
122
123         if (lcntl_outbound(state)) {
124             goto do_out;
125         }
126
127         if (c == '\n') {
128             if (lcntl_check_echo(state, _ECHONL)) {
129                 lcntl_echo_char(state, c);
130             }
131         }
132
133         // For input procesing
134
135         lcntl_set_flag(state, LCNTLF_SPECIAL_CHAR);
136
137         if (c == '\n' || c == EOL) {
138             lcntl_raise_line_event(state, LEVT_EOL);
139         }
140         else if (c == EOF) {
141             lcntl_raise_line_event(state, LEVT_EOF);
142             lcntl_set_flag(state, LCNTLF_CLEAR_INBUF);
143             lcntl_set_flag(state, LCNTLF_STOP);
144         }
145         else if (c == INTR) {
146             raise_sig(state, SIGINT);
147             lcntl_set_flag(state, LCNTLF_CLEAR_OUTBUF);
148         }
149         else if (c == QUIT) {
150             raise_sig(state, SIGKILL);
151             lcntl_set_flag(state, LCNTLF_CLEAR_OUTBUF);
152             lcntl_set_flag(state, LCNTLF_STOP);
153         }
154         else if (c == SUSP) {
155             raise_sig(state, SIGSTOP);
156         }
157         else if (c == ERASE) {
158             if (rbuffer_erase(state->outbuf) && 
159                 lcntl_check_echo(state, _ECHOE)) 
160             {
161                 lcntl_echo_char(state, '\x8'); 
162                 lcntl_echo_char(state, ' '); 
163                 lcntl_echo_char(state, '\x8');
164             }
165             continue;
166         }
167         else if (c == KILL) {
168             lcntl_set_flag(state, LCNTLF_CLEAR_OUTBUF);
169         }
170         else {
171             lcntl_unset_flag(state, LCNTLF_SPECIAL_CHAR);
172         }
173
174         if (lcntl_check_echo(state, _ECHOK) && c == KILL) {
175             lcntl_echo_char(state, c);
176             lcntl_echo_char(state, '\n');
177         }
178
179     do_out:
180         if (c == '\n' && (state->_of & _ONLCR)) {
181             full = !rbuffer_put_nof(state->outbuf, '\r');
182         }
183
184         if (!full) {
185             if (lcntl_inbound(state) && (state->_lf & _IEXTEN)) {
186                 full = !__ansi_actcontrol(state, c);
187             }
188             else {
189                 full = !lcntl_put_char(state, c);
190             }
191         }
192
193         if (lcntl_test_flag(state, LCNTLF_CLEAR_INBUF)) {
194             rbuffer_clear(state->inbuf);
195             lcntl_unset_flag(state, LCNTLF_CLEAR_INBUF);
196         }
197         
198         if (lcntl_test_flag(state, LCNTLF_CLEAR_OUTBUF)) {
199             rbuffer_clear(state->outbuf);
200             lcntl_unset_flag(state, LCNTLF_CLEAR_OUTBUF);
201         }
202
203         i++;
204     }
205
206     if (state->direction != OUTBOUND && !(state->_lf & _NOFLSH)) {
207         term_flush(tdev);
208     }
209
210     line_flip(state->active_line);
211
212     return i;
213 }
214
215 int
216 lcntl_transform_inseq(struct term* tdev)
217 {
218     struct lcntl_state state;
219
220     init_lcntl_state(&state, tdev, INBOUND);
221     return lcntl_transform_seq(&state);
222 }
223
224 int
225 lcntl_transform_outseq(struct term* tdev)
226 {
227     struct lcntl_state state;
228
229     init_lcntl_state(&state, tdev, OUTBOUND);
230     return lcntl_transform_seq(&state);
231 }
232
233 int 
234 lcntl_put_char(struct lcntl_state* state, char c)
235 {
236     if (lcntl_check_echo(state, _ECHO)) {
237         lcntl_echo_char(state, c);
238     }
239
240     if (!lcntl_test_flag(state, LCNTLF_SPECIAL_CHAR)) {
241         return rbuffer_put_nof(state->outbuf, c);
242     }
243
244     return 1;
245 }