mirror of
https://github.com/tmux/tmux.git
synced 2024-11-18 02:18:53 +00:00
828 lines
17 KiB
C
828 lines
17 KiB
C
/* $Id: input.c,v 1.5 2007-09-21 19:24:37 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"
|
|
|
|
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" }
|
|
};
|
|
|
|
/*
|
|
* 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);
|
|
}
|