eacf0af067f0d951893826fe1e11d87d7fe53807
[openvswitch] / extras / ezio / terminal.c
1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
2  *
3  * This program is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation, either version 3 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * In addition, as a special exception, Nicira Networks gives permission
17  * to link the code of its release of vswitchd with the OpenSSL project's
18  * "OpenSSL" library (or with modified versions of it that use the same
19  * license as the "OpenSSL" library), and distribute the linked
20  * executables.  You must obey the GNU General Public License in all
21  * respects for all of the code used other than "OpenSSL".  If you modify
22  * this file, you may extend this exception to your version of the file,
23  * but you are not obligated to do so.  If you do not wish to do so,
24  * delete this exception statement from your version.
25  *
26  */
27
28 #include <config.h>
29 #include "terminal.h"
30 #include <assert.h>
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include "dynamic-string.h"
38 #include "ezio.h"
39 #include "poll-loop.h"
40 #include "util.h"
41
42 #define THIS_MODULE VLM_terminal
43 #include "vlog.h"
44
45 /* UTF-8 decoding. */
46 static struct utf8_reader *utf8_reader_create(void);
47 static void utf8_reader_destroy(struct utf8_reader *);
48 static int utf8_reader_read(struct utf8_reader *, uint8_t c);
49
50 /* ANSI escape sequence decoding. */
51 struct ansi_sequence {
52     int n_args;
53 #define ANSI_MAX_ARGS 16
54     int args[ANSI_MAX_ARGS];
55     int function;
56 };
57
58 static struct ansi_decoder *ansi_decoder_create(void);
59 static void ansi_decoder_destroy(struct ansi_decoder *);
60 static int ansi_decoder_put(struct ansi_decoder *, uint8_t c);
61 static const struct ansi_sequence *ansi_decoder_get(struct ansi_decoder *);
62 \f
63 /* Terminal emulation. */
64 struct terminal {
65     struct ansi_decoder *ansi;
66     struct utf8_reader *utf8;
67     enum { EZIO, UTF8 } encoding;
68 };
69
70 static void recv_byte(struct terminal *term, struct ezio *ezio, uint8_t c);
71
72 struct terminal *
73 terminal_create(void)
74 {
75     struct terminal *term = xmalloc(sizeof *term);
76     term->ansi = ansi_decoder_create();
77     term->utf8 = utf8_reader_create();
78     term->encoding = UTF8;
79     return term;
80 }
81
82 void
83 terminal_destroy(struct terminal *term)
84 {
85     if (term) {
86         utf8_reader_destroy(term->utf8);
87         ansi_decoder_destroy(term->ansi);
88         free(term);
89     }
90 }
91
92 int
93 terminal_run(struct terminal *term, struct ezio *ezio, int input_fd)
94 {
95     char input[512];
96     int n;
97
98     n = read(input_fd, input, sizeof input);
99     if (n > 0) {
100         int i;
101
102         for (i = 0; i < n; i++) {
103             recv_byte(term, ezio, input[i]);
104         }
105         return 0;
106     } else {
107         return !n ? EOF : errno == EAGAIN ? 0 : errno;
108     }
109 }
110
111 void
112 terminal_wait(struct terminal *term UNUSED, int input_fd)
113 {
114     poll_fd_wait(input_fd, POLLIN);
115 }
116 \f
117 static void recv_ansi_sequence(const struct ansi_sequence *, struct ezio *);
118 static void recv_control(uint8_t c, struct ezio *);
119 static void recv_character(uint8_t byte, struct ezio *);
120 static int unicode_to_ezio(uint16_t unicode);
121 static int default_arg(int value, int default_value);
122 static int range(int value, int min, int max);
123 static void clear_elements(uint8_t *p, size_t size, int pos, int clear_type);
124 static void define_icon(struct ezio *e, const int *args);
125 static void clear_icon(struct ezio *e, int icon_nr);
126 static void set_cursor(struct ezio *e, int visibility);
127
128 static void
129 recv_byte(struct terminal *term, struct ezio *ezio, uint8_t c)
130 {
131     int retval;
132
133     /* Decode and interpret ANSI escape sequences. */
134     retval = ansi_decoder_put(term->ansi, c);
135     if (retval <= 0) {
136         if (retval < 0) {
137             recv_ansi_sequence(ansi_decoder_get(term->ansi), ezio);
138             return;
139         }
140         return;
141     }
142
143     /* Encoding selection. */
144     if (c == 0x0e) {
145         /* Shift Out. */
146         term->encoding = EZIO;
147         return;
148     } else if (c == 0x0f) {
149         /* Shift In. */
150         term->encoding = UTF8;
151         return;
152     }
153
154     if (term->encoding == UTF8) {
155         int unicode, ezchar;
156
157         /* Convert UTF-8 input to Unicode code point. */
158         unicode = utf8_reader_read(term->utf8, c);
159         if (unicode < 0) {
160             return;
161         }
162
163         /* Convert Unicode code point to EZIO encoding. */
164         ezchar = unicode_to_ezio(unicode);
165         if (ezchar >= 0) {
166             if (ezchar & 0xff00) {
167                 recv_character(ezchar >> 8, ezio);
168             }
169             recv_character(ezchar, ezio);
170         } else if (unicode < 0x100) {
171             recv_control(unicode, ezio);
172         } else {
173             /* Unsupported Unicode code point. */
174             return;
175         }
176     } else {
177         if (c >= 0x80 && c < 0x87) {
178             c &= 0x07;
179         }
180         if (c != 0xfe) {
181             recv_character(c, ezio);
182         }
183     }
184 }
185
186 static void
187 log_ansi_sequence(const struct ansi_sequence *seq, struct ezio *e)
188 {
189     struct sequence {
190         int function;
191         const char *name;
192     };
193     static const struct sequence sequences[] = {
194         {0x5a, "CBT: Cursor Backward Tabulation"},
195         {0x47, "CHA: Cursor Character Absolute"},
196         {0x49, "CHT: Cursor Forward Tabulation"},
197         {0x45, "CNL: Cursor Next Line"},
198         {0x46, "CPL: Cursor Preceding Line"},
199         {0x44, "CUB: Cursor Left"},
200         {0x42, "CUD: Cursor Down"},
201         {0x43, "CUF: Cursor Right"},
202         {0x48, "CUP: Cursor Position"},
203         {0x41, "CUU: Cursor Up"},
204         {0x50, "DCH: Delete Character"},
205         {0x4d, "DL: Delete Line"},
206         {0x58, "ECH: Erase Character"},
207         {0x4a, "ED: Erase in Page"},
208         {0x4b, "EL: Erase in Line"},
209         {0x40, "ICH: Insert Character"},
210         {0x4c, "IL: Insert Line"},
211         {0x4500, "NEL: Next Line"},
212         {0x4d00, "RI: Reverse Line Feed"},
213         {0x6300, "RIS: Reset to Initial State"},
214         {0x54, "SD: Scroll Down"},
215         {0x240, "SL: Scroll Left"},
216         {0x241, "SR: Scroll Right"},
217         {0x53, "SU: Scroll Up"},
218         {0x70, "DICO: Define Icon"},
219         {0x71, "CICO: Clear Icon"},
220         {0x72, "Set cursor visibility"},
221     };
222     const struct sequence *s;
223     struct ds ds;
224     int i;
225
226     ds_init(&ds);
227     for (s = sequences; s < &sequences[ARRAY_SIZE(sequences)]; s++) {
228         if (s->function == seq->function) {
229             ds_put_cstr(&ds, s->name);
230             goto found;
231         }
232     }
233     ds_put_format(&ds, "0x%02x", s->function);
234     if (s->function < 0x100) {
235         ds_put_format(&ds, "(%02d/%02d)", s->function / 16, s->function % 16);
236     }
237
238 found:
239     for (i = 0; i < seq->n_args; i++) {
240         ds_put_format(&ds, ", %d", seq->args[i]);
241     }
242     VLOG_DBG("%s (cursor:%d,%d)", ds_cstr(&ds), e->x, e->y);
243     ds_destroy(&ds);
244 }
245
246 static void
247 recv_ansi_sequence(const struct ansi_sequence *seq, struct ezio *e)
248 {
249 #define ARG1(DEFAULT) default_arg(seq->args[0], DEFAULT)
250 #define ARG2(DEFAULT) default_arg(seq->args[1], DEFAULT)
251     if (VLOG_IS_DBG_ENABLED()) {
252         log_ansi_sequence(seq, e);
253     }
254     switch (seq->function) {
255     case 0x5a: /* CBT: Cursor Backward Tabulation. */
256         e->x = 8 * (e->x / 8 - ARG1(1));
257         break;
258     case 0x47: /* CHA: Cursor Character Absolute. */
259         e->x = ARG1(1) - 1;
260         break;
261     case 0x49: /* CHT: Cursor Forward Tabulation. */
262         e->x = 8 * (e->x / 8 + ARG1(1));
263         break;
264     case 0x45: /* CNL: Cursor Next Line. */
265         e->x = 0;
266         e->y += ARG1(1);
267         break;
268     case 0x46: /* CPL: Cursor Preceding Line. */
269         e->x = 0;
270         e->y -= ARG1(1);
271         break;
272     case 0x44: /* CUB: Cursor Left. */
273         e->x -= ARG1(1);
274         break;
275     case 0x42: /* CUD: Cursor Down. */
276         e->y += ARG1(1);
277         break;
278     case 0x43: /* CUF: Cursor Right. */
279         e->x += ARG1(1);
280         break;
281     case 0x48: /* CUP: Cursor Position. */
282         e->y = ARG1(1) - 1;
283         e->x = ARG2(1) - 1;
284         break;
285     case 0x41: /* CUU: Cursor Up. */
286         e->y -= ARG1(1);
287         break;
288     case 0x50: /* DCH: Delete Character. */
289         ezio_delete_char(e, e->x, e->y, ARG1(1));
290         break;
291     case 0x4d: /* DL: Delete Line. */
292         ezio_delete_line(e, e->y, ARG1(1));
293         break;
294     case 0x58: /* ECH: Erase Character. */
295         memset(&e->chars[e->y][e->x], ' ', MIN(ARG1(1), 40 - e->x));
296         break;
297     case 0x4a: /* ED: Erase in Page. */
298         clear_elements(&e->chars[0][0], 2 * 40, e->x + 40 * e->y, ARG1(0));
299         break;
300     case 0x4b: /* EL: Erase in Line. */
301         clear_elements(&e->chars[e->y][0], 40, e->x, ARG1(0));
302         break;
303     case 0x40: /* ICH: Insert Character. */
304         ezio_insert_char(e, e->x, e->y, ARG1(1));
305         break;
306     case 0x4c: /* IL: Insert Line. */
307         ezio_insert_line(e, e->y, ARG1(1));
308         break;
309     case 0x4500: /* NEL: Next Line. */
310         e->x = 0;
311         e->y++;
312         break;
313     case 0x4d00: /* RI: Reverse Line Feed. */
314         e->y--;
315         break;
316     case 0x6300: /* RIS: Reset to Initial State. */
317         ezio_init(e);
318         break;
319     case 0x54: /* SD: Scroll Down. */
320         ezio_scroll_down(e, ARG1(1));
321         break;
322     case 0x240: /* SL: Scroll Left. */
323         ezio_scroll_left(e, ARG1(1));
324         break;
325     case 0x241: /* SR: Scroll Right. */
326         ezio_scroll_right(e, ARG1(1));
327         break;
328     case 0x53: /* SU: Scroll Up. */
329         ezio_scroll_up(e, ARG1(1));
330         break;
331
332         /* Private sequences. */
333     case 0x70: /* DICO: Define Icon. */
334         define_icon(e, seq->args);
335         break;
336     case 0x71: /* CICO: Clear Icon. */
337         clear_icon(e, ARG1(0));
338         break;
339     case 0x72: /* Set cursor visibility. */
340         set_cursor(e, ARG1(1));
341         break;
342     }
343     e->x = range(e->x, 0, 40);
344     e->y = range(e->y, 0, 1);
345     VLOG_DBG("cursor:%d,%d", e->x, e->y);
346 }
347
348 static void
349 recv_control(uint8_t c, struct ezio *e)
350 {
351     switch (c) {
352     case '\b':
353         if (e->x > 0) {
354             --e->x;
355         }
356         break;
357
358     case '\t':
359         e->x = ROUND_UP(e->x + 1, 8);
360         if (e->x > 40) {
361             ezio_newline(e);
362         }
363         break;
364
365     case '\n':
366         ezio_line_feed(e);
367         break;
368
369     case '\f':
370         ezio_clear(e);
371         break;
372
373     case '\r':
374         e->x = 0;
375         break;
376
377     default:
378         VLOG_DBG("Unhandled control character 0x%02"PRIx8, c);
379     }
380 }
381
382 static void
383 recv_character(uint8_t byte, struct ezio *e)
384 {
385     if (e->x >= 40) {
386         ezio_newline(e);
387     }
388     ezio_put_char(e, e->x++, e->y, byte);
389 }
390
391 static int
392 default_arg(int value, int default_value)
393 {
394     return value >= 0 ? value : default_value;
395 }
396
397 static int
398 range(int value, int min, int max)
399 {
400     return value < min ? min : value > max ? max : value;
401 }
402
403 static void
404 clear_elements(uint8_t *p, size_t size, int pos, int clear_type)
405 {
406     switch (clear_type) {
407     case 0:
408         /* Clear from 'pos' to end. */
409         memset(p + pos, ' ', size - pos);
410         break;
411     case 1:
412         /* Clear from beginning to 'pos'. */
413         memset(p, ' ', pos + 1);
414         break;
415     case 2:
416         /* Clear all. */
417         memset(p, ' ', size);
418         break;
419     }
420 }
421
422 static void
423 define_icon(struct ezio *e, const int *args)
424 {
425     int icon_nr;
426     int row;
427
428     icon_nr = args[0];
429     if (icon_nr < 0 || icon_nr > 7) {
430         return;
431     }
432
433     for (row = 0; row < 8; row++) {
434         e->icons[icon_nr][row] = default_arg(args[row + 1], 0) & 0x1f;
435     }
436 }
437
438 static void
439 clear_icon(struct ezio *e, int icon_nr)
440 {
441     if (icon_nr >= 0 && icon_nr <= 7) {
442         ezio_set_default_icon(e, icon_nr);
443     }
444 }
445
446 static void
447 set_cursor(struct ezio *e, int visibility)
448 {
449     switch (visibility) {
450     case 1:
451         e->show_cursor = e->blink_cursor = false;
452         break;
453     case 2:
454         e->show_cursor = true;
455         e->blink_cursor = false;
456         break;
457     case 3:
458         e->show_cursor = e->blink_cursor = true;
459         break;
460     }
461 }
462
463 static int
464 unicode_to_ezio(uint16_t unicode)
465 {
466     switch (unicode) {
467         /* Most ASCII characters map one-to-one. */
468     case 0x0020 ... 0x005b:
469     case 0x005d ... 0x007d:
470         return unicode;
471
472         /* A few ASCII characters have to be simulated with icons. */
473     case 0x005c: return 0x06; /* BACKSLASH */
474     case 0x007e: return 0x07; /* TILDE */
475
476         /* EZIO extended characters equivalents in Unicode - Japanese. */
477     case 0x00a5: return '\\';   /* YEN SIGN */
478     case 0x3002: return 0xa1;   /* IDEOGRAPHIC FULL STOP */
479     case 0x300c: return 0xa2;   /* LEFT CORNER BRACKET */
480     case 0x300d: return 0xa3;   /* RIGHT CORNER BRACKET */
481     case 0x3001: return 0xa4;   /* IDEOGRAPHIC COMMA */
482     case 0x30fb: return 0xa5;   /* KATAKANA MIDDLE DOT */
483     case 0x30f2: return 0xa6;   /* KATAKANA LETTER WO */
484     case 0x30a1: return 0xa7;   /* KATAKANA LETTER SMALL A */
485     case 0x30a3: return 0xa8;   /* KATAKANA LETTER SMALL I */
486     case 0x30a5: return 0xa9;   /* KATAKANA LETTER SMALL U */
487     case 0x30a7: return 0xaa;   /* KATAKANA LETTER SMALL E */
488     case 0x30a9: return 0xab;   /* KATAKANA LETTER SMALL O */
489     case 0x30e3: return 0xac;   /* KATAKANA LETTER SMALL YA */
490     case 0x30e5: return 0xad;   /* KATAKANA LETTER SMALL YU */
491     case 0x30e7: return 0xae;   /* KATAKANA LETTER SMALL YO */
492     case 0x30c3: return 0xaf;   /* KATAKANA LETTER SMALL TU = SMALL TSU */
493         case 0x30fc: return 0xb0;   /* KATAKANA-HIRAGANA PROLONGED SOUND MARK */
494     case 0x30a2: return 0xb1;   /* KATAKANA LETTER A */
495     case 0x30a4: return 0xb2;   /* KATAKANA LETTER I */
496     case 0x30a6: return 0xb3;   /* KATAKANA LETTER U */
497     case 0x30a8: return 0xb4;   /* KATAKANA LETTER E */
498     case 0x30aa: return 0xb5;   /* KATAKANA LETTER O */
499     case 0x30ab: return 0xb6;   /* KATAKANA LETTER KA */
500     case 0x30ac: return 0xb6de; /* KATAKANA LETTER GA */
501     case 0x30ad: return 0xb7;   /* KATAKANA LETTER KI */
502     case 0x30ae: return 0xb7de; /* KATAKANA LETTER GI */
503     case 0x30af: return 0xb8;   /* KATAKANA LETTER KU */
504     case 0x30b0: return 0xb8de; /* KATAKANA LETTER GU */
505     case 0x30b1: return 0xb9;   /* KATAKANA LETTER KE */
506     case 0x30b2: return 0xb9de; /* KATAKANA LETTER GE */
507     case 0x30b3: return 0xba;   /* KATAKANA LETTER KO */
508     case 0x30b4: return 0xbade; /* KATAKANA LETTER GO */
509     case 0x30b5: return 0xbb;   /* KATAKANA LETTER SA */
510     case 0x30b6: return 0xbbde; /* KATAKANA LETTER ZA */
511     case 0x30b7: return 0xbc;   /* KATAKANA LETTER SI = SHI */
512     case 0x30b8: return 0xbcde; /* KATAKANA LETTER ZI = JI */
513     case 0x30b9: return 0xbd;   /* KATAKANA LETTER SU */
514     case 0x30ba: return 0xbdde; /* KATAKANA LETTER ZU */
515     case 0x30bb: return 0xbe;   /* KATAKANA LETTER SE */
516     case 0x30bc: return 0xbede; /* KATAKANA LETTER ZE */
517     case 0x30bd: return 0xbf;   /* KATAKANA LETTER SO */
518     case 0x30be: return 0xbfde; /* KATAKANA LETTER ZO */
519     case 0x30bf: return 0xc0;   /* KATAKANA LETTER TA */
520     case 0x30c0: return 0xc0de; /* KATAKANA LETTER DA */
521     case 0x30c1: return 0xc1;   /* KATAKANA LETTER TI = CHI */
522     case 0x30c2: return 0xc1de; /* KATAKANA LETTER DI = JI */
523     case 0x30c4: return 0xc2;   /* KATAKANA LETTER TU = TSU */
524     case 0x30c5: return 0xc2de; /* KATAKANA LETTER DU = ZU */
525     case 0x30c6: return 0xc3;   /* KATAKANA LETTER TE */
526     case 0x30c7: return 0xc3de; /* KATAKANA LETTER DE */
527     case 0x30c8: return 0xc4;   /* KATAKANA LETTER TO */
528     case 0x30c9: return 0xc4de; /* KATAKANA LETTER DO */
529     case 0x30ca: return 0xc5;   /* KATAKANA LETTER NA */
530     case 0x30cb: return 0xc6;   /* KATAKANA LETTER NI */
531     case 0x30cc: return 0xc7;   /* KATAKANA LETTER NU */
532     case 0x30cd: return 0xc8;   /* KATAKANA LETTER NE */
533     case 0x30ce: return 0xc9;   /* KATAKANA LETTER NO */
534     case 0x30cf: return 0xca;   /* KATAKANA LETTER HA */
535     case 0x30d0: return 0xcade; /* KATAKANA LETTER BA */
536     case 0x30d1: return 0xcadf; /* KATAKANA LETTER PA */
537     case 0x30d2: return 0xcb;   /* KATAKANA LETTER HI */
538     case 0x30d3: return 0xcbde; /* KATAKANA LETTER BI */
539     case 0x30d4: return 0xcbdf; /* KATAKANA LETTER PI */
540     case 0x30d5: return 0xcc;   /* KATAKANA LETTER HU = FU */
541     case 0x30d6: return 0xccde; /* KATAKANA LETTER BU */
542     case 0x30d7: return 0xccdf; /* KATAKANA LETTER PU */
543     case 0x30d8: return 0xcd;   /* KATAKANA LETTER HE */
544     case 0x30d9: return 0xcdde; /* KATAKANA LETTER BE */
545     case 0x30da: return 0xcddf; /* KATAKANA LETTER PE */
546     case 0x30db: return 0xce;   /* KATAKANA LETTER HO */
547     case 0x30dc: return 0xcede; /* KATAKANA LETTER BO */
548     case 0x30dd: return 0xcedf; /* KATAKANA LETTER PO */
549     case 0x30de: return 0xcf;   /* KATAKANA LETTER MA */
550     case 0x30df: return 0xd0;   /* KATAKANA LETTER MI */
551     case 0x30e0: return 0xd1;   /* KATAKANA LETTER MU */
552     case 0x30e1: return 0xd2;   /* KATAKANA LETTER ME */
553     case 0x30e2: return 0xd3;   /* KATAKANA LETTER MO */
554     case 0x30e4: return 0xd4;   /* KATAKANA LETTER YA */
555     case 0x30e6: return 0xd5;   /* KATAKANA LETTER YU */
556     case 0x30e8: return 0xd6;   /* KATAKANA LETTER YO */
557     case 0x30e9: return 0xd7;   /* KATAKANA LETTER RA */
558     case 0x30ea: return 0xd8;   /* KATAKANA LETTER RI */
559     case 0x30eb: return 0xd9;   /* KATAKANA LETTER RU */
560     case 0x30ec: return 0xda;   /* KATAKANA LETTER RE */
561     case 0x30ed: return 0xdb;   /* KATAKANA LETTER RO */
562     case 0x30ef: return 0xdc;   /* KATAKANA LETTER WA */
563     case 0x30f3: return 0xdd;   /* KATAKANA LETTER N */
564     case 0x30f4: return 0xb3de; /* KATAKANA LETTER VU */
565     case 0x30f7: return 0xdcde; /* KATAKANA LETTER VA */
566     case 0x3099: return 0xde;   /* COMBINING KATAKANA-HIRAGANA VOICED SOUND
567                                  * MARK */
568     case 0x309a: return 0xdf;   /* COMBINING KATAKANA-HIRAGANA SEMI-VOICED
569                                  * SOUND MARK */
570     case 0x309b: return 0xde;   /* KATAKANA-HIRAGANA VOICED SOUND MARK */
571         case 0x309c: return 0xdf;   /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
572
573         /* EZIO extended characters equivalents in Unicode - other. */
574     case 0x2192: return 0x7e; /* RIGHTWARDS ARROW */
575     case 0x2190: return 0x7f; /* LEFTWARDS ARROW */
576     case 0x03b1: return 0xe0; /* GREEK SMALL LETTER ALPHA */
577     case 0x00e4: return 0xe1; /* LATIN SMALL LETTER A WITH DIAERESIS */
578     case 0x03b2: return 0xe2; /* GREEK SMALL LETTER BETA */
579     case 0x03b5: return 0xe3; /* GREEK SMALL LETTER EPSILON */
580     case 0x03bc: return 0xe4; /* GREEK SMALL LETTER MU */
581     case 0x03c6: return 0xe5; /* GREEK SMALL LETTER PHI */
582     case 0x03c1: return 0xe6; /* GREEK SMALL LETTER RHO */
583                               /* 0xe7 is 'g'. */
584     case 0x221a: return 0xe8; /* SQUARE ROOT = radical sign */
585                               /* 0xe9 is an unrecognizable symbol. */
586                               /* 0xea is 'j'. */
587                               /* 0xeb is an unrecognizable symbol.*/
588     case 0x00a2: return 0xec; /* CENT SIGN */
589     case 0x00a3: return 0xed; /* POUND SIGN */
590     case 0x00f1: return 0xee; /* LATIN SMALL LETTER N WITH TILDE */
591     case 0x00f6: return 0xef; /* LATIN SMALL LETTER O WITH DIAERESIS */
592                               /* 0xf0 is 'p'. */
593                               /* 0xf1 is 'q'. */
594     case 0x03b8: return 0xf2; /* GREEK SMALL LETTER THETA */
595     case 0x221e: return 0xf3; /* INFINITY */
596     case 0x03a9: return 0xf4; /* GREEK CAPITAL LETTER OMEGA */
597     case 0x00fc: return 0xf5; /* LATIN SMALL LETTER U WITH DIAERESIS */
598     case 0x03a3: return 0xf6; /* GREEK CAPITAL LETTER SIGMA */
599     case 0x03c0: return 0xf7; /* GREEK SMALL LETTER PI */
600                               /* 0xf8 is x-macron (the sample mean). */
601                               /* 0xf9 is 'y'. */
602     case 0x5343: return 0xfa; /* thousand */
603     case 0x4e07: return 0xfb; /* ten thousand */
604     case 0x5186: return 0xfc; /* yen */
605     case 0x00f7: return 0xfd; /* DIVISION SIGN */
606     case 0x2588: return 0xff; /* FULL BLOCK = solid */
607
608         /* EZIO icons (from the Unicode Private Use corporate subarea). */
609     case 0xf8f8: return 0x00;
610     case 0xf8f9: return 0x01;
611     case 0xf8fa: return 0x02;
612     case 0xf8fb: return 0x03;
613     case 0xf8fc: return 0x04;
614     case 0xf8fd: return 0x05;
615     case 0xf8fe: return 0x06;
616     case 0xf8ff: return 0x07;
617
618         /* No mappings for anything else. */
619     default: return -1;
620     }
621 }
622 \f
623 /* UTF-8 decoder. */
624
625 #define UTF_STATES                              \
626     UTF_STATE(UTF8_INIT, 0x00, 0xf4, UTF8_INIT) \
627     UTF_STATE(UTF8_3,    0x80, 0xbf, UTF8_2)    \
628     UTF_STATE(UTF8_2,    0x80, 0xbf, UTF8_1)    \
629     UTF_STATE(UTF8_1,    0x80, 0xbf, UTF8_INIT) \
630     UTF_STATE(UTF8_E0,   0xa0, 0xbf, UTF8_1)    \
631     UTF_STATE(UTF8_ED,   0x80, 0x9f, UTF8_1)    \
632     UTF_STATE(UTF8_F0,   0x90, 0xbf, UTF8_INIT) \
633     UTF_STATE(UTF8_F4,   0x80, 0x8f, UTF8_INIT)
634
635 enum state {
636 #define UTF_STATE(NAME, MIN, MAX, NEXT) NAME,
637     UTF_STATES
638 #undef UTF_STATE
639 };
640
641 struct state_info {
642     uint8_t min, max;
643     enum state next;
644 };
645
646 static const struct state_info states[] = {
647 #define UTF_STATE(NAME, MIN, MAX, NEXT) {MIN, MAX, NEXT},
648     UTF_STATES
649 #undef UTF_STATE
650 };
651
652 struct utf8_reader {
653     int cp;
654     enum state state;
655 };
656
657 struct utf8_reader *
658 utf8_reader_create(void)
659 {
660     struct utf8_reader *r = xmalloc(sizeof *r);
661     r->state = UTF8_INIT;
662     return r;
663 }
664
665 void
666 utf8_reader_destroy(struct utf8_reader *r)
667 {
668     free(r);
669 }
670
671 int
672 utf8_reader_read(struct utf8_reader *r, uint8_t c)
673 {
674     const struct state_info *s = &states[r->state];
675     if (c >= s->min && c <= s->max) {
676         if (r->state == UTF8_INIT) {
677             if (c < 0x80) {
678                 return c;
679             } else if (c >= 0xc2 && c <= 0xdf) {
680                 r->cp = c & 0x1f;
681                 r->state = UTF8_1;
682                 return -1;
683             } else if (c >= 0xe0 && c <= 0xef) {
684                 r->cp = c & 0x0f;
685                 r->state = c == 0xe0 ? UTF8_E0 : c == 0xed ? UTF8_ED : UTF8_2;
686                 return -1;
687             } else if (c >= 0xf0 && c <= 0xf4) {
688                 r->cp = c & 0x07;
689                 r->state = c == 0xf0 ? UTF8_F0 : c == 0xf4 ? UTF8_F4 : UTF8_3;
690                 return -1;
691             }
692         } else {
693             r->cp = (r->cp << 6) | (c & 0x3f);
694             r->state = s->next;
695             return r->state == UTF8_INIT ? r->cp : -1;
696         }
697     }
698
699     /* Invalid UTF-8 sequence.  Return the Unicode general substitute
700      * REPLACEMENT CHARACTER. */
701     r->state = UTF8_INIT;
702     return 0xfffd;
703 }
704 \f
705 /* ANSI control sequence decoder. */
706
707 /* States are named for what we are looking for in that state. */
708 enum ansi_state {
709     ANSI_ESC,                      /* Looking for ESC. */
710     ANSI_CSI,                      /* Looking for [ (to complete CSI). */
711     ANSI_PARAMETER,                /* Looking for parameter. */
712     ANSI_INTERMEDIATE,             /* Looking for intermediate byte. */
713     ANSI_FINAL,                    /* Looking for final byte. */
714     ANSI_COMPLETE                  /* Got an entire escape sequence. */
715 };
716
717 struct ansi_decoder {
718     enum ansi_state state;
719     struct ansi_sequence seq;
720     int c;
721 };
722
723 struct ansi_decoder *
724 ansi_decoder_create(void)
725 {
726     struct ansi_decoder *d = xmalloc(sizeof *d);
727     d->state = ANSI_ESC;
728     return d;
729 }
730
731 void
732 ansi_decoder_destroy(struct ansi_decoder *d)
733 {
734     free(d);
735 }
736
737 int
738 ansi_decoder_put(struct ansi_decoder *d, uint8_t c)
739 {
740     if (c == 27) {
741         /* Escape always starts a new escape sequence, aborting an incomplete
742          * one if necessary. */
743         if (d->state != ANSI_ESC) {
744             VLOG_DBG("Unexpected escape inside escape sequence");
745         }
746         d->state = ANSI_CSI;
747         return 0;
748     }
749
750     switch (d->state) {
751     case ANSI_ESC:
752         return 1;
753
754     case ANSI_CSI:
755         if (c == '[') {
756             d->state = ANSI_PARAMETER;
757             d->seq.n_args = 0;
758             d->seq.function = 0;
759         } else if (c >= 0x40 && c <= 0x5f) {
760             d->state = ANSI_COMPLETE;
761             d->seq.n_args = 0;
762             d->seq.function = 0;
763             d->seq.function = c << 8;
764             return -1;
765         } else {
766             d->state = ANSI_ESC;
767         }
768         break;
769
770     case ANSI_PARAMETER:
771         if (c >= '0' && c <= '9') {
772             int *arg;
773             if (d->seq.n_args == 0) {
774                 d->seq.args[d->seq.n_args++] = 0;
775             } else if (d->seq.n_args > ANSI_MAX_ARGS) {
776                 break;
777             }
778             arg = &d->seq.args[d->seq.n_args - 1];
779             if (*arg == -1) {
780                 *arg = 0;
781             }
782             *arg = *arg * 10 + (c - '0');
783             break;
784         } else if (c == ';') {
785             if (d->seq.n_args < ANSI_MAX_ARGS) {
786                 d->seq.args[d->seq.n_args] = -1;
787             }
788             d->seq.n_args++;
789             break;
790         }
791         d->state = ANSI_INTERMEDIATE;
792         /* Fall through. */
793
794     case ANSI_INTERMEDIATE:
795         if (c >= 0x20 && c <= 0x2f) {
796             d->seq.function = d->seq.function * 16 + (c - 0x20);
797             break;
798         }
799         d->state = ANSI_FINAL;
800         /* Fall through. */
801
802     case ANSI_FINAL:
803         if (c >= 0x40 && c <= 0x7e) {
804             d->seq.function = d->seq.function * 256 + c;
805             d->state = ANSI_COMPLETE;
806             return -1;
807         } else {
808             /* Invalid sequence. */
809             d->state = ANSI_ESC;
810         }
811         break;
812
813     case ANSI_COMPLETE:
814         NOT_REACHED();
815     }
816     return 0;
817 }
818
819 const struct ansi_sequence *
820 ansi_decoder_get(struct ansi_decoder *d)
821 {
822     assert(d->state == ANSI_COMPLETE);
823     d->state = ANSI_ESC;
824     if (d->seq.n_args < ANSI_MAX_ARGS) {
825         int i;
826         for (i = d->seq.n_args; i < ANSI_MAX_ARGS; i++) {
827             d->seq.args[i] = -1;
828         }
829     } else {
830         d->seq.n_args = ANSI_MAX_ARGS;
831     }
832     return &d->seq;
833 }