aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES14
-rw-r--r--Makefile6
-rw-r--r--input-keys.c78
-rw-r--r--input.c1494
-rw-r--r--local.c32
-rw-r--r--screen.c266
-rw-r--r--server-msg.c5
-rw-r--r--tmux.h77
-rw-r--r--window.c10
9 files changed, 1057 insertions, 925 deletions
diff --git a/CHANGES b/CHANGES
index a07d365d..bbfd3d32 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,15 @@
28 September 2007
- * (mxey) Added window remaming, like "tmux rename [-s session] [-i index] name"
+
+* (nicm) Major rewrite of input parser:
+ - Lose the old weirdness in favour of a state machine.
+ - Merge in parsing from screen.c.
+ - Split key parsing off into a separate file.
+ This is step one towards hopefully allowing a status line. It requires
+ that we output data as if the terminal had one line less than it really does -
+ a serious problem when it comes to things like scrolling. This change
+ consolidates all the range checking and limiting together which should make
+ it easier.
+* (mxey) Added window remaming, like "tmux rename [-s session] [-i index] name"
27 September 2007
@@ -58,5 +68,5 @@
(including mutt, emacs). No status bar yet and no key remapping or other
customisation.
-$Id: CHANGES,v 1.12 2007-09-28 21:41:51 mxey Exp $
+$Id: CHANGES,v 1.13 2007-09-28 22:47:21 nicm Exp $
diff --git a/Makefile b/Makefile
index e9912ec6..723735c5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.6 2007-09-26 18:32:16 nicm Exp $
+# $Id: Makefile,v 1.7 2007-09-28 22:47:21 nicm Exp $
.SUFFIXES: .c .o .y .h
.PHONY: clean
@@ -17,8 +17,8 @@ DEBUG=
META?= \002 # C-b
SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c \
- xmalloc.c xmalloc-debug.c input.c screen.c window.c session.c local.c \
- log.c client.c client-msg.c client-cmd.c op.c op-list.c
+ xmalloc.c xmalloc-debug.c input.c input-keys.c screen.c window.c \
+ session.c local.c log.c client.c client-msg.c client-cmd.c op.c op-list.c
YACC= yacc -d
diff --git a/input-keys.c b/input-keys.c
new file mode 100644
index 00000000..b0eab60a
--- /dev/null
+++ b/input-keys.c
@@ -0,0 +1,78 @@
+/* $Id: input-keys.c,v 1.1 2007-09-28 22:47:21 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct {
+ int key;
+ const char *data;
+} input_keys[] = {
+/* { KEYC_BACKSPACE, "\010" }, */
+ { KEYC_DC, "\e[3~" },
+ { KEYC_DOWN, "\eOB" },
+ { KEYC_F1, "\eOP" },
+ { KEYC_F10, "\e[21~" },
+ { KEYC_F11, "\e[23~" },
+ { KEYC_F12, "\e[24~" },
+ { KEYC_F2, "\eOQ" },
+ { KEYC_F3, "\eOR" },
+ { KEYC_F4, "\eOS" },
+ { KEYC_F5, "\e[15~" },
+ { KEYC_F6, "\e[17~" },
+ { KEYC_F7, "\e[18~" },
+ { KEYC_F8, "\e[19~" },
+ { KEYC_F9, "\e[20~" },
+ { KEYC_HOME, "\e[1~" },
+ { KEYC_IC, "\e[2~" },
+ { KEYC_LEFT, "\eOD" },
+ { KEYC_LL, "\e[4~" },
+ { KEYC_NPAGE, "\e[6~" },
+ { KEYC_PPAGE, "\e[5~" },
+ { KEYC_RIGHT, "\eOC" },
+ { KEYC_UP, "\eOA" }
+};
+#define NINPUTKEYS (sizeof input_keys / sizeof input_keys[0])
+
+/* Translate a key code from client into an output key sequence. */
+void
+input_translate_key(struct buffer *b, int key)
+{
+ u_int i;
+
+ log_debug2("writing key %d", key);
+ if (key != KEYC_NONE && key >= 0) {
+ input_store8(b, key);
+ return;
+ }
+
+ for (i = 0; i < NINPUTKEYS; i++) {
+ if (input_keys[i].key == key) {
+ log_debug2(
+ "found key %d: \"%s\"", key, input_keys[i].data);
+ buffer_write(
+ b, input_keys[i].data, strlen(input_keys[i].data));
+ return;
+ }
+ }
+}
diff --git a/input.c b/input.c
index 6dc44ecc..01fe7f4e 100644
--- a/input.c
+++ b/input.c
@@ -1,4 +1,4 @@
-/* $Id: input.c,v 1.5 2007-09-21 19:24:37 nicm Exp $ */
+/* $Id: input.c,v 1.6 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -19,753 +19,999 @@
#include <sys/types.h>
#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
+#include <stdlib.h>
#include "tmux.h"
-size_t input_sequence(
- u_char *, size_t, u_char *, u_char *, uint16_t **, u_int *);
-int input_control(
- u_char **, size_t *, struct buffer *, struct screen *, u_char);
-int input_pair_private(
- u_char **, size_t *, struct buffer *, struct screen *, u_char);
-int input_pair_standard(
- u_char **, size_t *, struct buffer *, struct screen *, u_char);
-int input_pair_control(
- u_char **, size_t *, struct buffer *, struct screen *, u_char);
-int input_control_sequence(
- u_char **, size_t *, struct buffer *, struct screen *);
-int input_check_one(uint16_t *, u_int, uint16_t *, uint16_t);
-int input_check_one2(
- uint16_t *, u_int, uint16_t *, uint16_t, uint16_t, uint16_t);
-int input_check_two(
- uint16_t *, u_int, uint16_t *, uint16_t, uint16_t *, uint16_t);
-
-struct input_key {
- int key;
- const char *string;
-};
-
-struct input_key input_keys[] = {
-/* { KEYC_BACKSPACE, "\010" }, */
- { KEYC_DC, "\e[3~" },
- { KEYC_DOWN, "\eOB" },
- { KEYC_F1, "\eOP" },
- { KEYC_F10, "\e[21~" },
- { KEYC_F11, "\e[23~" },
- { KEYC_F12, "\e[24~" },
- { KEYC_F2, "\eOQ" },
- { KEYC_F3, "\eOR" },
- { KEYC_F4, "\eOS" },
- { KEYC_F5, "\e[15~" },
- { KEYC_F6, "\e[17~" },
- { KEYC_F7, "\e[18~" },
- { KEYC_F8, "\e[19~" },
- { KEYC_F9, "\e[20~" },
- { KEYC_HOME, "\e[1~" },
- { KEYC_IC, "\e[2~" },
- { KEYC_LEFT, "\eOD" },
- { KEYC_LL, "\e[4~" },
- { KEYC_NPAGE, "\e[6~" },
- { KEYC_PPAGE, "\e[5~" },
- { KEYC_RIGHT, "\eOC" },
- { KEYC_UP, "\eOA" }
+struct {
+ u_char first;
+ u_char last;
+ enum input_class class;
+} input_table[] = {
+ { 0x00, 0x1f, INPUT_C0CONTROL },
+ { 0x20, 0x20, INPUT_SPACE },
+ { 0x21, 0x2F, INPUT_INTERMEDIATE }, /* also INPUT_SPACE */
+ { 0x30, 0x3F, INPUT_PARAMETER },
+ { 0x40, 0x5F, INPUT_UPPERCASE },
+ { 0x60, 0x7E, INPUT_LOWERCASE },
+ { 0x7F, 0x7F, INPUT_DELETE },
+ { 0x80, 0x9F, INPUT_C1CONTROL },
+ { 0xA0, 0xA0, INPUT_SPACE },
+ { 0xA1, 0xFE, INPUT_G1DISPLAYABLE },
+ { 0xFF, 0xFF, INPUT_SPECIAL },
};
-
-/*
- * This parses CSI escape sequences into a code and a block of uint16_t
- * arguments. buf must be the next byte after the \e[ and len the remaining
- * data.
- */
-size_t
-input_sequence(u_char *buf, size_t len,
- u_char *code, u_char *private, uint16_t **argv, u_int *argc)
+#define NINPUTCLASS (sizeof input_table / sizeof input_table[0])
+
+enum input_class input_lookup_class(u_char);
+int input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t);
+
+void *input_state_first(u_char, enum input_class, struct input_ctx *);
+void *input_state_escape(u_char, enum input_class, struct input_ctx *);
+void *input_state_intermediate(u_char, enum input_class, struct input_ctx *);
+void *input_state_sequence_first(
+ u_char, enum input_class, struct input_ctx *);
+void *input_state_sequence_next(
+ u_char, enum input_class, struct input_ctx *);
+void *input_state_sequence_intermediate(
+ u_char, enum input_class, struct input_ctx *);
+
+void input_handle_character(u_char, struct input_ctx *);
+void input_handle_c0_control(u_char, struct input_ctx *);
+void input_handle_c1_control(u_char, struct input_ctx *);
+void input_handle_private_two(u_char, struct input_ctx *);
+void input_handle_standard_two(u_char, struct input_ctx *);
+void input_handle_sequence(u_char, struct input_ctx *);
+
+void input_handle_sequence_cuu(struct input_ctx *);
+void input_handle_sequence_cud(struct input_ctx *);
+void input_handle_sequence_cuf(struct input_ctx *);
+void input_handle_sequence_cub(struct input_ctx *);
+void input_handle_sequence_dch(struct input_ctx *);
+void input_handle_sequence_dl(struct input_ctx *);
+void input_handle_sequence_ich(struct input_ctx *);
+void input_handle_sequence_il(struct input_ctx *);
+void input_handle_sequence_vpa(struct input_ctx *);
+void input_handle_sequence_hpa(struct input_ctx *);
+void input_handle_sequence_cup(struct input_ctx *);
+void input_handle_sequence_cup(struct input_ctx *);
+void input_handle_sequence_ed(struct input_ctx *);
+void input_handle_sequence_el(struct input_ctx *);
+void input_handle_sequence_sm(struct input_ctx *);
+void input_handle_sequence_rm(struct input_ctx *);
+void input_handle_sequence_decstbm(struct input_ctx *);
+void input_handle_sequence_sgr(struct input_ctx *);
+
+enum input_class
+input_lookup_class(u_char ch)
{
- char ch;
- u_char *ptr, *saved;
- const char *errstr;
-
- *code = 0;
+ enum input_class iclass;
+ u_int i;
- *argc = 0;
- *argv = NULL;
-
- if (len == 0)
- return (0);
- saved = buf;
-
- /*
- * 0x3c (<) to 0x3f (?) mark private sequences when appear as the first
- * character.
- */
- *private = '\0';
- if (*buf >= '<' && *buf <= '?') {
- *private = *buf;
- buf++; len--;
- } else if (*buf < '0' || *buf > ';')
- goto complete;
-
- while (len > 0) {
- /*
- * Every parameter substring is bytes from 0x30 (0) to 0x3a (:),
- * terminated by 0x3b (;). 0x3a is an internal seperator.
- */
-
- /* Find the end of the substring. */
- ptr = buf;
- while (len != 0 && *ptr >= '0' && *ptr <= '9') {
- ptr++;
- len--;
- }
- if (len == 0)
+ iclass = INPUT_SPACE;
+ for (i = 0; i < NINPUTCLASS; i++) {
+ if (ch >= input_table[i].first && ch <= input_table[i].last) {
+ iclass = input_table[i].class;
break;
-
- /* An 0x3a is unsupported. */
- if (*ptr == ':')
- goto invalid;
-
- /* Create a new argument. */
- (*argc)++;
- *argv = xrealloc(*argv, *argc, sizeof **argv);
-
- /* Fill in argument value. */
- errstr = NULL;
- if (ptr == buf)
- (*argv)[*argc - 1] = UINT16_MAX;
- else {
- ch = *ptr; *ptr = '\0';
- (*argv)[*argc - 1] =
- strtonum(buf, 0, UINT16_MAX - 1, &errstr);
- *ptr = ch;
}
- buf = ptr;
-
- /* If the conversion had errors, abort now. */
- if (errstr != NULL)
- goto invalid;
-
- /* Break for any non-terminator. */
- if (*buf != ';')
- goto complete;
- buf++; len--;
}
- if (len == 0)
- goto incomplete;
-
-complete:
- /* Valid final characters are 0x40 (@) to 0x7e (~). */
- if (*buf < '@' || *buf > '~')
- goto invalid;
+ if (i == NINPUTCLASS)
+ fatalx("character without class");
- *code = *buf;
- return (buf - saved + 1);
+ return (iclass);
+}
-invalid:
- if (*argv != NULL) {
- xfree(*argv);
- *argv = NULL;
- }
- *argc = 0;
+int
+input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d)
+{
+ struct input_arg *arg;
+ char tmp[64];
+ const char *errstr;
- /* Invalid. Consume until a valid terminator. */
- while (len > 0) {
- if (*buf >= '@' && *buf <= '~')
- break;
- buf++; len--;
- }
- if (len == 0)
- goto incomplete;
+ *n = d;
+ if (i >= ARRAY_LENGTH(&ictx->args))
+ return (0);
- *code = '\0';
- return (buf - saved + 1);
+ arg = &ARRAY_ITEM(&ictx->args, i);
+ if (arg->len == 0)
+ return (0);
-incomplete:
- if (*argv != NULL) {
- xfree(*argv);
- *argv = NULL;
- }
- *argc = 0;
+ if (arg->len > sizeof tmp - 1)
+ return (-1);
+ memcpy(tmp, ictx->buf + arg->off, arg->len);
+ tmp[arg->len] = '\0';
- *code = '\0';
+ *n = strtonum(tmp, 0, UINT16_MAX, &errstr);
+ if (errstr != NULL)
+ return (-1);
return (0);
}
-/* Translate a key code into an output key sequence. */
void
-input_key(struct buffer *b, int key)
+input_init(struct input_ctx *ictx, struct screen *s)
{
- struct input_key *ak;
- u_int i;
+ ictx->s = s;
- log_debug("writing key %d", key);
- if (key != KEYC_NONE && key >= 0) {
- input_store8(b, key);
- return;
- }
+ ictx->intoff = 0;
+ ictx->intlen = 0;
- for (i = 0; i < (sizeof input_keys / sizeof input_keys[0]); i++) {
- ak = input_keys + i;
- if (ak->key == key) {
- log_debug("found key %d: \"%s\"", key, ak->string);
- buffer_write(b, ak->string, strlen(ak->string));
- return;
- }
- }
+ ARRAY_INIT(&ictx->args);
+
+ ictx->state = input_state_first;
+}
+
+void
+input_free(struct input_ctx *ictx)
+{
+ ARRAY_FREE(&ictx->args);
}
-/*
- * Parse a block of data and normalise escape sequences into a \e, a single
- * character code and the correct number of arguments. This includes adding
- * missing arguments and default values, and enforcing limits. Returns the
- * number of bytes consumed. The screen is updated with the data and used
- * to fill in current cursor positions and sizes.
- */
size_t
-input_parse(u_char *buf, size_t len, struct buffer *b, struct screen *s)
+input_parse(struct input_ctx *ictx, u_char *buf, size_t len, struct buffer *b)
{
- u_char *saved, ch;
- size_t size;
- FILE *f;
+ enum input_class iclass;
+ u_char ch;
- saved = buf;
+ ictx->buf = buf;
+ ictx->len = len;
+ ictx->off = 0;
- if (debug_level > 1) {
- f = fopen("tmux-in.log", "a+");
- fwrite(buf, len, 1, f);
- fclose(f);
+ ictx->b = b;
+
+ log_debug2("entry; buffer=%zu", ictx->len);
+
+ while (ictx->off < ictx->len) {
+ ch = ictx->buf[ictx->off++];
+ iclass = input_lookup_class(ch);
+ ictx->state = ictx->state(ch, iclass, ictx);
}
- while (len > 0) {
- ch = *buf++; len--;
-
- /* Handle control characters. */
- if (ch != '\e') {
- if (ch < ' ') {
- if (input_control(&buf, &len, b, s, ch) == 1) {
- *--buf = ch;
- break;
- }
- } else {
- log_debug("character: %c (%hhu)", ch, ch);
- screen_character(s, ch);
- input_store8(b, ch);
- }
- continue;
- }
- if (len == 0) {
- *--buf = '\e';
- break;
- }
+ return (ictx->len);
+}
- /* Read the first character. */
- ch = *buf++; len--;
+void *
+input_state_first(u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ log_debug2("first (%hhu); sx=%u, sy=%u, cx=%u, cy=%u",
+ ch, ictx->s->sx, ictx->s->sy, ictx->s->cx, ictx->s->cy);
+
+ switch (iclass) {
+ case INPUT_C0CONTROL:
+ if (ch == 0x1b)
+ return (input_state_escape);
+ input_handle_c0_control(ch, ictx);
+ break;
+ case INPUT_C1CONTROL:
+ ch -= 0x40;
+ if (ch == '[')
+ return (input_state_sequence_first);
+ input_handle_c1_control(ch, ictx);
+ break;
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ case INPUT_PARAMETER:
+ case INPUT_UPPERCASE:
+ case INPUT_LOWERCASE:
+ case INPUT_DELETE:
+ case INPUT_G1DISPLAYABLE:
+ case INPUT_SPECIAL:
+ input_handle_character(ch, ictx);
+ break;
+ }
+ return (input_state_first);
+}
- /* Ignore delete. */
- if (ch == '\177') {
- if (len == 0) {
- *--buf = '\e';
- break;
- }
- ch = *buf++; len--;
- }
+void *
+input_state_escape(u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ if (iclass == INPUT_C1CONTROL || iclass == INPUT_G1DISPLAYABLE) {
+ /* Treat as 7-bit equivalent. */
+ ch &= 0x7f;
+ iclass = input_lookup_class(ch);
+ }
- /* Interpret C0 immediately. */
- if (ch < ' ') {
- if (input_control(&buf, &len, b, s, ch) == 1) {
- *--buf = ch;
- break;
- }
+ switch (iclass) {
+ case INPUT_C0CONTROL:
+ input_handle_c0_control(ch, ictx);
+ return (input_state_escape);
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ ictx->intoff = ictx->off;
+ ictx->intlen = 1;
+ return (input_state_intermediate);
+ case INPUT_PARAMETER:
+ input_handle_private_two(ch, ictx);
+ break;
+ case INPUT_UPPERCASE:
+ if (ch == '[')
+ return (input_state_sequence_first);
+ input_handle_c1_control(ch, ictx);
+ break;
+ case INPUT_LOWERCASE:
+ input_handle_standard_two(ch, ictx);
+ break;
+ case INPUT_DELETE:
+ case INPUT_SPECIAL:
+ case INPUT_C1CONTROL:
+ case INPUT_G1DISPLAYABLE:
+ break;
+ }
+ return (input_state_first);
+}
- if (len == 0) {
- *--buf = '\e';
- break;
- }
- ch = *buf++; len--;
- }
+void *
+input_state_intermediate(
+ u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ switch (iclass) {
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ ictx->intlen++;
+ return (input_state_intermediate);
+ case INPUT_PARAMETER:
+ input_handle_private_two(ch, ictx);
+ break;
+ case INPUT_UPPERCASE:
+ case INPUT_LOWERCASE:
+ input_handle_standard_two(ch, ictx);
+ break;
+ case INPUT_C0CONTROL:
+ case INPUT_DELETE:
+ case INPUT_SPECIAL:
+ case INPUT_C1CONTROL:
+ case INPUT_G1DISPLAYABLE:
+ break;
+ }
+ return (input_state_first);
+}
- /*
- * Save used size to work out how much to pass to
- * screen_sequence later.
- */
- size = BUFFER_USED(b);
-
- /* Skip until the end of intermediate strings. */
- if (ch >= ' ' && ch <= '/') {
- while (len != 0) {
- if (ch >= 0x30 && ch <= 0x3f)
- break;
- if (ch >= 0x40 && ch <= 0x5f)
- break;
- ch = *buf++; len--;
- }
- continue;
+void *
+input_state_sequence_first(
+ u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ ictx->private = '\0';
+
+ switch (iclass) {
+ case INPUT_PARAMETER:
+ if (ch >= 0x3c && ch <= 0x3f) {
+ /* Private control sequence. */
+ ictx->private = ch;
+
+ ictx->saved = ictx->off;
+ return (input_state_sequence_next);
}
+ break;
+ case INPUT_C0CONTROL:
+ case INPUT_C1CONTROL:
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ case INPUT_UPPERCASE:
+ case INPUT_LOWERCASE:
+ case INPUT_DELETE:
+ case INPUT_G1DISPLAYABLE:
+ case INPUT_SPECIAL:
+ break;
+ }
- /* Handle two-character sequences. */
- if (ch >= '0' && ch <= '?') {
- if (input_pair_private(&buf, &len, b, s, ch) == 1)
- goto incomplete;
- goto next;
+ /* Pass this character to next state directly. */
+ ictx->saved = ictx->off - 1;
+ return (input_state_sequence_next(ch, iclass, ictx));
+}
+
+void *
+input_state_sequence_next(
+ u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ struct input_arg *iarg;
+
+ switch (iclass) {
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ if (ictx->saved != ictx->off) {
+ ARRAY_EXPAND(&ictx->args, 1);
+ iarg = &ARRAY_LAST(&ictx->args);
+ iarg->off = ictx->saved;
+ iarg->len = ictx->off - ictx->saved - 1;
}
- if (ch >= '`' && ch <= '~') {
- if (input_pair_standard(&buf, &len, b, s, ch) == 1)
- goto incomplete;
- goto next;
+ ictx->intoff = ictx->off;
+ ictx->intlen = 1;
+ return (input_state_sequence_intermediate);
+ case INPUT_PARAMETER:
+ if (ch == ';') {
+ ARRAY_EXPAND(&ictx->args, 1);
+ iarg = &ARRAY_LAST(&ictx->args);
+ iarg->off = ictx->saved;
+ iarg->len = ictx->off - ictx->saved - 1;
+
+ ictx->saved = ictx->off;
+ return (input_state_sequence_next);
}
- if (ch >= '@' && ch <= '_' && ch != '[') {
- if (input_pair_control(&buf, &len, b, s, ch) == 1)
- goto incomplete;
- goto next;
+ return (input_state_sequence_next);
+ case INPUT_UPPERCASE:
+ case INPUT_LOWERCASE:
+ if (ictx->saved != ictx->off) {
+ ARRAY_EXPAND(&ictx->args, 1);
+ iarg = &ARRAY_LAST(&ictx->args);
+ iarg->off = ictx->saved;
+ iarg->len = ictx->off - ictx->saved - 1;
}
+ input_handle_sequence(ch, ictx);
+ break;
+ case INPUT_C0CONTROL:
+ case INPUT_C1CONTROL:
+ case INPUT_DELETE:
+ case INPUT_SPECIAL:
+ case INPUT_G1DISPLAYABLE:
+ break;
+ }
+ return (input_state_first);
+}
- /* If not CSI at this point, invalid. */
- if (ch != '[')
- continue;
-
- if (input_control_sequence(&buf, &len, b, s) == 1)
- goto incomplete;
-
- next:
- size = BUFFER_USED(b) - size;
- log_debug("output is %zu bytes", size);
- if (size > 0) /* XXX only one command? */
- screen_sequence(s, BUFFER_IN(b) - size);
- log_debug("remaining data %zu bytes", len);
- }
+void *
+input_state_sequence_intermediate(
+ u_char ch, enum input_class iclass, struct input_ctx *ictx)
+{
+ switch (iclass) {
+ case INPUT_SPACE:
+ case INPUT_INTERMEDIATE:
+ ictx->intlen++;
+ return (input_state_sequence_intermediate);
+ case INPUT_UPPERCASE:
+ case INPUT_LOWERCASE:
+ input_handle_sequence(ch, ictx);
+ break;
+ case INPUT_PARAMETER:
+ case INPUT_C0CONTROL:
+ case INPUT_DELETE:
+ case INPUT_SPECIAL:
+ case INPUT_C1CONTROL:
+ case INPUT_G1DISPLAYABLE:
+ break;
+ }
+ return (input_state_first);
+}
- return (buf - saved);
+void
+input_handle_character(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch);
-incomplete:
- *--buf = ch;
- *--buf = '\e';
- return (buf - saved);
+ screen_write_character(ictx->s, ch);
+ input_store8(ictx->b, ch);
}
-/* Handle single control characters. */
-int
-input_control(unused u_char **buf, unused size_t *len,
- struct buffer *b, struct screen *s, u_char ch)
+void
+input_handle_c0_control(u_char ch, struct input_ctx *ictx)
{
+ log_debug2("-- c0 %zu: %hhu", ictx->off, ch);
+
switch (ch) {
case '\0': /* NUL */
break;
case '\n': /* LF */
+ screen_cursor_down_scroll(ictx->s);
+ break;
case '\r': /* CR */
+ ictx->s->cx = 0;
+ break;
case '\010': /* BS */
- log_debug("control: %hhu", ch);
- screen_character(s, ch);
- input_store8(b, ch);
+ if (ictx->s->cx > 0)
+ ictx->s->cx--;
break;
default:
- log_debug("unknown control: %c (%hhu)", ch, ch);
- break;
+ log_debug("unknown c0: %hhu", ch);
+ return;
}
+ input_store8(ictx->b, ch);
+}
- return (0);
+void
+input_handle_c1_control(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch);
+
+ /* XXX window title */
+ switch (ch) {
+ case 'M': /* RI */
+ screen_cursor_up_scroll(ictx->s);
+ input_store_zero(ictx->b, CODE_REVERSEINDEX);
+ break;
+ default:
+ log_debug("unknown c1: %hhu", ch);
+ break;
+ }
}
-/* Translate a private two-character sequence. */
-int
-input_pair_private(unused u_char **buf, unused size_t *len,
- unused struct buffer *b, unused struct screen *s, unused u_char ch)
+void
+input_handle_private_two(u_char ch, struct input_ctx *ictx)
{
- log_debug("private2: %c (%hhu)", ch, ch);
+ log_debug2("-- p2 %zu: %hhu (%c) (%zu, %zu)",
+ ictx->off, ch, ch, ictx->intoff, ictx->intlen);
switch (ch) {
case '=': /* DECKPAM */
- input_store_zero(b, CODE_KKEYPADON);
+ input_store_zero(ictx->b, CODE_KKEYPADON);
break;
case '>': /* DECKPNM*/
- input_store_zero(b, CODE_KKEYPADOFF);
+ input_store_zero(ictx->b, CODE_KKEYPADOFF);
break;
default:
- log_debug("unknown private2: %c (%hhu)", ch, ch);
+ log_debug("unknown p2: %hhu", ch);
break;
}
+}
- return (0);
+void
+input_handle_standard_two(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2("-- s2 %zu: %hhu (%c) (%zu,%zu)",
+ ictx->off, ch, ch, ictx->intoff, ictx->intlen);
+
+ log_debug("unknown s2: %hhu", ch);
}
-/* Translate a standard two-character sequence. */
-int
-input_pair_standard(unused u_char **buf, unused size_t *len,
- unused struct buffer *b, unused struct screen *s, u_char ch)
+void
+input_handle_sequence(u_char ch, struct input_ctx *ictx)
{
- log_debug("unknown standard2: %c (%hhu)", ch, ch);
+ static const struct {
+ u_char ch;
+ void (*fn)(struct input_ctx *);
+ } table[] = {
+ { 'A', input_handle_sequence_cuu },
+ { 'B', input_handle_sequence_cud },
+ { 'C', input_handle_sequence_cuf },
+ { 'D', input_handle_sequence_cub },
+ { 'P', input_handle_sequence_dch },
+ { 'M', input_handle_sequence_dl },
+ { '@', input_handle_sequence_ich },
+ { 'L', input_handle_sequence_il },
+ { 'd', input_handle_sequence_vpa },
+ { 'G', input_handle_sequence_hpa },
+ { 'H', input_handle_sequence_cup },
+ { 'f', input_handle_sequence_cup },
+ { 'J', input_handle_sequence_ed },
+ { 'K', input_handle_sequence_el },
+ { 'h', input_handle_sequence_sm },
+ { 'l', input_handle_sequence_rm },
+ { 'r', input_handle_sequence_decstbm },
+ { 'm', input_handle_sequence_sgr },
+ };
+ u_int i;
+ struct input_arg *iarg;
+
+ log_debug2("-- sq %zu: %hhu (%c) (%zu,%zu): %u",
+ ictx->off, ch, ch, ictx->intoff, ictx->intlen,
+ ARRAY_LENGTH(&ictx->args));
+ for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
+ iarg = &ARRAY_ITEM(&ictx->args, i);
+ if (iarg->len > 0) {
+ log_debug(" ++ %u: (%zu) %.*s", i,
+ iarg->len, (int) iarg->len, ictx->buf + iarg->off);
+ }
+ }
- return (0);
+ /* XXX bsearch? */
+ for (i = 0; i < (sizeof table / sizeof table[0]); i++) {
+ if (table[i].ch == ch) {
+ table[i].fn(ictx);
+ ARRAY_CLEAR(&ictx->args);
+ return;
+ }
+ }
+ ARRAY_CLEAR(&ictx->args);
+
+ log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private);
}
-/* Translate a control two-character sequence. */
-int
-input_pair_control(u_char **buf, size_t *len,
- struct buffer *b, unused struct screen *s, u_char ch)
+void
+input_handle_sequence_cuu(struct input_ctx *ictx)
{
- u_char *ptr;
- size_t size;
+ uint16_t n;
- log_debug("control2: %c (%hhu)", ch, ch);
+ if (ictx->private != '\0')
+ return;
- switch (ch) {
- case ']': /* window title */
- if (*len < 3)
- return (1);
- ch = *(*buf)++; (*len)--;
-
- /*
- * Search MAXTITLELEN + 1 to allow space for the ;. The
- * \007 is also included, but space is needed for a \0 so
- * it doesn't need to be compensated for.
- */
- size = *len > MAXTITLELEN + 1 ? MAXTITLELEN + 1 : *len;
- if ((ptr = memchr(*buf, '\007', size)) == NULL) {
- log_debug("title not found in %zu bytes", size);
- if (*len >= MAXTITLELEN + 1)
- break;
- (*buf)--; (*len)++;
- return (1);
- }
- size = ptr - *buf;
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
- /* A zero size means no ;, just skip the \007 and return. */
- if (size == 0) {
- (*buf)++; (*len)--;
- break;
- }
+ if (n == 0 || n > ictx->s->cy) {
+ log_debug3("cuu: out of range: %hu", n);
+ return;
+ }
- /* Set the title if appropriate. */
- if (**buf == ';' && (ch == '0' || ch == '1')) {
- log_debug("title found, length %zu bytes: %.*s",
- size - 1, (int) size - 1, *buf + 1);
- if (size > 1) {
- input_store_one(b, CODE_TITLE, size - 1);
- buffer_write(b, *buf + 1, size - 1);
- }
- }
+ ictx->s->cy -= n;
+ input_store_one(ictx->b, CODE_CURSORUP, n);
+}
- /* Skip the title; add one for the \007. */
- (*buf) += size + 1;
- (*len) -= size + 1;
- break;
- case 'M': /* RI */
- input_store_zero(b, CODE_REVERSEINDEX);
- break;
- default:
- log_debug("unknown control2: %c (%hhu)", ch, ch);
- break;
+void
+input_handle_sequence_cud(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+
+ if (n == 0 || n > ictx->s->sy - ictx->s->cy - 1) {
+ log_debug3("cud: out of range: %hu", n);
+ return;
}
- return (0);
+ ictx->s->cy += n;
+ input_store_one(ictx->b, CODE_CURSORDOWN, n);
}
-/* Translate a control sequence. */
-int
-input_control_sequence(
- u_char **buf, size_t *len, struct buffer *b, struct screen *s)
+void
+input_handle_sequence_cuf(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+
+ if (n == 0 || n > ictx->s->sx - ictx->s->cx - 1) {
+ log_debug3("cuf: out of range: %hu", n);
+ return;
+ }
+
+ ictx->s->cx += n;
+ input_store_one(ictx->b, CODE_CURSORRIGHT, n);
+}
+
+void
+input_handle_sequence_cub(struct input_ctx *ictx)
{
- u_char code, private;
- size_t used;
- uint16_t *argv, ua, ub;
- u_int argc, i;
+ uint16_t n;
- used = input_sequence(*buf, *len, &code, &private, &argv, &argc);
- if (used == 0) /* incomplete */
- return (1);
+ if (ictx->private != '\0')
+ return;
- (*buf) += used;
- (*len) -= used;
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
- if (code == '\0') /* invalid */
- return (-1);
+ if (n == 0 || n > ictx->s->cx) {
+ log_debug3("cub: out of range: %hu", n);
+ return;
+ }
- log_debug(
- "sequence: %c (%hhu) [%c] [cx %u, cy %u, sx %u, sy %u]: %u", code,
- code, private, s->cx, s->cy, s->sx, s->sy, argc);
- for (i = 0; i < argc; i++)
- log_debug("\targument %u: %u", i, argv[i]);
+ ictx->s->cx -= n;
+ input_store_one(ictx->b, CODE_CURSORLEFT, n);
+}
- switch (code) {
- case 'A': /* CUU */
- if (private != '\0')
- break;
- if (input_check_one(argv, argc, &ua, 1) != 0)
- break;
- if (ua == 0)
- break;
- input_store_one(b, CODE_CURSORUP, ua);
- break;
- case 'B': /* CUD */
- if (private != '\0')
- break;
- if (input_check_one(argv, argc, &ua, 1) != 0)
- break;
- if (ua == 0)
- break;
- input_store_one(b, CODE_CURSORDOWN, ua);
- break;
- case 'C': /* CUF */
- if (private != '\0')
- break;
- if (input_check_one(argv, argc, &ua, 1) != 0)
- break;
- if (ua == 0)
- break;
- input_store_one(b, CODE_CURSORRIGHT, ua);
- break;
- case 'D': /* CUB */
- if (private != '\0')
- break;
- if (input_check_one(argv, argc, &ua, 1) != 0)
- break;
- if (ua == 0)
- break;
- input_store_one(b, CODE_CURSORLEFT, ua);
- break;
- case 'P': /* DCH */
- if (private != '\0')
- break;
- if (input_check_one(argv, argc, &ua, 1) != 0)
- break;
- if (ua == 0)
- break;
- input_store_one(b, CODE_DELETECHARACTER, ua);
+void
+input_handle_sequence_dch(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+
+ if (n == 0 || n > ictx->s->sx - ictx->s->cx - 1) {
+ log_debug3("dch: out of range: %hu", n);
+ return;
+ }
+
+ screen_delete_characters(ictx->s, ictx->s->cx, ictx->s->cy, n);
+ input_store_one(ictx->b, CODE_DELETECHARACTER, n);
+}
+
+void
+input_handle_sequence_dl(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+
+ if (n == 0 || n > ictx->s->sy - ictx->s->cy - 1) {
+ log_debug3("dl: out of range: %hu", n);
+ return;
+ }
+
+ screen_delete_lines(ictx->s, ictx->s->cy, n);
+ input_store_one(ictx->b, CODE_DELETELINE, n);
+}
+
+void
+input_handle_sequence_ich(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (