* Make the ksym table built-in with kernel image, thus remove the need
[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 <lunaix/process.h>
15 #include <lunaix/spike.h>
16
17 static inline void
18 raise_sig(struct term* at_term, struct linebuffer* lbuf, int sig)
19 {
20     term_sendsig(at_term, sig);
21     lbuf->sflags |= LSTATE_SIGRAISE;
22 }
23
24 static inline int must_inline optimize("-fipa-cp-clone")
25 lcntl_transform_seq(struct term* tdev, struct linebuffer* lbuf, bool out)
26 {
27     struct rbuffer* raw = lbuf->current;
28     struct rbuffer* cooked = lbuf->next;
29     struct rbuffer* output = tdev->line_out.current;
30
31     int i = 0, _if = tdev->iflags & -!out, _of = tdev->oflags & -!!out,
32         _lf = tdev->lflags;
33     int allow_more = 1, latest_eol = cooked->ptr;
34     char c;
35
36     int (*lcntl_slave_put)(struct term*, struct linebuffer*, char) =
37         tdev->lcntl->process_and_put;
38
39 #define EOL tdev->cc[_VEOL]
40 #define EOF tdev->cc[_VEOF]
41 #define ERASE tdev->cc[_VERASE]
42 #define KILL tdev->cc[_VKILL]
43 #define INTR tdev->cc[_VINTR]
44 #define QUIT tdev->cc[_VQUIT]
45 #define SUSP tdev->cc[_VSUSP]
46
47 #define putc_safe(rb, chr)                  \
48     ({                                      \
49         if (!rbuffer_put_nof(rb, chr)) {    \
50             break;                          \
51         }                                   \
52     })
53
54     if (!out) {
55         // Keep all cc's (except VMIN & VTIME) up to L2 cache.
56         for (size_t i = 0; i < _VMIN; i++) {
57             prefetch_rd(&tdev->cc[i], 2);
58         }
59     }
60
61     while (allow_more && rbuffer_get(raw, &c)) {
62
63         if (c == '\r' && ((_if & _ICRNL) || (_of & _OCRNL))) {
64             c = '\n';
65         } else if (c == '\n') {
66             if ((_if & _INLCR) || (_of & (_ONLRET))) {
67                 c = '\r';
68             }
69         }
70
71         if (c == '\0') {
72             if ((_if & _IGNBRK)) {
73                 continue;
74             }
75
76             if ((_if & _BRKINT)) {
77                 raise_sig(tdev, lbuf, SIGINT);
78                 break;
79             }
80         }
81
82         if ('a' <= c && c <= 'z') {
83             if ((_if & _IUCLC)) {
84                 c = c | 0b100000;
85             } else if ((_of & _OLCUC)) {
86                 c = c & ~0b100000;
87             }
88         }
89
90         if (out) {
91             goto do_out;
92         }
93
94         if (c == '\n') {
95             latest_eol = cooked->ptr + 1;
96             if ((_lf & _ECHONL)) {
97                 rbuffer_put(output, c);
98             }
99         }
100
101         // For input procesing
102
103         if (c == '\n' || c == EOL) {
104             lbuf->sflags |= LSTATE_EOL;
105         } else if (c == EOF) {
106             lbuf->sflags |= LSTATE_EOF;
107             rbuffer_clear(raw);
108             break;
109         } else if (c == INTR) {
110             raise_sig(tdev, lbuf, SIGINT);
111         } else if (c == QUIT) {
112             raise_sig(tdev, lbuf, SIGKILL);
113             break;
114         } else if (c == SUSP) {
115             raise_sig(tdev, lbuf, SIGSTOP);
116         } else if (c == ERASE) {
117             if (!rbuffer_erase(cooked))
118                 continue;
119         } else if (c == KILL) {
120             // TODO shrink the rbuffer
121         } else {
122             goto keep;
123         }
124
125         if ((_lf & _ECHOE) && c == ERASE) {
126             rbuffer_put(output, '\x8'); 
127             rbuffer_put(output, ' '); 
128             rbuffer_put(output, '\x8'); 
129         }
130         if ((_lf & _ECHOK) && c == KILL) {
131             rbuffer_put(output, c);
132             rbuffer_put(output, '\n');
133         }
134
135         continue;
136
137     keep:
138         if ((_lf & _ECHO)) {
139             rbuffer_put(output, c);
140         }
141
142         goto put_char;
143
144     do_out:
145         if (c == '\n' && (_of & _ONLCR)) {
146             putc_safe(cooked, '\r');
147         }
148
149     put_char:
150         if (!out && (_lf & _IEXTEN) && lcntl_slave_put) {
151             allow_more = lcntl_slave_put(tdev, lbuf, c);
152         } else {
153             allow_more = rbuffer_put_nof(cooked, c);
154         }
155     }
156
157     if (!out && !rbuffer_empty(output) && !(_lf & _NOFLSH)) {
158         term_flush(tdev);
159     }
160
161     line_flip(lbuf);
162
163     return i;
164 }
165
166 int
167 lcntl_transform_inseq(struct term* tdev)
168 {
169     return lcntl_transform_seq(tdev, &tdev->line_in, false);
170 }
171
172 int
173 lcntl_transform_outseq(struct term* tdev)
174 {
175     return lcntl_transform_seq(tdev, &tdev->line_out, true);
176 }