1 #include "vterm_internal.h"
2
3 #define UNICODE_INVALID 0xFFFD
4
5 #if defined(DEBUG) && DEBUG > 1
6 # define DEBUG_PRINT_UTF8
7 #endif
8
9 struct UTF8DecoderData {
10 // number of bytes remaining in this codepoint
11 int bytes_remaining;
12
13 // number of bytes total in this codepoint once it's finished
14 // (for detecting overlongs)
15 int bytes_total;
16
17 int this_cp;
18 };
19
init_utf8(VTermEncoding * enc,void * data_)20 static void init_utf8(VTermEncoding *enc, void *data_)
21 {
22 struct UTF8DecoderData *data = data_;
23
24 data->bytes_remaining = 0;
25 data->bytes_total = 0;
26 }
27
decode_utf8(VTermEncoding * enc,void * data_,uint32_t cp[],int * cpi,int cplen,const char bytes[],size_t * pos,size_t bytelen)28 static void decode_utf8(VTermEncoding *enc, void *data_,
29 uint32_t cp[], int *cpi, int cplen,
30 const char bytes[], size_t *pos, size_t bytelen)
31 {
32 struct UTF8DecoderData *data = data_;
33
34 #ifdef DEBUG_PRINT_UTF8
35 printf("BEGIN UTF-8\n");
36 #endif
37
38 for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
39 unsigned char c = bytes[*pos];
40
41 #ifdef DEBUG_PRINT_UTF8
42 printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
43 #endif
44
45 if(c < 0x20) // C0
46 return;
47
48 else if(c >= 0x20 && c < 0x7f) {
49 if(data->bytes_remaining)
50 cp[(*cpi)++] = UNICODE_INVALID;
51
52 cp[(*cpi)++] = c;
53 #ifdef DEBUG_PRINT_UTF8
54 printf(" UTF-8 char: U+%04x\n", c);
55 #endif
56 data->bytes_remaining = 0;
57 }
58
59 else if(c == 0x7f) // DEL
60 return;
61
62 else if(c >= 0x80 && c < 0xc0) {
63 if(!data->bytes_remaining) {
64 cp[(*cpi)++] = UNICODE_INVALID;
65 continue;
66 }
67
68 data->this_cp <<= 6;
69 data->this_cp |= c & 0x3f;
70 data->bytes_remaining--;
71
72 if(!data->bytes_remaining) {
73 #ifdef DEBUG_PRINT_UTF8
74 printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
75 #endif
76 // Check for overlong sequences
77 switch(data->bytes_total) {
78 case 2:
79 if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
80 break;
81 case 3:
82 if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
83 break;
84 case 4:
85 if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
86 break;
87 case 5:
88 if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
89 break;
90 case 6:
91 if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
92 break;
93 }
94 // Now look for plain invalid ones
95 if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
96 data->this_cp == 0xFFFE ||
97 data->this_cp == 0xFFFF)
98 data->this_cp = UNICODE_INVALID;
99 #ifdef DEBUG_PRINT_UTF8
100 printf(" char: U+%04x\n", data->this_cp);
101 #endif
102 cp[(*cpi)++] = data->this_cp;
103 }
104 }
105
106 else if(c >= 0xc0 && c < 0xe0) {
107 if(data->bytes_remaining)
108 cp[(*cpi)++] = UNICODE_INVALID;
109
110 data->this_cp = c & 0x1f;
111 data->bytes_total = 2;
112 data->bytes_remaining = 1;
113 }
114
115 else if(c >= 0xe0 && c < 0xf0) {
116 if(data->bytes_remaining)
117 cp[(*cpi)++] = UNICODE_INVALID;
118
119 data->this_cp = c & 0x0f;
120 data->bytes_total = 3;
121 data->bytes_remaining = 2;
122 }
123
124 else if(c >= 0xf0 && c < 0xf8) {
125 if(data->bytes_remaining)
126 cp[(*cpi)++] = UNICODE_INVALID;
127
128 data->this_cp = c & 0x07;
129 data->bytes_total = 4;
130 data->bytes_remaining = 3;
131 }
132
133 else if(c >= 0xf8 && c < 0xfc) {
134 if(data->bytes_remaining)
135 cp[(*cpi)++] = UNICODE_INVALID;
136
137 data->this_cp = c & 0x03;
138 data->bytes_total = 5;
139 data->bytes_remaining = 4;
140 }
141
142 else if(c >= 0xfc && c < 0xfe) {
143 if(data->bytes_remaining)
144 cp[(*cpi)++] = UNICODE_INVALID;
145
146 data->this_cp = c & 0x01;
147 data->bytes_total = 6;
148 data->bytes_remaining = 5;
149 }
150
151 else {
152 cp[(*cpi)++] = UNICODE_INVALID;
153 }
154 }
155 }
156
157 static VTermEncoding encoding_utf8 = {
158 .init = &init_utf8,
159 .decode = &decode_utf8,
160 };
161
decode_usascii(VTermEncoding * enc,void * data,uint32_t cp[],int * cpi,int cplen,const char bytes[],size_t * pos,size_t bytelen)162 static void decode_usascii(VTermEncoding *enc, void *data,
163 uint32_t cp[], int *cpi, int cplen,
164 const char bytes[], size_t *pos, size_t bytelen)
165 {
166 int is_gr = bytes[*pos] & 0x80;
167
168 for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
169 unsigned char c = bytes[*pos] ^ is_gr;
170
171 if(c < 0x20 || c == 0x7f || c >= 0x80)
172 return;
173
174 cp[(*cpi)++] = c;
175 }
176 }
177
178 static VTermEncoding encoding_usascii = {
179 .decode = &decode_usascii,
180 };
181
182 struct StaticTableEncoding {
183 const VTermEncoding enc;
184 const uint32_t chars[128];
185 };
186
decode_table(VTermEncoding * enc,void * data,uint32_t cp[],int * cpi,int cplen,const char bytes[],size_t * pos,size_t bytelen)187 static void decode_table(VTermEncoding *enc, void *data,
188 uint32_t cp[], int *cpi, int cplen,
189 const char bytes[], size_t *pos, size_t bytelen)
190 {
191 struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
192 int is_gr = bytes[*pos] & 0x80;
193
194 for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
195 unsigned char c = bytes[*pos] ^ is_gr;
196
197 if(c < 0x20 || c == 0x7f || c >= 0x80)
198 return;
199
200 if(table->chars[c])
201 cp[(*cpi)++] = table->chars[c];
202 else
203 cp[(*cpi)++] = c;
204 }
205 }
206
207 #include "encoding/DECdrawing.inc"
208 #include "encoding/uk.inc"
209
210 static struct {
211 VTermEncodingType type;
212 char designation;
213 VTermEncoding *enc;
214 }
215 encodings[] = {
216 { ENC_UTF8, 'u', &encoding_utf8 },
217 { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
218 { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
219 { ENC_SINGLE_94, 'B', &encoding_usascii },
220 { 0 },
221 };
222
223 /* This ought to be INTERNAL but isn't because it's used by unit testing */
vterm_lookup_encoding(VTermEncodingType type,char designation)224 VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
225 {
226 for(int i = 0; encodings[i].designation; i++)
227 if(encodings[i].type == type && encodings[i].designation == designation)
228 return encodings[i].enc;
229 return NULL;
230 }
231