• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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