summaryrefslogtreecommitdiff
path: root/src/libsystemd-terminal/term-parser.c
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2015-07-27 20:15:34 +0200
committerDavid Herrmann <dh.herrmann@gmail.com>2015-07-27 20:15:34 +0200
commitd537694a987bbb01e780bd5abe9412722fc38faa (patch)
tree596cb0594f289059368924d57d0f3f47368179b8 /src/libsystemd-terminal/term-parser.c
parent2d5c8a2756fec59d12aa0122359135653de1b8cb (diff)
terminal: drop unfinished code
This drops the libsystemd-terminal and systemd-consoled code for various reasons: * It's been sitting there unfinished for over a year now and won't get finished any time soon. * Since its initial creation, several parts need significant rework: The input handling should be replaced with the now commonly used libinput, the drm accessors should coordinate the handling of mode-object hotplugging (including split connectors) with other DRM users, and the internal library users should be converted to sd-device and friends. * There is still significant kernel work required before sd-console is really useful. This includes, but is not limited to, simpledrm and drmlog. * The authority daemon is needed before all this code can be used for real. And this will definitely take a lot more time to get done as no-one else is currently working on this, but me. * kdbus maintenance has taken up way more time than I thought and it has much higher priority. I don't see me spending much time on the terminal code in the near future. If anyone intends to hack on this, please feel free to contact me. I'll gladly help you out with any issues. Once kdbus and authorityd are finished (whenever that will be..) I'll definitely pick this up again. But until then, lets reduce compile times and maintenance efforts on this code and drop it for now.
Diffstat (limited to 'src/libsystemd-terminal/term-parser.c')
-rw-r--r--src/libsystemd-terminal/term-parser.c1702
1 files changed, 0 insertions, 1702 deletions
diff --git a/src/libsystemd-terminal/term-parser.c b/src/libsystemd-terminal/term-parser.c
deleted file mode 100644
index 8dc1da2f9c..0000000000
--- a/src/libsystemd-terminal/term-parser.c
+++ /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;
-}