1 /*
2  * Copyright (C) 2014 haru <uobikiemukot at gmail dot com>
3  * Copyright (C) 2014 Hayaki Saito <user@zuse.jp>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 #include "yaft.h"
21 #include "util.h"
22 #include "terminal.h"
23 #include "function.h"
24 #include "dcs.h"
25 
26 #include <stdio.h>
27 #if HAVE_CTYPE_H
28 # include <ctype.h>
29 #endif
30 #if HAVE_STRING_H
31 # include <string.h>
32 #endif
33 
34 static void (*ctrl_func[CTRL_CHARS])(struct terminal *term) = {
35     [BS]  = bs,
36     [HT]  = tab,
37     [LF]  = nl,
38     [VT]  = nl,
39     [FF]  = nl,
40     [CR]  = cr,
41     [ESC] = enter_esc,
42 };
43 
44 static void (*esc_func[ESC_CHARS])(struct terminal *term) = {
45     ['7'] = save_state,
46     ['8'] = restore_state,
47     ['D'] = nl,
48     ['E'] = crnl,
49     ['H'] = set_tabstop,
50     ['M'] = reverse_nl,
51     ['P'] = enter_dcs,
52     ['['] = enter_csi,
53     [']'] = enter_osc,
54     ['c'] = ris,
55 };
56 
57 static void (*csi_func[ESC_CHARS])(struct terminal *term, struct parm_t *) = {
58     ['@'] = insert_blank,
59     ['A'] = curs_up,
60     ['B'] = curs_down,
61     ['C'] = curs_forward,
62     ['D'] = curs_back,
63     ['E'] = curs_nl,
64     ['F'] = curs_pl,
65     ['G'] = curs_col,
66     ['H'] = curs_pos,
67     ['J'] = erase_display,
68     ['K'] = erase_line,
69     ['L'] = insert_line,
70     ['M'] = delete_line,
71     ['P'] = delete_char,
72     ['X'] = erase_char,
73     ['a'] = curs_forward,
74     ['d'] = curs_line,
75     ['e'] = curs_down,
76     ['f'] = curs_pos,
77     ['g'] = clear_tabstop,
78     ['h'] = set_mode,
79     ['l'] = reset_mode,
80     ['m'] = set_attr,
81     ['r'] = set_margin,
82     //['s'] = save_state,
83     //['u'] = restore_state,
84     ['`'] = curs_col,
85 };
86 
87 /* ctr char/esc sequence/charset function */
control_character(struct terminal * term,uint8_t ch)88 static void control_character(struct terminal *term, uint8_t ch)
89 {
90     static const char *ctrl_char[] = {
91         "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
92         "BS ", "HT ", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ",
93         "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
94         "CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US ",
95     };
96 
97     *term->esc.bp = '\0';
98 
99     if (DEBUG)
100         fprintf(stderr, "ctl: %s\n", ctrl_char[ch]);
101 
102     if (ctrl_func[ch])
103         ctrl_func[ch](term);
104 }
105 
esc_sequence(struct terminal * term,uint8_t ch)106 static void esc_sequence(struct terminal *term, uint8_t ch)
107 {
108     *term->esc.bp = '\0';
109 
110     if (DEBUG)
111         fprintf(stderr, "esc: ESC %s\n", term->esc.buf);
112 
113     if (strlen(term->esc.buf) == 1 && esc_func[ch])
114         esc_func[ch](term);
115 
116     /* not reset if csi/osc/dcs seqence */
117     if (ch == '[' || ch == ']' || ch == 'P')
118         return;
119 
120     reset_esc(term);
121 }
122 
csi_sequence(struct terminal * term,uint8_t ch)123 static void csi_sequence(struct terminal *term, uint8_t ch)
124 {
125     struct parm_t parm;
126 
127     *(term->esc.bp - 1) = '\0'; /* omit final character */
128 
129     if (DEBUG)
130         fprintf(stderr, "csi: CSI %s\n", term->esc.buf + 1);
131 
132     reset_parm(&parm);
133     parse_arg(term->esc.buf + 1, &parm, ';', isdigit); /* skip '[' */
134 
135     if (csi_func[ch])
136         csi_func[ch](term, &parm);
137 
138     reset_esc(term);
139 }
140 
is_osc_parm(int c)141 static int is_osc_parm(int c)
142 {
143     if (isdigit(c) || isalpha(c) ||
144         c == '?' || c == ':' || c == '/' || c == '#')
145         return true;
146     else
147         return false;
148 }
149 
omit_string_terminator(char * bp,uint8_t ch)150 static void omit_string_terminator(char *bp, uint8_t ch)
151 {
152     if (ch == BACKSLASH) /* ST: ESC BACKSLASH */
153         *(bp - 2) = '\0';
154     else                 /* ST: BEL */
155         *(bp - 1) = '\0';
156 }
157 
osc_sequence(struct terminal * term,uint8_t ch)158 static void osc_sequence(struct terminal *term, uint8_t ch)
159 {
160     int osc_mode;
161     struct parm_t parm;
162 
163     omit_string_terminator(term->esc.bp, ch);
164 
165     if (DEBUG)
166         fprintf(stderr, "osc: OSC %s\n", term->esc.buf);
167 
168     reset_parm(&parm);
169     parse_arg(term->esc.buf + 1, &parm, ';', is_osc_parm); /* skip ']' */
170 
171     reset_esc(term);
172 }
173 
dcs_sequence(struct terminal * term,uint8_t ch)174 static void dcs_sequence(struct terminal *term, uint8_t ch)
175 {
176     char *cp;
177 
178     omit_string_terminator(term->esc.bp, ch);
179 
180     if (DEBUG)
181         fprintf(stderr, "dcs: DCS %s\n", term->esc.buf);
182 
183     /* check DCS header */
184     cp = term->esc.buf + 1; /* skip P */
185     while (cp < term->esc.bp) {
186         if (*cp == '{' || *cp == 'q')      /* DECDLD or sixel */
187             break;
188         else if (*cp == ';'                /* valid DCS header */
189             || ('0' <= *cp && *cp <= '9'))
190             ;
191         else                               /* invalid sequence */
192             cp = term->esc.bp;
193         cp++;
194     }
195 
196     if (cp != term->esc.bp) { /* header only or cannot find final char */
197         /* parse DCS header */
198         if (*cp == 'q')
199             sixel_parse_header(term, term->esc.buf + 1);
200         else if (*cp == '{')
201             decdld_parse_header(term, term->esc.buf + 1);
202     }
203 
204     reset_esc(term);
205 }
206 
utf8_charset(struct terminal * term,uint8_t ch)207 static void utf8_charset(struct terminal *term, uint8_t ch)
208 {
209     if (0x80 <= ch && ch <= 0xBF) {
210         /* check illegal UTF-8 sequence
211             * ? byte sequence: first byte must be between 0xC2 ~ 0xFD
212             * 2 byte sequence: first byte must be between 0xC2 ~ 0xDF
213              * 3 byte sequence: second byte following 0xE0 must be between 0xA0 ~ 0xBF
214              * 4 byte sequence: second byte following 0xF0 must be between 0x90 ~ 0xBF
215              * 5 byte sequence: second byte following 0xF8 must be between 0x88 ~ 0xBF
216              * 6 byte sequence: second byte following 0xFC must be between 0x84 ~ 0xBF
217         */
218         if ((term->charset.following_byte == 0)
219             || (term->charset.following_byte == 1 && term->charset.count == 0 && term->charset.code <= 1)
220             || (term->charset.following_byte == 2 && term->charset.count == 0 && term->charset.code == 0 && ch < 0xA0)
221             || (term->charset.following_byte == 3 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x90)
222             || (term->charset.following_byte == 4 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x88)
223             || (term->charset.following_byte == 5 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x84))
224             term->charset.is_valid = false;
225 
226         term->charset.code <<= 6;
227         term->charset.code += ch & 0x3F;
228         term->charset.count++;
229     }
230     else if (0xC0 <= ch && ch <= 0xDF) {
231         term->charset.code = ch & 0x1F;
232         term->charset.following_byte = 1;
233         term->charset.count = 0;
234         return;
235     }
236     else if (0xE0 <= ch && ch <= 0xEF) {
237         term->charset.code = ch & 0x0F;
238         term->charset.following_byte = 2;
239         term->charset.count = 0;
240         return;
241     }
242     else if (0xF0 <= ch && ch <= 0xF7) {
243         term->charset.code = ch & 0x07;
244         term->charset.following_byte = 3;
245         term->charset.count = 0;
246         return;
247     }
248     else if (0xF8 <= ch && ch <= 0xFB) {
249         term->charset.code = ch & 0x03;
250         term->charset.following_byte = 4;
251         term->charset.count = 0;
252         return;
253     }
254     else if (0xFC <= ch && ch <= 0xFD) {
255         term->charset.code = ch & 0x01;
256         term->charset.following_byte = 5;
257         term->charset.count = 0;
258         return;
259     }
260     else { /* 0xFE - 0xFF: not used in UTF-8 */
261         addch(term, REPLACEMENT_CHAR);
262         reset_charset(term);
263         return;
264     }
265 
266     if (term->charset.count >= term->charset.following_byte) {
267         /*    illegal code point (ref: http://www.unicode.org/reports/tr27/tr27-4.html)
268             0xD800   ~ 0xDFFF : surrogate pair
269             0xFDD0   ~ 0xFDEF : noncharacter
270             0xnFFFE  ~ 0xnFFFF: noncharacter (n: 0x00 ~ 0x10)
271             0x110000 ~        : invalid (unicode U+0000 ~ U+10FFFF)
272         */
273         if (!term->charset.is_valid
274             || (0xD800 <= term->charset.code && term->charset.code <= 0xDFFF)
275             || (0xFDD0 <= term->charset.code && term->charset.code <= 0xFDEF)
276             || ((term->charset.code & 0xFFFF) == 0xFFFE || (term->charset.code & 0xFFFF) == 0xFFFF)
277             || (term->charset.code > 0x10FFFF))
278             addch(term, REPLACEMENT_CHAR);
279         else
280             addch(term, term->charset.code);
281 
282         reset_charset(term);
283     }
284 }
285 
parse(struct terminal * term,uint8_t * buf,int size,int * pdirty)286 void parse(struct terminal *term, uint8_t *buf, int size, int *pdirty)
287 {
288     /*
289         CTRL CHARS      : 0x00 ~ 0x1F
290         ASCII(printable): 0x20 ~ 0x7E
291         CTRL CHARS(DEL) : 0x7F
292         UTF-8           : 0x80 ~ 0xFF
293     */
294     uint8_t ch;
295     int i;
296 
297     for (i = 0; i < size; i++) {
298         ch = buf[i];
299         if (term->esc.state == STATE_RESET) {
300             /* interrupted by illegal byte */
301             if (term->charset.following_byte > 0 && (ch < 0x80 || ch > 0xBF)) {
302                 addch(term, REPLACEMENT_CHAR);
303                 reset_charset(term);
304             }
305 
306             if (ch <= 0x1F)
307                 control_character(term, ch);
308             else if (ch <= 0x7F)
309                 addch(term, ch);
310             else
311                 utf8_charset(term, ch);
312         }
313         else if (term->esc.state == STATE_ESC) {
314             if (push_esc(term, ch))
315                 esc_sequence(term, ch);
316         }
317         else if (term->esc.state == STATE_CSI) {
318             if (push_esc(term, ch))
319                 csi_sequence(term, ch);
320         }
321         else if (term->esc.state == STATE_OSC) {
322             if (push_esc(term, ch))
323                 osc_sequence(term, ch);
324         }
325         else if (term->esc.state == STATE_DCS) {
326             if (push_esc(term, ch)) {
327                 dcs_sequence(term, ch);
328                 *pdirty = 1;
329             }
330         }
331     }
332 }
333 
334 /* emacs, -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
335 /* vim: set expandtab ts=4 : */
336 /* EOF */
337