vte: new state machine implementation
authorDavid Herrmann <dh.herrmann@googlemail.com>
Wed, 29 Feb 2012 14:25:53 +0000 (15:25 +0100)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Wed, 29 Feb 2012 14:25:53 +0000 (15:25 +0100)
This is now a fully vt500-series compliant state machine that parses escape
sequences. See vt100.net/emu for information on this state-machine. This is
written from scratch, though.

It now handles all kind of escape sequences that we every want to support. It
correctly ignores all unsupported ones right now.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
src/vte.c

index 2da49b3..476ddb9 100644 (file)
--- a/src/vte.c
+++ b/src/vte.c
 
 /*
  * Virtual Terminal Emulator
- * This is a vt100 implementation. It is written from scratch. It uses the
- * console subsystem as output and is tightly bound to it.
+ * This is the VT implementation. It is written from scratch. It uses the
+ * console subsystem as output and is tightly bound to it. It supports
+ * functionality from vt100 up to vt500 series. It doesn't implement an
+ * explicitly selected terminal but tries to support the most important commands
+ * to be compatible with existing implementations.
  */
 
 #include <errno.h>
 
 /* Input parser states */
 enum parser_state {
-       STATE_NORMAL,           /* normal mode */
-       STATE_ESC,              /* starting escape sequence */
-       STATE_CSI,              /* Control Sequence Introducer */
-       STATE_OTHER,            /* other known but unimpl escp seqs */
+       STATE_NONE,             /* placeholder */
+       STATE_GROUND,           /* initial state and ground */
+       STATE_ESC,              /* ESC sequence was started */
+       STATE_ESC_INT,          /* intermediate escape characters */
+       STATE_CSI_ENTRY,        /* starting CSI sequence */
+       STATE_CSI_PARAM,        /* CSI parameters */
+       STATE_CSI_INT,          /* intermediate CSI characters */
+       STATE_CSI_IGNORE,       /* CSI error; ignore this CSI sequence */
+       STATE_DCS_ENTRY,        /* starting DCS sequence */
+       STATE_DCS_PARAM,        /* DCS parameters */
+       STATE_DCS_INT,          /* intermediate DCS characters */
+       STATE_DCS_PASS,         /* DCS data passthrough */
+       STATE_DCS_IGNORE,       /* DCS error; ignore this DCS sequence */
+       STATE_OSC_STRING,       /* parsing OCS sequence */
+       STATE_ST_IGNORE,        /* unimplemented seq; ignore until ST */
+       STATE_NUM
 };
 
-/* maximum CSI parameters */
-#define CSI_ARG_MAX 15
+/* Input parser actions */
+enum parser_action {
+       ACTION_NONE,            /* placeholder */
+       ACTION_IGNORE,          /* ignore the character entirely */
+       ACTION_PRINT,           /* print the character on the console */
+       ACTION_EXECUTE,         /* execute single control character (C0/C1) */
+       ACTION_CLEAR,           /* clear current parameter state */
+       ACTION_COLLECT,         /* collect intermediate character */
+       ACTION_PARAM,           /* collect parameter character */
+       ACTION_ESC_DISPATCH,    /* dispatch escape sequence */
+       ACTION_CSI_DISPATCH,    /* dispatch csi sequence */
+       ACTION_DCS_START,       /* start of DCS data */
+       ACTION_DCS_COLLECT,     /* collect DCS data */
+       ACTION_DCS_END,         /* end of DCS data */
+       ACTION_OSC_START,       /* start of OSC data */
+       ACTION_OSC_COLLECT,     /* collect OSC data */
+       ACTION_OSC_END,         /* end of OSC data */
+       ACTION_NUM
+};
 
