/* $Id: input.c,v 1.4 2007-08-28 09:19:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott * * 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 #include #include #include #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, "" }, { KEYC_DC, "[3~" }, { KEYC_DOWN, "OB" }, { KEYC_F1, "OP" }, { KEYC_F10, "[21~" }, { KEYC_F11, "[23~" }, { KEYC_F12, "[24~" }, { KEYC_F2, "OQ" }, { KEYC_F3, "OR" }, { KEYC_F4, "OS" }, { KEYC_F5, "[15~" }, { KEYC_F6, "[17~" }, { KEYC_F7, "[18~" }, { KEYC_F8, "[19~" }, { KEYC_F9, "[20~" }, { KEYC_HOME, "[1~" }, { KEYC_IC, "[2~" }, { KEYC_LEFT, "OD" }, { KEYC_LL, "[4~" }, { KEYC_NPAGE, "[6~" }, { KEYC_PPAGE, "[5~" }, { KEYC_RIGHT, "OC" }, { KEYC_UP, "OA" } }; /* * 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) { char ch; u_char *ptr, *saved; const char *errstr; *code = 0; *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) 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; *code = *buf; return (buf - saved + 1); invalid: if (*argv != NULL) { xfree(*argv); *argv = NULL; } *argc = 0; /* Invalid. Consume until a valid terminator. */ while (len > 0) { if (*buf >= '@' && *buf <= '~') break; buf++; len--; } if (len == 0) goto incomplete; *code = '\0'; return (buf - saved + 1); incomplete: if (*argv != NULL) { xfree(*argv); *argv = NULL; } *argc = 0; *code = '\0'; return (0); } /* Translate a key code into an output key sequence. */ void input_key(struct buffer *b, int key) { struct input_key *ak; u_int i; log_debug("writing key %d", key); if (key != KEYC_NONE && key >= 0) { input_store8(b, key); return; } 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; } } } /* * 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) { u_char *saved, ch; size_t size; FILE *f; saved = buf; if (debug_level > 1) { f = fopen("tmux-in.log", "a+"); fwrite(buf, len, 1, f); fclose(f); } 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; } /* Read the first character. */ ch = *buf++; len--; /* Ignore delete. */ if (ch == '\177') { if (len == 0) { *--buf = '\e'; break; } ch = *buf++; len--; } /* Interpret C0 immediately. */ if (ch < ' ') { if (input_control(&buf, &len, b, s, ch) == 1) { *--buf = ch; break; } if (len == 0) { *--buf = '\e'; break; } ch = *buf++; len--; } /* * 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; } /* Handle two-character sequences. */ if (ch >= '0' && ch <= '?') { if (input_pair_private(&buf, &len, b, s, ch) == 1) goto incomplete; goto next; } if (ch >= '`' && ch <= '~') { if (input_pair_standard(&buf, &len, b, s, ch) == 1) goto incomplete; goto next; } if (ch >= '@' && ch <= '_' && ch != '[') { if (input_pair_control(&buf, &len, b, s, ch) == 1) goto incomplete; goto next; } /* 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); } return (buf - saved); incomplete: *--buf = ch; *--buf = '\e'; return (buf - saved); } /* Handle single control characters. */ int input_control(unused u_char **buf, unused size_t *len, struct buffer *b, struct screen *s, u_char ch) { switch (ch) { case '\0': /* NUL */ break; case '\n': /* LF */ case '\r': /* CR */ case '\010': /* BS */ log_debug("control: %hhu", ch); screen_character(s, ch); input_store8(b, ch); break; default: log_debug("unknown control: %c (%hhu)", ch, ch); break; } return (0); } /* 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) { log_debug("private2: %c (%hhu)", ch, ch); switch (ch) { case '=': /* DECKPAM */ input_store_zero(b, CODE_KKEYPADON); break; case '>': /* DECKPNM*/ input_store_zero(b, CODE_KKEYPADOFF); break; default: log_debug("unknown private2: %c (%hhu)", ch, ch); break; } return (0); } /* 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) { log_debug("unknown standard2: %c (%hhu)", ch, ch); return (0); } /* 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) { u_char *ptr; size_t size; log_debug("control2: %c (%hhu)", ch, ch); 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; /* A zero size means no ;, just skip the \007 and return. */ if (size == 0) { (*buf)++; (*len)--; break; } /* 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); } } /* 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; } return (0); } /* Translate a control sequence. */ int input_control_sequence( u_char **buf, size_t *len, struct buffer *b, struct screen *s) { u_char code, private; size_t used; uint16_t *argv, ua, ub; u_int argc, i; used = input_sequence(*buf, *len, &code, &private, &argv, &argc); if (used == 0) /* incomplete */ return (1); (*buf) += used; (*len) -= used; if (code == '\0') /* invalid */ return (-1); 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]); 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); break; case 'M': /* DL */ if (private != '\0') break; if (input_check_one(argv, argc, &ua, 1) != 0) break; if (ua == 0) break; input_store_one(b, CODE_DELETELINE, ua); break; case '@': /* ICH */ if (private != '\0') break; if (input_check_one(argv, argc, &ua, 1) != 0) break; if (ua == 0) break; input_store_one(b, CODE_INSERTCHARACTER, ua); break; case 'L': /* IL */ if (private != '\0') break; if (input_check_one(argv, argc, &ua, 1) != 0) break; if (ua == 0) break; input_store_one(b, CODE_INSERTLINE, ua); break; case 'd': /* VPA */ if (private != '\0') break; if (input_check_one(argv, argc, &ua, 1) != 0) break; if (ua == 0) break; input_store_two(b, CODE_CURSORMOVE, ua, s->cx + 1); break; case 'G': /* HPA */ if (private != '\0') break; if (input_check_one(argv, argc, &ua, 1) != 0) break; if (ua == 0) break; input_store_two(b, CODE_CURSORMOVE, s->cy + 1, ua); break; case 'H': /* CUP */ case 'f': /* HVP */ if (private != '\0') break; if (input_check_two(argv, argc, &ua, 1, &ub, 1) != 0) break; if (ua == 0 || ub == 0) break; input_store_two(b, CODE_CURSORMOVE, ua, ub); break; case 'J': /* ED */ if (private != '\0') break; if (input_check_one2(argv, argc, &ua, 0, 0, 2) != 0) break; switch (ua) { case 0: input_store_zero(b, CODE_CLEARENDOFSCREEN); break; case 2: input_store_zero(b, CODE_CLEARSCREEN); break; } break; case 'K': /* EL */ if (private != '\0') break; if (input_check_one2(argv, argc, &ua, 0, 0, 2) != 0) break; switch (ua) { case 0: input_store_zero(b, CODE_CLEARENDOFLINE); break; case 1: input_store_zero(b, CODE_CLEARSTARTOFLINE); break; case 2: input_store_zero(b, CODE_CLEARLINE); break; } break; case 'h': /* SM */ if (input_check_one(argv, argc, &ua, 0) != 0) break; switch (private) { case '?': switch (ua) { case 1: /* GATM */ input_store_zero(b, CODE_KCURSORON); break; case 25: /* TCEM */ input_store_zero(b, CODE_CURSORON); break; default: log_debug("unknown SM [%d]: %u", private, ua); } break; case '\0': switch (ua) { case 4: /* IRM */ input_store_zero(b, CODE_INSERTON); break; case 34: /* Cursor high visibility not supported. */ break; default: log_debug("unknown SM [%d]: %u", private, ua); break; } break; } break; case 'l': /* RM */ if (input_check_one(argv, argc, &ua, 0) != 0) break; switch (private) { case '?': switch (ua) { case 1: /* GATM */ input_store_zero(b, CODE_KCURSOROFF); break; case 25: /* TCEM */ input_store_zero(b, CODE_CURSOROFF); break; default: log_debug("unknown RM [%d]: %u", private, ua); } break; case '\0': switch (ua) { case 4: /* IRM */ input_store_zero(b, CODE_INSERTOFF); break; case 34: /* Cursor high visibility not supported. */ break; default: log_debug("unknown RM [%d]: %u", private, ua); break; } break; } break; case 'r': /* DECSTBM */ if (private != '\0') break; if (input_check_two(argv, argc, &ua, s->ry_upper + 1, &ub, s->ry_lower + 1) != 0) break; if (ua == 0 || ub == 0 || ub < ua) break; input_store_two(b, CODE_SCROLLREGION, ua, ub); break; case 'm': /* SGR */ input_store_zero(b, CODE_ATTRIBUTES); if (argc == 0) { input_store16(b, 1); input_store16(b, 0); } else { input_store16(b, argc); for (i = 0; i < argc; i++) { if (argv[i] == UINT16_MAX) argv[i] = 0; input_store16(b, argv[i]); } } break; default: log_debug("unknown sequence: %c (%hhu)", code, code); break; } if (argv != NULL) { xfree(argv); argv = NULL; } return (0); } /* Check for one argument. */ int input_check_one(uint16_t *argv, u_int argc, uint16_t *a, uint16_t ad) { *a = ad; if (argc == 1) { if (argv[0] != UINT16_MAX) *a = argv[0]; } else if (argc != 0) return (-1); return (0); } /* Check for one argument with limits. */ int input_check_one2(uint16_t *argv, u_int argc, uint16_t *a, uint16_t ad, uint16_t al, uint16_t au) { *a = ad; if (argc == 1) { if (argv[0] != UINT16_MAX) *a = argv[0]; } else if (argc != 0) return (-1); if (*a < al || *a > au) return (-1); return (0); } /* Check for two arguments. */ int input_check_two(uint16_t *argv, u_int argc, uint16_t *a, uint16_t ad, uint16_t *b, uint16_t bd) { *a = ad; *b = bd; if (argc == 1) { if (argv[0] != UINT16_MAX) *a = argv[0]; } else if (argc == 2) { if (argv[0] != UINT16_MAX) *a = argv[0]; if (argv[1] != UINT16_MAX) *b = argv[1]; } else if (argc != 0) return (-1); return (0); } /* Store a code without arguments. */ void input_store_zero(struct buffer *b, u_char code) { input_store8(b, '\e'); input_store8(b, code); } /* Store a code with a single argument. */ void input_store_one(struct buffer *b, u_char code, uint16_t ua) { input_store8(b, '\e'); input_store8(b, code); input_store16(b, ua); } /* Store a code with two arguments. */ void input_store_two(struct buffer *b, u_char code, uint16_t ua, uint16_t ub) { input_store8(b, '\e'); input_store8(b, code); input_store16(b, ua); input_store16(b, ub); } /* Write an 8-bit quantity to a buffer. */ void input_store8(struct buffer *b, uint8_t n) { buffer_write(b, &n, sizeof n); } /* Write a 16-bit argument to a buffer. */ void input_store16(struct buffer *b, uint16_t n) { buffer_write(b, &n, sizeof n); } /* Extract an 8-bit quantity from a buffer. */ uint8_t input_extract8(struct buffer *b) { uint8_t n; buffer_read(b, &n, sizeof n); return (n); } /* Extract a 16-bit argument from a pointer. */ uint16_t input_extract16(struct buffer *b) { uint16_t n; buffer_read(b, &n, sizeof n); return (n); }