diff options
| -rw-r--r-- | CHANGES | 14 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | input-keys.c | 78 | ||||
| -rw-r--r-- | input.c | 1494 | ||||
| -rw-r--r-- | local.c | 32 | ||||
| -rw-r--r-- | screen.c | 266 | ||||
| -rw-r--r-- | server-msg.c | 5 | ||||
| -rw-r--r-- | tmux.h | 77 | ||||
| -rw-r--r-- | window.c | 10 |
9 files changed, 1057 insertions, 925 deletions
@@ -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 $ @@ -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; + } + } +} @@ -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 ( |