-/* CSI flags */
-#define CSI_START      0x01 /* no arg has been parsed yet */
-#define CSI_QUESTION   0x02 /* ? flag */
-#define CSI_BANG       0x04 /* ! flag */
+/* max CSI arguments */
+#define CSI_ARG_MAX 16
 
 struct kmscon_vte {
        unsigned long ref;
@@ -65,12 +95,9 @@ struct kmscon_vte {
        const char *kbd_sym;
        struct kmscon_utf8_mach *mach;
 
-       struct {
-               unsigned int state;
-               unsigned int csi_flags;
-               unsigned int csi_argc;
-               unsigned int csi_argv[CSI_ARG_MAX];
-       } parser;
+       unsigned int state;
+       unsigned int csi_argc;
+       int csi_argv[CSI_ARG_MAX];
 };
 
 int kmscon_vte_new(struct kmscon_vte **out, struct kmscon_symbol_table *st)
@@ -90,6 +117,7 @@ int kmscon_vte_new(struct kmscon_vte **out, struct kmscon_symbol_table *st)
        memset(vte, 0, sizeof(*vte));
        vte->ref = 1;
        vte->st = st;
+       vte->state = STATE_GROUND;
 
        ret = kmscon_utf8_mach_new(&vte->mach);
        if (ret)
@@ -138,7 +166,8 @@ void kmscon_vte_bind(struct kmscon_vte *vte, struct kmscon_console *con)
        kmscon_console_ref(vte->con);
 }
 
-static void parse_control(struct kmscon_vte *vte, uint32_t ctrl)
+/* execute control character (C0 or C1) */
+static void do_execute(struct kmscon_vte *vte, uint32_t ctrl)
 {
        switch (ctrl) {
                case 0x00: /* NUL */
@@ -188,157 +217,526 @@ static void parse_control(struct kmscon_vte *vte, uint32_t ctrl)
                        break;
                case 0x1b: /* ESC */
                        /* Invokes an escape sequence */
-                       vte->parser.state = STATE_ESC;
-                       break;
-               case 0x7f: /* DEL */
-                       /* Ignored on input */
                        break;
        }
 }
 
