chiark / gitweb /
Remove src/libsystemd-terminal
[elogind.git] / src / libsystemd-terminal / term-parser.c
diff --git a/src/libsystemd-terminal/term-parser.c b/src/libsystemd-terminal/term-parser.c
deleted file mode 100644 (file)
index 8dc1da2..0000000
+++ /dev/null
@@ -1,1702 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Parser
- * This file contains a bunch of UTF-8 helpers and the main ctlseq-parser. The
- * parser is a simple state-machine that correctly parses all CSI, DCS, OSC, ST
- * control sequences and generic escape sequences.
- * The parser itself does not perform any actions but lets the caller react to
- * detected sequences.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-static const uint8_t default_palette[18][3] = {
-        {   0,   0,   0 }, /* black */
-        { 205,   0,   0 }, /* red */
-        {   0, 205,   0 }, /* green */
-        { 205, 205,   0 }, /* yellow */
-        {   0,   0, 238 }, /* blue */
-        { 205,   0, 205 }, /* magenta */
-        {   0, 205, 205 }, /* cyan */
-        { 229, 229, 229 }, /* light grey */
-        { 127, 127, 127 }, /* dark grey */
-        { 255,   0,   0 }, /* light red */
-        {   0, 255,   0 }, /* light green */
-        { 255, 255,   0 }, /* light yellow */
-        {  92,  92, 255 }, /* light blue */
-        { 255,   0, 255 }, /* light magenta */
-        {   0, 255, 255 }, /* light cyan */
-        { 255, 255, 255 }, /* white */
-
-        { 229, 229, 229 }, /* light grey */
-        {   0,   0,   0 }, /* black */
-};
-
-static uint32_t term_color_to_argb32(const term_color *color, const term_attr *attr, const uint8_t *palette) {
-        static const uint8_t bval[] = {
-                0x00, 0x5f, 0x87,
-                0xaf, 0xd7, 0xff,
-        };
-        uint8_t r, g, b, t;
-
-        assert(color);
-
-        if (!palette)
-                palette = (void*)default_palette;
-
-        switch (color->ccode) {
-        case TERM_CCODE_RGB:
-                r = color->red;
-                g = color->green;
-                b = color->blue;
-
-                break;
-        case TERM_CCODE_256:
-                t = color->c256;
-                if (t < 16) {
-                        r = palette[t * 3 + 0];
-                        g = palette[t * 3 + 1];
-                        b = palette[t * 3 + 2];
-                } else if (t < 232) {
-                        t -= 16;
-                        b = bval[t % 6];
-                        t /= 6;
-                        g = bval[t % 6];
-                        t /= 6;
-                        r = bval[t % 6];
-                } else {
-                        t = (t - 232) * 10 + 8;
-                        r = t;
-                        g = t;
-                        b = t;
-                }
-
-                break;
-        case TERM_CCODE_BLACK ... TERM_CCODE_LIGHT_WHITE:
-                t = color->ccode - TERM_CCODE_BLACK;
-
-                /* bold causes light colors (only for foreground colors) */
-                if (t < 8 && attr->bold && color == &attr->fg)
-                        t += 8;
-
-                r = palette[t * 3 + 0];
-                g = palette[t * 3 + 1];
-                b = palette[t * 3 + 2];
-                break;
-        case TERM_CCODE_DEFAULT:
-                /* fallthrough */
-        default:
-                t = 16 + !(color == &attr->fg);
-                r = palette[t * 3 + 0];
-                g = palette[t * 3 + 1];
-                b = palette[t * 3 + 2];
-                break;
-        }
-
-        return (0xff << 24) | (r << 16) | (g << 8) | b;
-}
-
-/**
- * term_attr_to_argb32() - Encode terminal colors as native ARGB32 value
- * @color: Terminal attributes to work on
- * @fg: Storage for foreground color (or NULL)
- * @bg: Storage for background color (or NULL)
- * @palette: The color palette to use (or NULL for default)
- *
- * This encodes the colors attr->fg and attr->bg as native-endian ARGB32 values
- * and returns them. Any color conversions are automatically applied.
- */
-void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette) {
-        uint32_t f, b, t;
-
-        assert(attr);
-
-        f = term_color_to_argb32(&attr->fg, attr, palette);
-        b = term_color_to_argb32(&attr->bg, attr, palette);
-
-        if (attr->inverse) {
-                t = f;
-                f = b;
-                b = t;
-        }
-
-        if (fg)
-                *fg = f;
-        if (bg)
-                *bg = b;
-}
-
-/**
- * term_utf8_decode() - Try decoding the next UCS-4 character
- * @p: decoder object to operate on or NULL
- * @out_len: output storage for pointer to decoded UCS-4 string or NULL
- * @c: next char to push into decoder
- *
- * This decodes a UTF-8 stream. It must be called for each input-byte of the
- * UTF-8 stream and returns a UCS-4 stream. A pointer to the parsed UCS-4
- * string is stored in @out_buf if non-NULL. The length of this string (number
- * of parsed UCS4 characters) is returned as result. The string is not
- * zero-terminated! Furthermore, the string is only valid until the next
- * invocation of this function. It is also bound to the parser state @p and
- * must not be freed nor written to by the caller.
- *
- * This function is highly optimized to work with terminal-emulators. Instead
- * of being strict about UTF-8 validity, this tries to perform a fallback to
- * ISO-8859-1 in case a wrong series was detected. Therefore, this function
- * might return multiple UCS-4 characters by parsing just a single UTF-8 byte.
- *
- * The parser state @p should be allocated and managed by the caller. There're
- * no helpers to do that for you. To initialize it, simply reset it to all
- * zero. You can reset or free the object at any point in time.
- *
- * Returns: Number of parsed UCS4 characters
- */
-size_t term_utf8_decode(term_utf8 *p, uint32_t **out_buf, char c) {
-        static uint32_t ucs4_null = 0;
-        uint32_t t, *res = NULL;
-        uint8_t byte;
-        size_t len = 0;
-
-        if (!p)
-                goto out;
-
-        byte = c;
-
-        if (!p->valid || p->i_bytes >= p->n_bytes) {
-                /*
-                 * If the previous sequence was invalid or fully parsed, start
-                 * parsing a fresh new sequence.
-                 */
-
-                if ((byte & 0xE0) == 0xC0) {
-                        /* start of two byte sequence */
-                        t = byte & 0x1F;
-                        p->n_bytes = 2;
-                        p->i_bytes = 1;
-                        p->valid = 1;
-                } else if ((byte & 0xF0) == 0xE0) {
-                        /* start of three byte sequence */
-                        t = byte & 0x0F;
-                        p->n_bytes = 3;
-                        p->i_bytes = 1;
-                        p->valid = 1;
-                } else if ((byte & 0xF8) == 0xF0) {
-                        /* start of four byte sequence */
-                        t = byte & 0x07;
-                        p->n_bytes = 4;
-                        p->i_bytes = 1;
-                        p->valid = 1;
-                } else {
-                        /* Either of:
-                         *  - single ASCII 7-bit char
-                         *  - out-of-sync continuation byte
-                         *  - overlong encoding
-                         * All of them are treated as single byte ISO-8859-1 */
-                        t = byte;
-                        p->n_bytes = 1;
-                        p->i_bytes = 1;
-                        p->valid = 0;
-                }
-
-                p->chars[0] = byte;
-                p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
-        } else {
-                /*
-                 * ..otherwise, try to continue the previous sequence..
-                 */
-
-                if ((byte & 0xC0) == 0x80) {
-                        /*
-                         * Valid continuation byte. Append to sequence and
-                         * update the ucs4 cache accordingly.
-                         */
-
-                        t = byte & 0x3F;
-                        p->chars[p->i_bytes++] = byte;
-                        p->ucs4 |= t << (6 * (p->n_bytes - p->i_bytes));
-                } else {
-                        /*
-                         * Invalid continuation? Treat cached sequence as
-                         * ISO-8859-1, but parse the new char as valid new
-                         * starting character. If it's a new single-byte UTF-8
-                         * sequence, we immediately return it in the same run,
-                         * otherwise, we might suffer from starvation.
-                         */
-
-                        if ((byte & 0xE0) == 0xC0 ||
-                            (byte & 0xF0) == 0xE0 ||
-                            (byte & 0xF8) == 0xF0) {
-                                /*
-                                 * New multi-byte sequence. Move to-be-returned
-                                 * data at the end and start new sequence. Only
-                                 * return the old sequence.
-                                 */
-
-                                memmove(p->chars + 1,
-                                        p->chars,
-                                        sizeof(*p->chars) * p->i_bytes);
-                                res = p->chars + 1;
-                                len = p->i_bytes;
-
-                                if ((byte & 0xE0) == 0xC0) {
-                                        /* start of two byte sequence */
-                                        t = byte & 0x1F;
-                                        p->n_bytes = 2;
-                                        p->i_bytes = 1;
-                                        p->valid = 1;
-                                } else if ((byte & 0xF0) == 0xE0) {
-                                        /* start of three byte sequence */
-                                        t = byte & 0x0F;
-                                        p->n_bytes = 3;
-                                        p->i_bytes = 1;
-                                        p->valid = 1;
-                                } else if ((byte & 0xF8) == 0xF0) {
-                                        /* start of four byte sequence */
-                                        t = byte & 0x07;
-                                        p->n_bytes = 4;
-                                        p->i_bytes = 1;
-                                        p->valid = 1;
-                                } else
-                                        assert_not_reached("Should not happen");
-
-                                p->chars[0] = byte;
-                                p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
-
-                                goto out;
-                        } else {
-                                /*
-                                 * New single byte sequence, append to output
-                                 * and return combined sequence.
-                                 */
-
-                                p->chars[p->i_bytes++] = byte;
-                                p->valid = 0;
-                        }
-                }
-        }
-
-        /*
-         * Check whether a full sequence (valid or invalid) has been parsed and
-         * then return it. Otherwise, return nothing.
-         */
-        if (p->valid) {
-                /* still parsing? then bail out */
-                if (p->i_bytes < p->n_bytes)
-                        goto out;
-
-                res = &p->ucs4;
-                len = 1;
-        } else {
-                res = p->chars;
-                len = p->i_bytes;
-        }
-
-        p->valid = 0;
-        p->i_bytes = 0;
-        p->n_bytes = 0;
-
-out:
-        if (out_buf)
-                *out_buf = res ? : &ucs4_null;
-        return len;
-}
-
-/*
- * Command Parser
- * The ctl-seq parser "term_parser" only detects whole sequences, it does not
- * detect the specific command. Once a sequence is parsed, the command-parsers
- * are used to figure out their meaning. Note that this depends on whether we
- * run on the host or terminal side.
- */
-
-static unsigned int term_parse_host_control(const term_seq *seq) {
-        assert_return(seq, TERM_CMD_NONE);
-
-        switch (seq->terminator) {
-        case 0x00: /* NUL */
-                return TERM_CMD_NULL;
-        case 0x05: /* ENQ */
-                return TERM_CMD_ENQ;
-        case 0x07: /* BEL */
-                return TERM_CMD_BEL;
-        case 0x08: /* BS */
-                return TERM_CMD_BS;
-        case 0x09: /* HT */
-                return TERM_CMD_HT;
-        case 0x0a: /* LF */
-                return TERM_CMD_LF;
-        case 0x0b: /* VT */
-                return TERM_CMD_VT;
-        case 0x0c: /* FF */
-                return TERM_CMD_FF;
-        case 0x0d: /* CR */
-                return TERM_CMD_CR;
-        case 0x0e: /* SO */
-                return TERM_CMD_SO;
-        case 0x0f: /* SI */
-                return TERM_CMD_SI;
-        case 0x11: /* DC1 */
-                return TERM_CMD_DC1;
-        case 0x13: /* DC3 */
-                return TERM_CMD_DC3;
-        case 0x18: /* CAN */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x1a: /* SUB */
-                return TERM_CMD_SUB;
-        case 0x1b: /* ESC */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x1f: /* DEL */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x84: /* IND */
-                return TERM_CMD_IND;
-        case 0x85: /* NEL */
-                return TERM_CMD_NEL;
-        case 0x88: /* HTS */
-                return TERM_CMD_HTS;
-        case 0x8d: /* RI */
-                return TERM_CMD_RI;
-        case 0x8e: /* SS2 */
-                return TERM_CMD_SS2;
-        case 0x8f: /* SS3 */
-                return TERM_CMD_SS3;
-        case 0x90: /* DCS */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x96: /* SPA */
-                return TERM_CMD_SPA;
-        case 0x97: /* EPA */
-                return TERM_CMD_EPA;
-        case 0x98: /* SOS */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x9a: /* DECID */
-                return TERM_CMD_DECID;
-        case 0x9b: /* CSI */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x9c: /* ST */
-                return TERM_CMD_ST;
-        case 0x9d: /* OSC */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x9e: /* PM */
-                /* this is already handled by the state-machine */
-                break;
-        case 0x9f: /* APC */
-                /* this is already handled by the state-machine */
-                break;
-        }
-
-        return TERM_CMD_NONE;
-}
-
-static inline int charset_from_cmd(uint32_t raw, unsigned int flags, bool require_96) {
-        static const struct {
-                uint32_t raw;
-                unsigned int flags;
-        } charset_cmds[] = {
-                /* 96-compat charsets */
-                [TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL]   = { .raw = 'A', .flags = 0 },
-                [TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL]   = { .raw = 'B', .flags = 0 },
-                [TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL]   = { .raw = 'M', .flags = 0 },
-                [TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL]    = { .raw = 'F', .flags = 0 },
-                [TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL]   = { .raw = 'H', .flags = 0 },
-                [TERM_CHARSET_ISO_LATIN_CYRILLIC]        = { .raw = 'L', .flags = 0 },
-
-                /* 94-compat charsets */
-                [TERM_CHARSET_DEC_SPECIAL_GRAPHIC]       = { .raw = '0', .flags = 0 },
-                [TERM_CHARSET_DEC_SUPPLEMENTAL]          = { .raw = '5', .flags = TERM_SEQ_FLAG_PERCENT },
-                [TERM_CHARSET_DEC_TECHNICAL]             = { .raw = '>', .flags = 0 },
-                [TERM_CHARSET_CYRILLIC_DEC]              = { .raw = '4', .flags = TERM_SEQ_FLAG_AND },
-                [TERM_CHARSET_DUTCH_NRCS]                = { .raw = '4', .flags = 0 },
-                [TERM_CHARSET_FINNISH_NRCS]              = { .raw = '5', .flags = 0 },
-                [TERM_CHARSET_FRENCH_NRCS]               = { .raw = 'R', .flags = 0 },
-                [TERM_CHARSET_FRENCH_CANADIAN_NRCS]      = { .raw = '9', .flags = 0 },
-                [TERM_CHARSET_GERMAN_NRCS]               = { .raw = 'K', .flags = 0 },
-                [TERM_CHARSET_GREEK_DEC]                 = { .raw = '?', .flags = TERM_SEQ_FLAG_DQUOTE },
-                [TERM_CHARSET_GREEK_NRCS]                = { .raw = '>', .flags = TERM_SEQ_FLAG_DQUOTE },
-                [TERM_CHARSET_HEBREW_DEC]                = { .raw = '4', .flags = TERM_SEQ_FLAG_DQUOTE },
-                [TERM_CHARSET_HEBREW_NRCS]               = { .raw = '=', .flags = TERM_SEQ_FLAG_PERCENT },
-                [TERM_CHARSET_ITALIAN_NRCS]              = { .raw = 'Y', .flags = 0 },
-                [TERM_CHARSET_NORWEGIAN_DANISH_NRCS]     = { .raw = '`', .flags = 0 },
-                [TERM_CHARSET_PORTUGUESE_NRCS]           = { .raw = '6', .flags = TERM_SEQ_FLAG_PERCENT },
-                [TERM_CHARSET_RUSSIAN_NRCS]              = { .raw = '5', .flags = TERM_SEQ_FLAG_AND },
-                [TERM_CHARSET_SCS_NRCS]                  = { .raw = '3', .flags = TERM_SEQ_FLAG_PERCENT },
-                [TERM_CHARSET_SPANISH_NRCS]              = { .raw = 'Z', .flags = 0 },
-                [TERM_CHARSET_SWEDISH_NRCS]              = { .raw = '7', .flags = 0 },
-                [TERM_CHARSET_SWISS_NRCS]                = { .raw = '=', .flags = 0 },
-                [TERM_CHARSET_TURKISH_DEC]               = { .raw = '0', .flags = TERM_SEQ_FLAG_PERCENT },
-                [TERM_CHARSET_TURKISH_NRCS]              = { .raw = '2', .flags = TERM_SEQ_FLAG_PERCENT },
-
-                /* special charsets */
-                [TERM_CHARSET_USERPREF_SUPPLEMENTAL]     = { .raw = '<', .flags = 0 },
-
-                /* secondary choices */
-                [TERM_CHARSET_CNT + TERM_CHARSET_FINNISH_NRCS]            = { .raw = 'C', .flags = 0 },
-                [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_NRCS]             = { .raw = 'f', .flags = 0 },
-                [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_CANADIAN_NRCS]    = { .raw = 'Q', .flags = 0 },
-                [TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS]   = { .raw = 'E', .flags = 0 },
-                [TERM_CHARSET_CNT + TERM_CHARSET_SWEDISH_NRCS]            = { .raw = 'H', .flags = 0 }, /* unused; conflicts with ISO_HEBREW */
-
-                /* tertiary choices */
-                [TERM_CHARSET_CNT + TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = '6', .flags = 0 },
-        };
-        size_t i, cs;
-
-        /*
-         * Secondary choice on SWEDISH_NRCS and primary choice on
-         * ISO_HEBREW_SUPPLEMENTAL have a conflict: raw=="H", flags==0.
-         * We always choose the ISO 96-compat set, which is what VT510 does.
-         */
-
-        for (i = 0; i < ELEMENTSOF(charset_cmds); ++i) {
-                if (charset_cmds[i].raw == raw && charset_cmds[i].flags == flags) {
-                        cs = i;
-                        while (cs >= TERM_CHARSET_CNT)
-                                cs -= TERM_CHARSET_CNT;
-
-                        if (!require_96 || cs < TERM_CHARSET_96_CNT || cs >= TERM_CHARSET_94_CNT)
-                                return cs;
-                }
-        }
-
-        return -ENOENT;
-}
-
-/* true if exactly one bit in @value is set */
-static inline bool exactly_one_bit_set(unsigned int value) {
-        return __builtin_popcount(value) == 1;
-}
-
-static unsigned int term_parse_host_escape(const term_seq *seq, unsigned int *cs_out) {
-        unsigned int t, flags;
-        int cs;
-
-        assert_return(seq, TERM_CMD_NONE);
-
-        flags = seq->intermediates;
-        t = TERM_SEQ_FLAG_POPEN | TERM_SEQ_FLAG_PCLOSE | TERM_SEQ_FLAG_MULT |
-            TERM_SEQ_FLAG_PLUS  | TERM_SEQ_FLAG_MINUS  | TERM_SEQ_FLAG_DOT  |
-            TERM_SEQ_FLAG_SLASH;
-
-        if (exactly_one_bit_set(flags & t)) {
-                switch (flags & t) {
-                case TERM_SEQ_FLAG_POPEN:
-                case TERM_SEQ_FLAG_PCLOSE:
-                case TERM_SEQ_FLAG_MULT:
-                case TERM_SEQ_FLAG_PLUS:
-                        cs = charset_from_cmd(seq->terminator, flags & ~t, false);
-                        break;
-                case TERM_SEQ_FLAG_MINUS:
-                case TERM_SEQ_FLAG_DOT:
-                case TERM_SEQ_FLAG_SLASH:
-                        cs = charset_from_cmd(seq->terminator, flags & ~t, true);
-                        break;
-                default:
-                        cs = -ENOENT;
-                        break;
-                }
-
-                if (cs >= 0) {
-                        if (cs_out)
-                                *cs_out = cs;
-                        return TERM_CMD_SCS;
-                }
-
-                /* looked like a charset-cmd but wasn't; continue */
-        }
-
-        switch (seq->terminator) {
-        case '3':
-                if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL top-half */
-                        return TERM_CMD_DECDHL_TH;
-                break;
-        case '4':
-                if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL bottom-half */
-                        return TERM_CMD_DECDHL_BH;
-                break;
-        case '5':
-                if (flags == TERM_SEQ_FLAG_HASH) /* DECSWL */
-                        return TERM_CMD_DECSWL;
-                break;
-        case '6':
-                if (flags == 0) /* DECBI */
-                        return TERM_CMD_DECBI;
-                else if (flags == TERM_SEQ_FLAG_HASH) /* DECDWL */
-                        return TERM_CMD_DECDWL;
-                break;
-        case '7':
-                if (flags == 0) /* DECSC */
-                        return TERM_CMD_DECSC;
-                break;
-        case '8':
-                if (flags == 0) /* DECRC */
-                        return TERM_CMD_DECRC;
-                else if (flags == TERM_SEQ_FLAG_HASH) /* DECALN */
-                        return TERM_CMD_DECALN;
-                break;
-        case '9':
-                if (flags == 0) /* DECFI */
-                        return TERM_CMD_DECFI;
-                break;
-        case '<':
-                if (flags == 0) /* DECANM */
-                        return TERM_CMD_DECANM;
-                break;
-        case '=':
-                if (flags == 0) /* DECKPAM */
-                        return TERM_CMD_DECKPAM;
-                break;
-        case '>':
-                if (flags == 0) /* DECKPNM */
-                        return TERM_CMD_DECKPNM;
-                break;
-        case '@':
-                if (flags == TERM_SEQ_FLAG_PERCENT) {
-                        /* Select default character set */
-                        return TERM_CMD_XTERM_SDCS;
-                }
-                break;
-        case 'D':
-                if (flags == 0) /* IND */
-                        return TERM_CMD_IND;
-                break;
-        case 'E':
-                if (flags == 0) /* NEL */
-                        return TERM_CMD_NEL;
-                break;
-        case 'F':
-                if (flags == 0) /* Cursor to lower-left corner of screen */
-                        return TERM_CMD_XTERM_CLLHP;
-                else if (flags == TERM_SEQ_FLAG_SPACE) /* S7C1T */
-                        return TERM_CMD_S7C1T;
-                break;
-        case 'G':
-                if (flags == TERM_SEQ_FLAG_SPACE) { /* S8C1T */
-                        return TERM_CMD_S8C1T;
-                } else if (flags == TERM_SEQ_FLAG_PERCENT) {
-                        /* Select UTF-8 character set */
-                        return TERM_CMD_XTERM_SUCS;
-                }
-                break;
-        case 'H':
-                if (flags == 0) /* HTS */
-                        return TERM_CMD_HTS;
-                break;
-        case 'L':
-                if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* Set ANSI conformance level 1 */
-                        return TERM_CMD_XTERM_SACL1;
-                }
-                break;
-        case 'M':
-                if (flags == 0) { /* RI */
-                        return TERM_CMD_RI;
-                } else if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* Set ANSI conformance level 2 */
-                        return TERM_CMD_XTERM_SACL2;
-                }
-                break;
-        case 'N':
-                if (flags == 0) { /* SS2 */
-                        return TERM_CMD_SS2;
-                } else if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* Set ANSI conformance level 3 */
-                        return TERM_CMD_XTERM_SACL3;
-                }
-                break;
-        case 'O':
-                if (flags == 0) /* SS3 */
-                        return TERM_CMD_SS3;
-                break;
-        case 'P':
-                if (flags == 0) /* DCS: this is already handled by the state-machine */
-                        return 0;
-                break;
-        case 'V':
-                if (flags == 0) /* SPA */
-                        return TERM_CMD_SPA;
-                break;
-        case 'W':
-                if (flags == 0) /* EPA */
-                        return TERM_CMD_EPA;
-                break;
-        case 'X':
-                if (flags == 0) { /* SOS */
-                        /* this is already handled by the state-machine */
-                        break;
-                }
-                break;
-        case 'Z':
-                if (flags == 0) /* DECID */
-                        return TERM_CMD_DECID;
-                break;
-        case '[':
-                if (flags == 0) { /* CSI */
-                        /* this is already handled by the state-machine */
-                        break;
-                }
-                break;
-        case '\\':
-                if (flags == 0) /* ST */
-                        return TERM_CMD_ST;
-                break;
-        case ']':
-                if (flags == 0) { /* OSC */
-                        /* this is already handled by the state-machine */
-                        break;
-                }
-                break;
-        case '^':
-                if (flags == 0) { /* PM */
-                        /* this is already handled by the state-machine */
-                        break;
-                }
-                break;
-        case '_':
-                if (flags == 0) { /* APC */
-                        /* this is already handled by the state-machine */
-                        break;
-                }
-                break;
-        case 'c':
-                if (flags == 0) /* RIS */
-                        return TERM_CMD_RIS;
-                break;
-        case 'l':
-                if (flags == 0) /* Memory lock */
-                        return TERM_CMD_XTERM_MLHP;
-                break;
-        case 'm':
-                if (flags == 0) /* Memory unlock */
-                        return TERM_CMD_XTERM_MUHP;
-                break;
-        case 'n':
-                if (flags == 0) /* LS2 */
-                        return TERM_CMD_LS2;
-                break;
-        case 'o':
-                if (flags == 0) /* LS3 */
-                        return TERM_CMD_LS3;
-                break;
-        case '|':
-                if (flags == 0) /* LS3R */
-                        return TERM_CMD_LS3R;
-                break;
-        case '}':
-                if (flags == 0) /* LS2R */
-                        return TERM_CMD_LS2R;
-                break;
-        case '~':
-                if (flags == 0) /* LS1R */
-                        return TERM_CMD_LS1R;
-                break;
-        }
-
-        return TERM_CMD_NONE;
-}
-
-static unsigned int term_parse_host_csi(const term_seq *seq) {
-        unsigned int flags;
-
-        assert_return(seq, TERM_CMD_NONE);
-
-        flags = seq->intermediates;
-
-        switch (seq->terminator) {
-        case 'A':
-                if (flags == 0) /* CUU */
-                        return TERM_CMD_CUU;
-                break;
-        case 'a':
-                if (flags == 0) /* HPR */
-                        return TERM_CMD_HPR;
-                break;
-        case 'B':
-                if (flags == 0) /* CUD */
-                        return TERM_CMD_CUD;
-                break;
-        case 'b':
-                if (flags == 0) /* REP */
-                        return TERM_CMD_REP;
-                break;
-        case 'C':
-                if (flags == 0) /* CUF */
-                        return TERM_CMD_CUF;
-                break;
-        case 'c':
-                if (flags == 0) /* DA1 */
-                        return TERM_CMD_DA1;
-                else if (flags == TERM_SEQ_FLAG_GT) /* DA2 */
-                        return TERM_CMD_DA2;
-                else if (flags == TERM_SEQ_FLAG_EQUAL) /* DA3 */
-                        return TERM_CMD_DA3;
-                break;
-        case 'D':
-                if (flags == 0) /* CUB */
-                        return TERM_CMD_CUB;
-                break;
-        case 'd':
-                if (flags == 0) /* VPA */
-                        return TERM_CMD_VPA;
-                break;
-        case 'E':
-                if (flags == 0) /* CNL */
-                        return TERM_CMD_CNL;
-                break;
-        case 'e':
-                if (flags == 0) /* VPR */
-                        return TERM_CMD_VPR;
-                break;
-        case 'F':
-                if (flags == 0) /* CPL */
-                        return TERM_CMD_CPL;
-                break;
-        case 'f':
-                if (flags == 0) /* HVP */
-                        return TERM_CMD_HVP;
-                break;
-        case 'G':
-                if (flags == 0) /* CHA */
-                        return TERM_CMD_CHA;
-                break;
-        case 'g':
-                if (flags == 0) /* TBC */
-                        return TERM_CMD_TBC;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECLFKC */
-                        return TERM_CMD_DECLFKC;
-                break;
-        case 'H':
-                if (flags == 0) /* CUP */
-                        return TERM_CMD_CUP;
-                break;
-        case 'h':
-                if (flags == 0) /* SM ANSI */
-                        return TERM_CMD_SM_ANSI;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* SM DEC */
-                        return TERM_CMD_SM_DEC;
-                break;
-        case 'I':
-                if (flags == 0) /* CHT */
-                        return TERM_CMD_CHT;
-                break;
-        case 'i':
-                if (flags == 0) /* MC ANSI */
-                        return TERM_CMD_MC_ANSI;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* MC DEC */
-                        return TERM_CMD_MC_DEC;
-                break;
-        case 'J':
-                if (flags == 0) /* ED */
-                        return TERM_CMD_ED;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSED */
-                        return TERM_CMD_DECSED;
-                break;
-        case 'K':
-                if (flags == 0) /* EL */
-                        return TERM_CMD_EL;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSEL */
-                        return TERM_CMD_DECSEL;
-                break;
-        case 'L':
-                if (flags == 0) /* IL */
-                        return TERM_CMD_IL;
-                break;
-        case 'l':
-                if (flags == 0) /* RM ANSI */
-                        return TERM_CMD_RM_ANSI;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* RM DEC */
-                        return TERM_CMD_RM_DEC;
-                break;
-        case 'M':
-                if (flags == 0) /* DL */
-                        return TERM_CMD_DL;
-                break;
-        case 'm':
-                if (flags == 0) /* SGR */
-                        return TERM_CMD_SGR;
-                else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SMR */
-                        return TERM_CMD_XTERM_SRV;
-                break;
-        case 'n':
-                if (flags == 0) /* DSR ANSI */
-                        return TERM_CMD_DSR_ANSI;
-                else if (flags == TERM_SEQ_FLAG_GT) /* XTERM RMR */
-                        return TERM_CMD_XTERM_RRV;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* DSR DEC */
-                        return TERM_CMD_DSR_DEC;
-                break;
-        case 'P':
-                if (flags == 0) /* DCH */
-                        return TERM_CMD_DCH;
-                else if (flags == TERM_SEQ_FLAG_SPACE) /* PPA */
-                        return TERM_CMD_PPA;
-                break;
-        case 'p':
-                if (flags == 0) /* DECSSL */
-                        return TERM_CMD_DECSSL;
-                else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSSCLS */
-                        return TERM_CMD_DECSSCLS;
-                else if (flags == TERM_SEQ_FLAG_BANG) /* DECSTR */
-                        return TERM_CMD_DECSTR;
-                else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCL */
-                        return TERM_CMD_DECSCL;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECRQM-ANSI */
-                        return TERM_CMD_DECRQM_ANSI;
-                else if (flags == (TERM_SEQ_FLAG_CASH | TERM_SEQ_FLAG_WHAT)) /* DECRQM-DEC */
-                        return TERM_CMD_DECRQM_DEC;
-                else if (flags == TERM_SEQ_FLAG_PCLOSE) /* DECSDPT */
-                        return TERM_CMD_DECSDPT;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECSPPCS */
-                        return TERM_CMD_DECSPPCS;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSR */
-                        return TERM_CMD_DECSR;
-                else if (flags == TERM_SEQ_FLAG_COMMA) /* DECLTOD */
-                        return TERM_CMD_DECLTOD;
-                else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SPM */
-                        return TERM_CMD_XTERM_SPM;
-                break;
-        case 'Q':
-                if (flags == TERM_SEQ_FLAG_SPACE) /* PPR */
-                        return TERM_CMD_PPR;
-                break;
-        case 'q':
-                if (flags == 0) /* DECLL */
-                        return TERM_CMD_DECLL;
-                else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSCUSR */
-                        return TERM_CMD_DECSCUSR;
-                else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCA */
-                        return TERM_CMD_DECSCA;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECSDDT */
-                        return TERM_CMD_DECSDDT;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECSRC */
-                        return TERM_CMD_DECSR;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECELF */
-                        return TERM_CMD_DECELF;
-                else if (flags == TERM_SEQ_FLAG_COMMA) /* DECTID */
-                        return TERM_CMD_DECTID;
-                break;
-        case 'R':
-                if (flags == TERM_SEQ_FLAG_SPACE) /* PPB */
-                        return TERM_CMD_PPB;
-                break;
-        case 'r':
-                if (flags == 0) {
-                        /* DECSTBM */
-                        return TERM_CMD_DECSTBM;
-                } else if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* DECSKCV */
-                        return TERM_CMD_DECSKCV;
-                } else if (flags == TERM_SEQ_FLAG_CASH) {
-                        /* DECCARA */
-                        return TERM_CMD_DECCARA;
-                } else if (flags == TERM_SEQ_FLAG_MULT) {
-                        /* DECSCS */
-                        return TERM_CMD_DECSCS;
-                } else if (flags == TERM_SEQ_FLAG_PLUS) {
-                        /* DECSMKR */
-                        return TERM_CMD_DECSMKR;
-                } else if (flags == TERM_SEQ_FLAG_WHAT) {
-                        /*
-                         * There's a conflict between DECPCTERM and XTERM-RPM.
-                         * XTERM-RPM takes a single argument, DECPCTERM takes 2.
-                         * Split both up and forward the call to the closer
-                         * match.
-                         */
-                        if (seq->n_args <= 1) /* XTERM RPM */
-                                return TERM_CMD_XTERM_RPM;
-                        else if (seq->n_args >= 2) /* DECPCTERM */
-                                return TERM_CMD_DECPCTERM;
-                }
-                break;
-        case 'S':
-                if (flags == 0) /* SU */
-                        return TERM_CMD_SU;
-                else if (flags == TERM_SEQ_FLAG_WHAT) /* XTERM SGFX */
-                        return TERM_CMD_XTERM_SGFX;
-                break;
-        case 's':
-                if (flags == 0) {
-                        /*
-                         * There's a conflict between DECSLRM and SC-ANSI which
-                         * cannot be resolved without knowing the state of
-                         * DECLRMM. We leave that decision up to the caller.
-                         */
-                        return TERM_CMD_DECSLRM_OR_SC;
-                } else if (flags == TERM_SEQ_FLAG_CASH) {
-                        /* DECSPRTT */
-                        return TERM_CMD_DECSPRTT;
-                } else if (flags == TERM_SEQ_FLAG_MULT) {
-                        /* DECSFC */
-                        return TERM_CMD_DECSFC;
-                } else if (flags == TERM_SEQ_FLAG_WHAT) {
-                        /* XTERM SPM */
-                        return TERM_CMD_XTERM_SPM;
-                }
-                break;
-        case 'T':
-                if (flags == 0) {
-                        /*
-                         * Awesome: There's a conflict between SD and XTERM IHMT
-                         * that we have to resolve by checking the parameter
-                         * count.. XTERM_IHMT needs exactly 5 arguments, SD
-                         * takes 0 or 1. We're conservative here and give both
-                         * a wider range to allow unused arguments (compat...).
-                         */
-                        if (seq->n_args >= 5) {
-                                /* XTERM IHMT */
-                                return TERM_CMD_XTERM_IHMT;
-                        } else if (seq->n_args < 5) {
-                                /* SD */
-                                return TERM_CMD_SD;
-                        }
-                } else if (flags == TERM_SEQ_FLAG_GT) {
-                        /* XTERM RTM */
-                        return TERM_CMD_XTERM_RTM;
-                }
-                break;
-        case 't':
-                if (flags == 0) {
-                        if (seq->n_args > 0 && seq->args[0] < 24) {
-                                /* XTERM WM */
-                                return TERM_CMD_XTERM_WM;
-                        } else {
-                                /* DECSLPP */
-                                return TERM_CMD_DECSLPP;
-                        }
-                } else if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* DECSWBV */
-                        return TERM_CMD_DECSWBV;
-                } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
-                        /* DECSRFR */
-                        return TERM_CMD_DECSRFR;
-                } else if (flags == TERM_SEQ_FLAG_CASH) {
-                        /* DECRARA */
-                        return TERM_CMD_DECRARA;
-                } else if (flags == TERM_SEQ_FLAG_GT) {
-                        /* XTERM STM */
-                        return TERM_CMD_XTERM_STM;
-                }
-                break;
-        case 'U':
-                if (flags == 0) /* NP */
-                        return TERM_CMD_NP;
-                break;
-        case 'u':
-                if (flags == 0) {
-                        /* RC */
-                        return TERM_CMD_RC;
-                } else if (flags == TERM_SEQ_FLAG_SPACE) {
-                        /* DECSMBV */
-                        return TERM_CMD_DECSMBV;
-                } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
-                        /* DECSTRL */
-                        return TERM_CMD_DECSTRL;
-                } else if (flags == TERM_SEQ_FLAG_WHAT) {
-                        /* DECRQUPSS */
-                        return TERM_CMD_DECRQUPSS;
-                } else if (seq->args[0] == 1 && flags == TERM_SEQ_FLAG_CASH) {
-                        /* DECRQTSR */
-                        return TERM_CMD_DECRQTSR;
-                } else if (flags == TERM_SEQ_FLAG_MULT) {
-                        /* DECSCP */
-                        return TERM_CMD_DECSCP;
-                } else if (flags == TERM_SEQ_FLAG_COMMA) {
-                        /* DECRQKT */
-                        return TERM_CMD_DECRQKT;
-                }
-                break;
-        case 'V':
-                if (flags == 0) /* PP */
-                        return TERM_CMD_PP;
-                break;
-        case 'v':
-                if (flags == TERM_SEQ_FLAG_SPACE) /* DECSLCK */
-                        return TERM_CMD_DECSLCK;
-                else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECRQDE */
-                        return TERM_CMD_DECRQDE;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECCRA */
-                        return TERM_CMD_DECCRA;
-                else if (flags == TERM_SEQ_FLAG_COMMA) /* DECRPKT */
-                        return TERM_CMD_DECRPKT;
-                break;
-        case 'W':
-                if (seq->args[0] == 5 && flags == TERM_SEQ_FLAG_WHAT) {
-                        /* DECST8C */
-                        return TERM_CMD_DECST8C;
-                }
-                break;
-        case 'w':
-                if (flags == TERM_SEQ_FLAG_CASH) /* DECRQPSR */
-                        return TERM_CMD_DECRQPSR;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECEFR */
-                        return TERM_CMD_DECEFR;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSPP */
-                        return TERM_CMD_DECSPP;
-                break;
-        case 'X':
-                if (flags == 0) /* ECH */
-                        return TERM_CMD_ECH;
-                break;
-        case 'x':
-                if (flags == 0) /* DECREQTPARM */
-                        return TERM_CMD_DECREQTPARM;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECFRA */
-                        return TERM_CMD_DECFRA;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECSACE */
-                        return TERM_CMD_DECSACE;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECRQPKFM */
-                        return TERM_CMD_DECRQPKFM;
-                break;
-        case 'y':
-                if (flags == 0) /* DECTST */
-                        return TERM_CMD_DECTST;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECRQCRA */
-                        return TERM_CMD_DECRQCRA;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKFMR */
-                        return TERM_CMD_DECPKFMR;
-                break;
-        case 'Z':
-                if (flags == 0) /* CBT */
-                        return TERM_CMD_CBT;
-                break;
-        case 'z':
-                if (flags == TERM_SEQ_FLAG_CASH) /* DECERA */
-                        return TERM_CMD_DECERA;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECELR */
-                        return TERM_CMD_DECELR;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECINVM */
-                        return TERM_CMD_DECINVM;
-                else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKA */
-                        return TERM_CMD_DECPKA;
-                break;
-        case '@':
-                if (flags == 0) /* ICH */
-                        return TERM_CMD_ICH;
-                break;
-        case '`':
-                if (flags == 0) /* HPA */
-                        return TERM_CMD_HPA;
-                break;
-        case '{':
-                if (flags == TERM_SEQ_FLAG_CASH) /* DECSERA */
-                        return TERM_CMD_DECSERA;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECSLE */
-                        return TERM_CMD_DECSLE;
-                break;
-        case '|':
-                if (flags == TERM_SEQ_FLAG_CASH) /* DECSCPP */
-                        return TERM_CMD_DECSCPP;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECRQLP */
-                        return TERM_CMD_DECRQLP;
-                else if (flags == TERM_SEQ_FLAG_MULT) /* DECSNLS */
-                        return TERM_CMD_DECSNLS;
-                break;
-        case '}':
-                if (flags == TERM_SEQ_FLAG_SPACE) /* DECKBD */
-                        return TERM_CMD_DECKBD;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECSASD */
-                        return TERM_CMD_DECSASD;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECIC */
-                        return TERM_CMD_DECIC;
-                break;
-        case '~':
-                if (flags == TERM_SEQ_FLAG_SPACE) /* DECTME */
-                        return TERM_CMD_DECTME;
-                else if (flags == TERM_SEQ_FLAG_CASH) /* DECSSDT */
-                        return TERM_CMD_DECSSDT;
-                else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECDC */
-                        return TERM_CMD_DECDC;
-                break;
-        }
-
-        return TERM_CMD_NONE;
-}
-
-/*
- * State Machine
- * This parser controls the parser-state and returns any detected sequence to
- * the caller. The parser is based on this state-diagram from Paul Williams:
- *   http://vt100.net/emu/
- * It was written from scratch and extended where needed.
- * This parser is fully compatible up to the vt500 series. We expect UCS-4 as
- * input. It's the callers responsibility to do any UTF-8 parsing.
- */
-
-enum parser_state {
-        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 OSC sequence */
-        STATE_ST_IGNORE,        /* unimplemented seq; ignore until ST */
-        STATE_NUM
-};
-
-enum parser_action {
-        ACTION_NONE,            /* placeholder */
-        ACTION_CLEAR,           /* clear parameters */
-        ACTION_IGNORE,          /* ignore the character entirely */
-        ACTION_PRINT,           /* print the character on the console */
-        ACTION_EXECUTE,         /* execute single control character (C0/C1) */
-        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_CONSUME,     /* consume DCS terminator */
-        ACTION_DCS_DISPATCH,    /* dispatch dcs sequence */
-        ACTION_OSC_START,       /* start of OSC data */
-        ACTION_OSC_COLLECT,     /* collect OSC data */
-        ACTION_OSC_CONSUME,     /* consume OSC terminator */
-        ACTION_OSC_DISPATCH,    /* dispatch osc sequence */
-        ACTION_NUM
-};
-
-int term_parser_new(term_parser **out, bool host) {
-        _term_parser_free_ term_parser *parser = NULL;
-
-        assert_return(out, -EINVAL);
-
-        parser = new0(term_parser, 1);
-        if (!parser)
-                return -ENOMEM;
-
-        parser->is_host = host;
-        parser->st_alloc = 64;
-        parser->seq.st = new0(char, parser->st_alloc + 1);
-        if (!parser->seq.st)
-                return -ENOMEM;
-
-        *out = parser;
-        parser = NULL;
-        return 0;
-}
-
-term_parser *term_parser_free(term_parser *parser) {
-        if (!parser)
-                return NULL;
-
-        free(parser->seq.st);
-        free(parser);
-        return NULL;
-}
-
-static inline void parser_clear(term_parser *parser) {
-        unsigned int i;
-
-        parser->seq.command = TERM_CMD_NONE;
-        parser->seq.terminator = 0;
-        parser->seq.intermediates = 0;
-        parser->seq.charset = TERM_CHARSET_NONE;
-        parser->seq.n_args = 0;
-        for (i = 0; i < TERM_PARSER_ARG_MAX; ++i)
-                parser->seq.args[i] = -1;
-
-        parser->seq.n_st = 0;
-        parser->seq.st[0] = 0;
-}
-
-static int parser_ignore(term_parser *parser, uint32_t raw) {
-        parser_clear(parser);
-        parser->seq.type = TERM_SEQ_IGNORE;
-        parser->seq.command = TERM_CMD_NONE;
-        parser->seq.terminator = raw;
-        parser->seq.charset = TERM_CHARSET_NONE;
-
-        return parser->seq.type;
-}
-
-static int parser_print(term_parser *parser, uint32_t raw) {
-        parser_clear(parser);
-        parser->seq.type = TERM_SEQ_GRAPHIC;
-        parser->seq.command = TERM_CMD_GRAPHIC;
-        parser->seq.terminator = raw;
-        parser->seq.charset = TERM_CHARSET_NONE;
-
-        return parser->seq.type;
-}
-
-static int parser_execute(term_parser *parser, uint32_t raw) {
-        parser_clear(parser);
-        parser->seq.type = TERM_SEQ_CONTROL;
-        parser->seq.command = TERM_CMD_GRAPHIC;
-        parser->seq.terminator = raw;
-        parser->seq.charset = TERM_CHARSET_NONE;
-        if (!parser->is_host)
-                parser->seq.command = term_parse_host_control(&parser->seq);
-
-        return parser->seq.type;
-}
-
-static void parser_collect(term_parser *parser, uint32_t raw) {
-        /*
-         * Usually, characters from 0x30 to 0x3f are only allowed as leading
-         * markers (or as part of the parameters), characters from 0x20 to 0x2f
-         * are only allowed as trailing markers. However, our state-machine
-         * already verifies those restrictions so we can handle them the same
-         * way here. Note that we safely allow markers to be specified multiple
-         * times.
-         */
-
-        if (raw >= 0x20 && raw <= 0x3f)
-                parser->seq.intermediates |= 1 << (raw - 0x20);
-}
-
-static void parser_param(term_parser *parser, uint32_t raw) {
-        int new;
-
-        if (raw == ';') {
-                if (parser->seq.n_args < TERM_PARSER_ARG_MAX)
-                        ++parser->seq.n_args;
-
-                return;
-        }
-
-        if (parser->seq.n_args >= TERM_PARSER_ARG_MAX)
-                return;
-
-        if (raw >= '0' && raw <= '9') {
-                new = parser->seq.args[parser->seq.n_args];
-                if (new < 0)
-                        new = 0;
-                new = new * 10 + raw - '0';
-
-                /* VT510 tells us to clamp all values to [0, 9999], however, it
-                 * also allows commands with values up to 2^15-1. We simply use
-                 * 2^16 as maximum here to be compatible to all commands, but
-                 * avoid overflows in any calculations. */
-                if (new > 0xffff)
-                        new = 0xffff;
-
-                parser->seq.args[parser->seq.n_args] = new;
-        }
-}
-
-static int parser_esc(term_parser *parser, uint32_t raw) {
-        parser->seq.type = TERM_SEQ_ESCAPE;
-        parser->seq.command = TERM_CMD_NONE;
-        parser->seq.terminator = raw;
-        parser->seq.charset = TERM_CHARSET_NONE;
-        if (!parser->is_host)
-                parser->seq.command = term_parse_host_escape(&parser->seq, &parser->seq.charset);
-
-        return parser->seq.type;
-}
-
-static int parser_csi(term_parser *parser, uint32_t raw) {
-        /* parser->seq is cleared during CSI-ENTER state, thus there's no need
-         * to clear invalid fields here. */
-
-        if (parser->seq.n_args < TERM_PARSER_ARG_MAX) {
-                if (parser->seq.n_args > 0 ||
-                    parser->seq.args[parser->seq.n_args] >= 0)
-                        ++parser->seq.n_args;
-        }
-
-        parser->seq.type = TERM_SEQ_CSI;
-        parser->seq.command = TERM_CMD_NONE;
-        parser->seq.terminator = raw;
-        parser->seq.charset = TERM_CHARSET_NONE;
-        if (!parser->is_host)
-                parser->seq.command = term_parse_host_csi(&parser->seq);
-
-        return parser->seq.type;
-}
-
-/* perform state transition and dispatch related actions */
-static int parser_transition(term_parser *parser, uint32_t raw, unsigned int state, unsigned int action) {
-        if (state != STATE_NONE)
-                parser->state = state;
-
-        switch (action) {
-        case ACTION_NONE:
-                return TERM_SEQ_NONE;
-        case ACTION_CLEAR:
-                parser_clear(parser);
-                return TERM_SEQ_NONE;
-        case ACTION_IGNORE:
-                return parser_ignore(parser, raw);
-        case ACTION_PRINT:
-                return parser_print(parser, raw);
-        case ACTION_EXECUTE:
-                return parser_execute(parser, raw);
-        case ACTION_COLLECT:
-                parser_collect(parser, raw);
-                return TERM_SEQ_NONE;
-        case ACTION_PARAM:
-                parser_param(parser, raw);
-                return TERM_SEQ_NONE;
-        case ACTION_ESC_DISPATCH:
-                return parser_esc(parser, raw);
-        case ACTION_CSI_DISPATCH:
-                return parser_csi(parser, raw);
-        case ACTION_DCS_START:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_DCS_COLLECT:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_DCS_CONSUME:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_DCS_DISPATCH:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_OSC_START:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_OSC_COLLECT:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_OSC_CONSUME:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        case ACTION_OSC_DISPATCH:
-                /* not implemented */
-                return TERM_SEQ_NONE;
-        default:
-                assert_not_reached("invalid vte-parser action");
-                return TERM_SEQ_NONE;
-        }
-}
-
-static int parser_feed_to_state(term_parser *parser, uint32_t raw) {
-        switch (parser->state) {
-        case STATE_NONE:
-                /*
-                 * During initialization, parser->state is cleared. Treat this
-                 * as STATE_GROUND. We will then never get to STATE_NONE again.
-                 */
-        case STATE_GROUND:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                case 0x80 ... 0x9b:     /* C1 \ { ST } */
-                case 0x9d ... 0x9f:
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_PRINT);
-        case STATE_ESC:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
-                case 0x30 ... 0x4f:     /* ['0' - '~'] \ { 'P', 'X', '[', ']', '^', '_' } */
-                case 0x51 ... 0x57:
-                case 0x59 ... 0x5a:
-                case 0x5c:
-                case 0x60 ... 0x7e:
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
-                case 0x50:              /* 'P' */
-                        return parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
-                case 0x5b:              /* '[' */
-                        return parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
-                case 0x5d:              /* ']' */
-                        return parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
-                case 0x58:              /* 'X' */
-                case 0x5e:              /* '^' */
-                case 0x5f:              /* '_' */
-                        return parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
-        case STATE_ESC_INT:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
-                case 0x30 ... 0x7e:     /* ['0' - '~'] */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
-        case STATE_CSI_ENTRY:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
-                case 0x3a:              /* ':' */
-                        return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-                case 0x30 ... 0x39:     /* ['0' - '9'] */
-                case 0x3b:              /* ';' */
-                        return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_PARAM);
-                case 0x3c ... 0x3f:     /* ['<' - '?'] */
-                        return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_COLLECT);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-        case STATE_CSI_PARAM:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
-                case 0x30 ... 0x39:     /* ['0' - '9'] */
-                case 0x3b:              /* ';' */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
-                case 0x3a:              /* ':' */
-                case 0x3c ... 0x3f:     /* ['<' - '?'] */
-                        return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-        case STATE_CSI_INT:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
-                case 0x30 ... 0x3f:     /* ['0' - '?'] */
-                        return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
-        case STATE_CSI_IGNORE:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
-                case 0x20 ... 0x3f:     /* [' ' - '?'] */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
-        case STATE_DCS_ENTRY:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
-                case 0x3a:              /* ':' */
-                        return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
-                case 0x30 ... 0x39:     /* ['0' - '9'] */
-                case 0x3b:              /* ';' */
-                        return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_PARAM);
-                case 0x3c ... 0x3f:     /* ['<' - '?'] */
-                        return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_COLLECT);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-        case STATE_DCS_PARAM:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
-                case 0x30 ... 0x39:     /* ['0' - '9'] */
-                case 0x3b:              /* ';' */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
-                case 0x3a:              /* ':' */
-                case 0x3c ... 0x3f:     /* ['<' - '?'] */
-                        return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-        case STATE_DCS_INT:
-                switch (raw) {
-                case 0x00 ... 0x1f:     /* C0 */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x20 ... 0x2f:     /* [' ' - '\'] */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
-                case 0x30 ... 0x3f:     /* ['0' - '?'] */
-                        return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
-                case 0x40 ... 0x7e:     /* ['@' - '~'] */
-                        return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
-        case STATE_DCS_PASS:
-                switch (raw) {
-                case 0x00 ... 0x7e:     /* ASCII \ { DEL } */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
-                case 0x7f:              /* DEL */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_DCS_DISPATCH);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
-        case STATE_DCS_IGNORE:
-                switch (raw) {
-                case 0x00 ... 0x7f:     /* ASCII */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
-        case STATE_OSC_STRING:
-                switch (raw) {
-                case 0x00 ... 0x06:     /* C0 \ { BEL } */
-                case 0x08 ... 0x1f:
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x20 ... 0x7f:     /* [' ' - DEL] */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
-                case 0x07:              /* BEL */
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_OSC_DISPATCH);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
-        case STATE_ST_IGNORE:
-                switch (raw) {
-                case 0x00 ... 0x7f:     /* ASCII */
-                        return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
-                case 0x9c:              /* ST */
-                        return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                }
-
-                return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
-        }
-
-        assert_not_reached("bad vte-parser state");
-        return -EINVAL;
-}
-
-int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw) {
-        int r;
-
-        assert_return(parser, -EINVAL);
-        assert_return(seq_out, -EINVAL);
-
-        /*
-         * Notes:
-         *  * DEC treats GR codes as GL. We don't do that as we require UTF-8
-         *    as charset and, thus, it doesn't make sense to treat GR special.
-         *  * During control sequences, unexpected C1 codes cancel the sequence
-         *    and immediately start a new one. C0 codes, however, may or may not
-         *    be ignored/executed depending on the sequence.
-         */
-
-        switch (raw) {
-        case 0x18:              /* CAN */
-                r = parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
-                break;
-        case 0x1a:              /* SUB */
-                r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
-                break;
-        case 0x80 ... 0x8f:     /* C1 \ {DCS, SOS, CSI, ST, OSC, PM, APC} */
-        case 0x91 ... 0x97:
-        case 0x99 ... 0x9a:
-                r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
-                break;
-        case 0x1b:              /* ESC */
-                r = parser_transition(parser, raw, STATE_ESC, ACTION_CLEAR);
-                break;
-        case 0x98:              /* SOS */
-        case 0x9e:              /* PM */
-        case 0x9f:              /* APC */
-                r = parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
-                break;
-        case 0x90:              /* DCS */
-                r = parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
-                break;
-        case 0x9d:              /* OSC */
-                r = parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
-                break;
-        case 0x9b:              /* CSI */
-                r = parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
-                break;
-        default:
-                r = parser_feed_to_state(parser, raw);
-                break;
-        }
-
-        if (r <= 0)
-                *seq_out = NULL;
-        else
-                *seq_out = &parser->seq;
-
-        return r;
-}