-static void parse_csi(struct kmscon_vte *vte, uint32_t val)
+static void do_clear(struct kmscon_vte *vte)
+{
+       int i;
+
+       vte->csi_argc = 0;
+       for (i = 0; i < CSI_ARG_MAX; ++i)
+               vte->csi_argv[i] = -1;
+}
+
+static void do_param(struct kmscon_vte *vte, uint32_t data)
 {
        int new;
-       unsigned int num;
-
-       if (vte->parser.csi_flags & CSI_START) {
-               switch (val) {
-                       case '?':
-                               vte->parser.csi_flags |= CSI_QUESTION;
-                               return;
-                       case '!':
-                               vte->parser.csi_flags |= CSI_BANG;
-                               return;
-                       default:
-                               vte->parser.csi_flags &= ~CSI_START;
-               }
-       }
 
-       if (val == ';') {
-               if (vte->parser.csi_argc < CSI_ARG_MAX)
-                       vte->parser.csi_argc++;
+       if (data == ';') {
+               if (vte->csi_argc < CSI_ARG_MAX)
+                       vte->csi_argc++;
                return;
        }
 
-       if (val >= '0' && val <= '9') {
-               if (vte->parser.csi_argc >= CSI_ARG_MAX)
-                       return;
-
-               new = vte->parser.csi_argv[vte->parser.csi_argc];
-               new *= 10;
-               new += val - '0';
-
-               /* avoid integer overflows */
-               if (new > vte->parser.csi_argv[vte->parser.csi_argc])
-                       vte->parser.csi_argv[vte->parser.csi_argc] = new;
+       if (vte->csi_argc >= CSI_ARG_MAX)
+               return;
 
+       /* avoid integer overflows; max allowed value is 16384 anyway */
+       if (vte->csi_argv[vte->csi_argc] > 0xffff)
                return;
+
+       if (data >= '0' && data <= '9') {
+               new = vte->csi_argv[vte->csi_argc];
+               if (new <= 0)
+                       new = data - '0';
+               else
+                       new = new * 10 + data - '0';
+               vte->csi_argv[vte->csi_argc] = new;
        }
+}
 
-       vte->parser.csi_argc++;
-       vte->parser.state = STATE_NORMAL;
+static void do_csi(struct kmscon_vte *vte, uint32_t data)
+{
+       int num;
+
+       if (vte->csi_argc < CSI_ARG_MAX)
+               vte->csi_argc++;
 
-       switch (val) {
+       switch (data) {
                case 'A':
-                       num = vte->parser.csi_argv[0];
-                       if (!num)
+                       num = vte->csi_argv[0];
+                       if (num <= 0)
                                num = 1;
                        kmscon_console_move_up(vte->con, num, false);
                        break;
                case 'B':
-                       num = vte->parser.csi_argv[0];
-                       if (!num)
+                       num = vte->csi_argv[0];
+                       if (num <= 0)
                                num = 1;
                        kmscon_console_move_down(vte->con, num, false);
                        break;
                case 'C':
-                       num = vte->parser.csi_argv[0];
-                       if (!num)
+                       num = vte->csi_argv[0];
+                       if (num <= 0)
                                num = 1;
                        kmscon_console_move_right(vte->con, num);
                        break;
                case 'D':
-                       num = vte->parser.csi_argv[0];
-                       if (!num)
+                       num = vte->csi_argv[0];
+                       if (num <= 0)
                                num = 1;
                        kmscon_console_move_left(vte->con, num);
                        break;
                case 'J':
-                       if (vte->parser.csi_argc < 1 ||
-                                               vte->parser.csi_argv[0] == 0)
+                       if (vte->csi_argv[0] <= 0)
                                kmscon_console_erase_cursor_to_screen(vte->con);
-                       else if (vte->parser.csi_argv[0] == 1)
+                       else if (vte->csi_argv[0] == 1)
                                kmscon_console_erase_screen_to_cursor(vte->con);
-                       else if (vte->parser.csi_argv[0] == 2)
+                       else if (vte->csi_argv[0] == 2)
                                kmscon_console_erase_screen(vte->con);
                        break;
                case 'K':
-                       if (vte->parser.csi_argc < 1 ||
-                                               vte->parser.csi_argv[0] == 0)
+                       if (vte->csi_argv[0] <= 0)
                                kmscon_console_erase_cursor_to_end(vte->con);
-                       else if (vte->parser.csi_argv[0] == 1)
+                       else if (vte->csi_argv[0] == 1)
                                kmscon_console_erase_home_to_cursor(vte->con);
-                       else if (vte->parser.csi_argv[0] == 2)
+                       else if (vte->csi_argv[0] == 2)
                                kmscon_console_erase_current_line(vte->con);
                        break;
                default:
-                       log_debug("vte: unhandled CSI sequence %c\n", val);
+                       log_debug("vte: unhandled CSI sequence %c\n", data);
        }
 }
 
-static void parse_other(struct kmscon_vte *vte, uint32_t val)
+/* perform parser action */
+static void do_action(struct kmscon_vte *vte, uint32_t data, int action)
 {
-       /* TODO: make this more sophisticated */
-       vte->parser.state = STATE_NORMAL;
-}
+       kmscon_symbol_t sym;
 
-static void parse_esc(struct kmscon_vte *vte, uint32_t val)
-{
-       switch (val) {
-               case '[': /* CSI */
-                       vte->parser.state = STATE_CSI;
-                       vte->parser.csi_flags = CSI_START;
-                       vte->parser.csi_argc = 0;
-                       memset(vte->parser.csi_argv, 0,
-                                               sizeof(vte->parser.csi_argv));
-                       break;
-               case 'P': /* DCS */
-               case ']': /* OCS */
-               case '^': /* PM */
-               case '_': /* APC */
-               case '#': /* special */
-               case '(': /* G0 */
-               case ')': /* G1 */
-               case '%': /* charset */
-               default:
-                       vte->parser.state = STATE_OTHER;
+       switch (action) {
+               case ACTION_NONE:
+                       /* do nothing */
+                       return;
+               case ACTION_IGNORE:
+                       /* ignore character */
+                       break;
+               case ACTION_PRINT:
+                       sym = kmscon_symbol_make(data);
+                       kmscon_console_write(vte->con, sym);
+                       break;
+               case ACTION_EXECUTE:
+                       do_execute(vte, data);
+                       break;
+               case ACTION_CLEAR:
+                       do_clear(vte);
+                       break;
+               case ACTION_COLLECT:
+                       break;
+               case ACTION_PARAM:
+                       do_param(vte, data);
+                       break;
+               case ACTION_ESC_DISPATCH:
                        break;
+               case ACTION_CSI_DISPATCH:
+                       do_csi(vte, data);
+                       break;
+               case ACTION_DCS_START:
+                       break;
+               case ACTION_DCS_COLLECT:
+                       break;
+               case ACTION_DCS_END:
+                       break;
+               case ACTION_OSC_START:
+                       break;
+               case ACTION_OSC_COLLECT:
+                       break;
+               case ACTION_OSC_END:
+                       break;
+               default:
+                       log_warn("vte: invalid action %d\n", action);
        }
 }
 
-static void parse_input(struct kmscon_vte *vte, uint32_t val)
+/* entry actions to be performed when entering the selected state */
+static int entry_action[] = {
+       [STATE_CSI_ENTRY] = ACTION_CLEAR,
+       [STATE_DCS_ENTRY] = ACTION_CLEAR,
+       [STATE_DCS_PASS] = ACTION_DCS_START,
+       [STATE_ESC] = ACTION_CLEAR,
+       [STATE_OSC_STRING] = ACTION_OSC_START,
+       [STATE_NUM] = ACTION_NONE,
+};
+
+/* exit actions to be performed when leaving the selected state */
+static int exit_action[] = {
+       [STATE_DCS_PASS] = ACTION_DCS_END,
+       [STATE_OSC_STRING] = ACTION_OSC_END,
+       [STATE_NUM] = ACTION_NONE,
+};
+
+/* perform state transision and dispatch related actions */
+static void do_trans(struct kmscon_vte *vte, uint32_t data, int state, int act)
 {
-       kmscon_symbol_t sym;
+       if (state != STATE_NONE) {
+               /* A state transition occurs. Perform exit-action,
+                * transition-action and entry-action. Even when performing a
+                * transition to the same state as the current state we do this.
+                * Use STATE_NONE if this is not the desired behavior.
+                */
+               do_action(vte, data, exit_action[vte->state]);
+               do_action(vte, data, act);
+               do_action(vte, data, entry_action[state]);
+               vte->state = state;
+       } else {
+               do_action(vte, data, act);
+       }
+}
 
-       if (val < 0x20) {
-               parse_control(vte, val);
-               return;
+/*
+ * Escape sequence parser
+ * This parses the new input character \data. It performs state transition and
+ * calls the right callbacks for each action.
+ */
+static void parse_data(struct kmscon_vte *vte, uint32_t raw)
+{
+       /* events that may occur in any state */
+       switch (raw) {
+               case 0x18:
+               case 0x1a:
+               case 0x80 ... 0x8f:
+               case 0x91 ... 0x97:
+               case 0x99:
+               case 0x9a:
+               case 0x9c:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_EXECUTE);
+                       return;
+               case 0x1b:
+                       do_trans(vte, raw, STATE_ESC, ACTION_NONE);
+                       return;
+               case 0x98:
+               case 0x9e:
+               case 0x9f:
+                       do_trans(vte, raw, STATE_ST_IGNORE, ACTION_NONE);
+                       return;
+               case 0x90:
+                       do_trans(vte, raw, STATE_DCS_ENTRY, ACTION_NONE);
+                       return;
+               case 0x9d:
+                       do_trans(vte, raw, STATE_OSC_STRING, ACTION_NONE);
+                       return;
+               case 0x9b:
+                       do_trans(vte, raw, STATE_CSI_ENTRY, ACTION_NONE);
+                       return;
        }
 
-       switch (vte->parser.state) {
-               case STATE_ESC:
-                       parse_esc(vte, val);
+       /* events that depend on the current state */
+       switch (vte->state) {
+       case STATE_GROUND:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x80 ... 0x8f:
+               case 0x91 ... 0x9a:
+               case 0x9c:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
                        return;
-               case STATE_CSI:
-                       parse_csi(vte, val);
+               case 0x20 ... 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_PRINT);
                        return;
-               case STATE_OTHER:
-                       parse_other(vte, val);
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_PRINT);
+               return;
+       case STATE_ESC:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_ESC_INT, ACTION_COLLECT);
+                       return;
+               case 0x30 ... 0x4f:
+               case 0x51 ... 0x57:
+               case 0x59:
+               case 0x5a:
+               case 0x5c:
+               case 0x60 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
+                       return;
+               case 0x5b:
+                       do_trans(vte, raw, STATE_CSI_ENTRY, ACTION_NONE);
+                       return;
+               case 0x5d:
+                       do_trans(vte, raw, STATE_OSC_STRING, ACTION_NONE);
+                       return;
+               case 0x50:
+                       do_trans(vte, raw, STATE_DCS_ENTRY, ACTION_NONE);
+                       return;
+               case 0x58:
+               case 0x5e:
+               case 0x5f:
+                       do_trans(vte, raw, STATE_ST_IGNORE, ACTION_NONE);
                        return;
+               }
+               do_trans(vte, raw, STATE_ESC_INT, ACTION_COLLECT);
+               return;
+       case STATE_ESC_INT:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_COLLECT);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x30 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_COLLECT);
+               return;
+       case STATE_CSI_ENTRY:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_CSI_INT, ACTION_COLLECT);
+                       return;
+               case 0x3a:
+                       do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+                       return;
+               case 0x30 ... 0x39:
+               case 0x3b:
+                       do_trans(vte, raw, STATE_CSI_PARAM, ACTION_PARAM);
+                       return;
+               case 0x3c ... 0x3f:
+                       do_trans(vte, raw, STATE_CSI_PARAM, ACTION_COLLECT);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
+                       return;
+               }
+               do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+               return;
+       case STATE_CSI_PARAM:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x30 ... 0x39:
+               case 0x3b:
+                       do_trans(vte, raw, STATE_NONE, ACTION_PARAM);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x3a:
+               case 0x3c ... 0x3f:
+                       do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_CSI_INT, ACTION_COLLECT);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
+                       return;
+               }
+               do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+               return;
+       case STATE_CSI_INT:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_COLLECT);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x30 ... 0x3f:
+                       do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
+                       return;
+               }
+               do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE);
+               return;
+       case STATE_CSI_IGNORE:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE);
+                       return;
+               case 0x20 ... 0x3f:
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+               return;
+       case STATE_DCS_ENTRY:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x3a:
+                       do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_DCS_INT, ACTION_COLLECT);
+                       return;
+               case 0x30 ... 0x39:
+               case 0x3b:
+                       do_trans(vte, raw, STATE_DCS_PARAM, ACTION_PARAM);
+                       return;
+               case 0x3c ... 0x3f:
+                       do_trans(vte, raw, STATE_DCS_PARAM, ACTION_COLLECT);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+               return;
+       case STATE_DCS_PARAM:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x30 ... 0x39:
+               case 0x3b:
+                       do_trans(vte, raw, STATE_NONE, ACTION_PARAM);
+                       return;
+               case 0x3a:
+               case 0x3c ... 0x3f:
+                       do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_DCS_INT, ACTION_COLLECT);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+               return;
+       case STATE_DCS_INT:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x20 ... 0x2f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_COLLECT);
+                       return;
+               case 0x30 ... 0x3f:
+                       do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE);
+                       return;
+               case 0x40 ... 0x7e:
+                       do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE);
+               return;
+       case STATE_DCS_PASS:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x20 ... 0x7e:
+                       do_trans(vte, raw, STATE_NONE, ACTION_DCS_COLLECT);
+                       return;
+               case 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x9c:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_DCS_COLLECT);
+               return;
+       case STATE_DCS_IGNORE:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x20 ... 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x9c:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+               return;
+       case STATE_OSC_STRING:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x20 ... 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_OSC_COLLECT);
+                       return;
+               case 0x9c:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_OSC_COLLECT);
+               return;
+       case STATE_ST_IGNORE:
+               switch (raw) {
+               case 0x00 ... 0x17:
+               case 0x19:
+               case 0x1c ... 0x1f:
+               case 0x20 ... 0x7f:
+                       do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+                       return;
+               case 0x9c:
+                       do_trans(vte, raw, STATE_GROUND, ACTION_NONE);
+                       return;
+               }
+               do_trans(vte, raw, STATE_NONE, ACTION_IGNORE);
+               return;
        }
 
-       sym = kmscon_symbol_make(val);
-       kmscon_console_write(vte->con, sym);
+       log_warn("vte: unhandled input %u in state %d\n", raw, vte->state);
 }
 
 void kmscon_vte_input(struct kmscon_vte *vte, const char *u8, size_t len)
@@ -354,7 +752,7 @@ void kmscon_vte_input(struct kmscon_vte *vte, const char *u8, size_t len)
                if (state == KMSCON_UTF8_ACCEPT ||
                                state == KMSCON_UTF8_REJECT) {
                        ucs4 = kmscon_utf8_mach_get(vte->mach);
-                       parse_input(vte, ucs4);
+                       parse_data(vte, ucs4);
                }
        }
 }