input.c revision 1.4
1/* $OpenBSD: input.c,v 1.4 2009/06/04 14:15:50 nicm Exp $ */
2
3/*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "tmux.h"
26
27#define INPUT_C0CONTROL(ch) 	(ch <= 0x1f)
28#define INPUT_INTERMEDIATE(ch)	(ch == 0xa0 || (ch >= 0x20 && ch <= 0x2f))
29#define INPUT_PARAMETER(ch)	(ch >= 0x30 && ch <= 0x3f)
30#define INPUT_UPPERCASE(ch)	(ch >= 0x40 && ch <= 0x5f)
31#define INPUT_LOWERCASE(ch)	(ch >= 0x60 && ch <= 0x7e)
32#define INPUT_DELETE(ch)	(ch == 0x7f)
33#define INPUT_C1CONTROL(ch)	(ch >= 0x80 && ch <= 0x9f)
34#define INPUT_G1DISPLAYABLE(ch)	(ch >= 0xa1 && ch <= 0xfe)
35#define INPUT_SPECIAL(ch)	(ch == 0xff)
36
37int	 input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t);
38int	 input_new_argument(struct input_ctx *);
39int	 input_add_argument(struct input_ctx *, u_char);
40
41void	 input_start_string(struct input_ctx *, int);
42void	 input_abort_string(struct input_ctx *);
43int	 input_add_string(struct input_ctx *, u_char);
44char	*input_get_string(struct input_ctx *);
45
46void	 input_state(struct input_ctx *, void *);
47
48void	 input_state_first(u_char, struct input_ctx *);
49void	 input_state_escape(u_char, struct input_ctx *);
50void	 input_state_intermediate(u_char, struct input_ctx *);
51void	 input_state_sequence_first(u_char, struct input_ctx *);
52void	 input_state_sequence_next(u_char, struct input_ctx *);
53void	 input_state_sequence_intermediate(u_char, struct input_ctx *);
54void	 input_state_string_next(u_char, struct input_ctx *);
55void	 input_state_string_escape(u_char, struct input_ctx *);
56void	 input_state_utf8(u_char, struct input_ctx *);
57
58void	 input_handle_character(u_char, struct input_ctx *);
59void	 input_handle_c0_control(u_char, struct input_ctx *);
60void	 input_handle_c1_control(u_char, struct input_ctx *);
61void	 input_handle_private_two(u_char, struct input_ctx *);
62void	 input_handle_standard_two(u_char, struct input_ctx *);
63void	 input_handle_sequence(u_char, struct input_ctx *);
64
65void	 input_handle_sequence_cuu(struct input_ctx *);
66void	 input_handle_sequence_cud(struct input_ctx *);
67void	 input_handle_sequence_cuf(struct input_ctx *);
68void	 input_handle_sequence_cub(struct input_ctx *);
69void	 input_handle_sequence_dch(struct input_ctx *);
70void	 input_handle_sequence_dl(struct input_ctx *);
71void	 input_handle_sequence_ich(struct input_ctx *);
72void	 input_handle_sequence_il(struct input_ctx *);
73void	 input_handle_sequence_vpa(struct input_ctx *);
74void	 input_handle_sequence_hpa(struct input_ctx *);
75void	 input_handle_sequence_cup(struct input_ctx *);
76void	 input_handle_sequence_cup(struct input_ctx *);
77void	 input_handle_sequence_ed(struct input_ctx *);
78void	 input_handle_sequence_el(struct input_ctx *);
79void	 input_handle_sequence_sm(struct input_ctx *);
80void	 input_handle_sequence_rm(struct input_ctx *);
81void	 input_handle_sequence_decstbm(struct input_ctx *);
82void	 input_handle_sequence_sgr(struct input_ctx *);
83void	 input_handle_sequence_dsr(struct input_ctx *);
84
85int	 input_sequence_cmp(const void *, const void *);
86
87struct input_sequence_entry {
88	u_char	ch;
89	void	(*fn)(struct input_ctx *);
90};
91const struct input_sequence_entry input_sequence_table[] = {
92	{ '@', input_handle_sequence_ich },
93	{ 'A', input_handle_sequence_cuu },
94	{ 'B', input_handle_sequence_cud },
95	{ 'C', input_handle_sequence_cuf },
96	{ 'D', input_handle_sequence_cub },
97	{ 'G', input_handle_sequence_hpa },
98	{ 'H', input_handle_sequence_cup },
99	{ 'J', input_handle_sequence_ed },
100	{ 'K', input_handle_sequence_el },
101	{ 'L', input_handle_sequence_il },
102	{ 'M', input_handle_sequence_dl },
103	{ 'P', input_handle_sequence_dch },
104	{ 'd', input_handle_sequence_vpa },
105	{ 'f', input_handle_sequence_cup },
106	{ 'h', input_handle_sequence_sm },
107	{ 'l', input_handle_sequence_rm },
108	{ 'm', input_handle_sequence_sgr },
109	{ 'n', input_handle_sequence_dsr },
110	{ 'r', input_handle_sequence_decstbm },
111};
112
113int
114input_sequence_cmp(const void *a, const void *b)
115{
116	int	ai = ((const struct input_sequence_entry *) a)->ch;
117	int	bi = ((const struct input_sequence_entry *) b)->ch;
118
119	return (ai - bi);
120}
121
122int
123input_new_argument(struct input_ctx *ictx)
124{
125	struct input_arg       *arg;
126
127	ARRAY_EXPAND(&ictx->args, 1);
128
129	arg = &ARRAY_LAST(&ictx->args);
130	arg->used = 0;
131
132	return (0);
133}
134
135int
136input_add_argument(struct input_ctx *ictx, u_char ch)
137{
138	struct input_arg       *arg;
139
140	if (ARRAY_LENGTH(&ictx->args) == 0)
141		return (0);
142
143	arg = &ARRAY_LAST(&ictx->args);
144	if (arg->used > (sizeof arg->data) - 1)
145		return (-1);
146	arg->data[arg->used++] = ch;
147
148	return (0);
149}
150
151int
152input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d)
153{
154	struct input_arg	*arg;
155	const char		*errstr;
156
157	*n = d;
158	if (i >= ARRAY_LENGTH(&ictx->args))
159		return (0);
160
161	arg = &ARRAY_ITEM(&ictx->args, i);
162	if (*arg->data == '\0')
163		return (0);
164
165	*n = strtonum(arg->data, 0, UINT16_MAX, &errstr);
166	if (errstr != NULL)
167		return (-1);
168	return (0);
169}
170
171void
172input_start_string(struct input_ctx *ictx, int type)
173{
174	ictx->string_type = type;
175	ictx->string_len = 0;
176}
177
178void
179input_abort_string(struct input_ctx *ictx)
180{
181	if (ictx->string_buf != NULL)
182		xfree(ictx->string_buf);
183	ictx->string_buf = NULL;
184}
185
186int
187input_add_string(struct input_ctx *ictx, u_char ch)
188{
189	ictx->string_buf = xrealloc(ictx->string_buf, 1, ictx->string_len + 1);
190	ictx->string_buf[ictx->string_len++] = ch;
191
192	if (ictx->string_len >= MAXSTRINGLEN) {
193		input_abort_string(ictx);
194		return (1);
195	}
196
197	return (0);
198}
199
200char *
201input_get_string(struct input_ctx *ictx)
202{
203	char	*s;
204
205	if (ictx->string_buf == NULL || input_add_string(ictx, '\0') != 0)
206		return (xstrdup(""));
207
208	s = ictx->string_buf;
209	ictx->string_buf = NULL;
210	return (s);
211}
212
213void
214input_state(struct input_ctx *ictx, void *state)
215{
216	ictx->state = state;
217}
218
219void
220input_init(struct window_pane *wp)
221{
222	struct input_ctx	*ictx = &wp->ictx;
223
224	ARRAY_INIT(&ictx->args);
225
226	ictx->string_len = 0;
227	ictx->string_buf = NULL;
228
229 	memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
230
231	memcpy(&ictx->saved_cell, &grid_default_cell, sizeof ictx->saved_cell);
232	ictx->saved_cx = 0;
233	ictx->saved_cy = 0;
234
235	input_state(ictx, input_state_first);
236}
237
238void
239input_free(struct window_pane *wp)
240{
241	if (wp->ictx.string_buf != NULL)
242		xfree(wp->ictx.string_buf);
243
244	ARRAY_FREE(&wp->ictx.args);
245}
246
247void
248input_parse(struct window_pane *wp)
249{
250	struct input_ctx	*ictx = &wp->ictx;
251	u_char			 ch;
252
253	if (BUFFER_USED(wp->in) == 0)
254		return;
255
256	ictx->buf = BUFFER_OUT(wp->in);
257	ictx->len = BUFFER_USED(wp->in);
258	ictx->off = 0;
259
260	ictx->wp = wp;
261
262	log_debug2("entry; buffer=%zu", ictx->len);
263
264	if (wp->mode == NULL)
265		screen_write_start(&ictx->ctx, wp, &wp->base);
266	else
267		screen_write_start(&ictx->ctx, NULL, &wp->base);
268
269	if (ictx->off != ictx->len)
270		wp->window->flags |= WINDOW_ACTIVITY;
271	while (ictx->off < ictx->len) {
272		ch = ictx->buf[ictx->off++];
273		ictx->state(ch, ictx);
274	}
275
276	screen_write_stop(&ictx->ctx);
277
278	buffer_remove(wp->in, ictx->len);
279}
280
281void
282input_state_first(u_char ch, struct input_ctx *ictx)
283{
284	ictx->intermediate = '\0';
285
286	if (INPUT_C0CONTROL(ch)) {
287		if (ch == 0x1b)
288			input_state(ictx, input_state_escape);
289		else
290			input_handle_c0_control(ch, ictx);
291		return;
292	}
293
294#if 0
295  	if (INPUT_C1CONTROL(ch)) {
296		ch -= 0x40;
297		if (ch == '[')
298			input_state(ictx, input_state_sequence_first);
299		else if (ch == ']') {
300			input_start_string(ictx, STRING_SYSTEM);
301			input_state(ictx, input_state_string_next);
302		} else if (ch == '_') {
303			input_start_string(ictx, STRING_APPLICATION);
304			input_state(ictx, input_state_string_next);
305		} else
306			input_handle_c1_control(ch, ictx);
307		return;
308	}
309#endif
310
311	if (INPUT_DELETE(ch))
312		return;
313
314	input_handle_character(ch, ictx);
315}
316
317void
318input_state_escape(u_char ch, struct input_ctx *ictx)
319{
320	/* Treat C1 control and G1 displayable as 7-bit equivalent. */
321	if (INPUT_C1CONTROL(ch) || INPUT_G1DISPLAYABLE(ch))
322		ch &= 0x7f;
323
324	if (INPUT_C0CONTROL(ch)) {
325		input_handle_c0_control(ch, ictx);
326		return;
327	}
328
329	if (INPUT_INTERMEDIATE(ch)) {
330		log_debug2(":: in1 %zu: %hhu (%c)", ictx->off, ch, ch);
331		ictx->intermediate = ch;
332		input_state(ictx, input_state_intermediate);
333		return;
334	}
335
336	if (INPUT_PARAMETER(ch)) {
337		input_state(ictx, input_state_first);
338		input_handle_private_two(ch, ictx);
339		return;
340	}
341
342	if (INPUT_UPPERCASE(ch)) {
343		if (ch == '[')
344			input_state(ictx, input_state_sequence_first);
345		else if (ch == ']') {
346			input_start_string(ictx, STRING_SYSTEM);
347			input_state(ictx, input_state_string_next);
348		} else if (ch == '_') {
349			input_start_string(ictx, STRING_APPLICATION);
350			input_state(ictx, input_state_string_next);
351		} else {
352			input_state(ictx, input_state_first);
353			input_handle_c1_control(ch, ictx);
354		}
355		return;
356	}
357
358	if (INPUT_LOWERCASE(ch)) {
359		input_state(ictx, input_state_first);
360		input_handle_standard_two(ch, ictx);
361		return;
362	}
363
364	input_state(ictx, input_state_first);
365}
366
367void
368input_state_intermediate(u_char ch, struct input_ctx *ictx)
369{
370	if (INPUT_INTERMEDIATE(ch)) {
371		/* Multiple intermediates currently ignored. */
372		log_debug2(":: in2 %zu: %hhu (%c)", ictx->off, ch, ch);
373		return;
374	}
375
376	if (INPUT_PARAMETER(ch)) {
377		input_state(ictx, input_state_first);
378		input_handle_private_two(ch, ictx);
379		return;
380	}
381
382	if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
383		input_state(ictx, input_state_first);
384		input_handle_standard_two(ch, ictx);
385		return;
386	}
387
388	input_state(ictx, input_state_first);
389}
390
391void
392input_state_sequence_first(u_char ch, struct input_ctx *ictx)
393{
394	ictx->private = '\0';
395	ARRAY_CLEAR(&ictx->args);
396
397	/* Most C0 control are accepted within CSI. */
398	if (INPUT_C0CONTROL(ch)) {
399		if (ch == 0x1b) {			/* ESC */
400			/* Abort sequence and begin with new. */
401			input_state(ictx, input_state_escape);
402		} else if (ch != 0x18 && ch != 0x1a) {	/* CAN and SUB */
403			/* Handle C0 immediately. */
404			input_handle_c0_control(ch, ictx);
405		}
406		/*
407		 * Just come back to this state, in case the next character
408		 * is the start of a private sequence.
409		 */
410		return;
411	}
412
413	input_state(ictx, input_state_sequence_next);
414
415	/* Private sequence: always the first character. */
416	if (ch >= 0x3c && ch <= 0x3f) {
417		ictx->private = ch;
418		return;
419	}
420
421	/* Pass character on directly. */
422	input_state_sequence_next(ch, ictx);
423}
424
425void
426input_state_sequence_next(u_char ch, struct input_ctx *ictx)
427{
428	if (INPUT_INTERMEDIATE(ch)) {
429		if (input_add_argument(ictx, '\0') != 0)
430			input_state(ictx, input_state_first);
431		else {
432			log_debug2(":: si1 %zu: %hhu (%c)", ictx->off, ch, ch);
433			input_state(ictx, input_state_sequence_intermediate);
434		}
435		return;
436	}
437
438	if (INPUT_PARAMETER(ch)) {
439		if (ARRAY_EMPTY(&ictx->args))
440			input_new_argument(ictx);
441
442		if (ch == ';') {
443			if (input_add_argument(ictx, '\0') != 0)
444				input_state(ictx, input_state_first);
445			else
446				input_new_argument(ictx);
447		} else if (input_add_argument(ictx, ch) != 0)
448			input_state(ictx, input_state_first);
449		return;
450	}
451
452	if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
453		if (input_add_argument(ictx, '\0') != 0)
454			input_state(ictx, input_state_first);
455		else {
456			input_state(ictx, input_state_first);
457			input_handle_sequence(ch, ictx);
458		}
459		return;
460	}
461
462	/* Most C0 control are accepted within CSI. */
463	if (INPUT_C0CONTROL(ch)) {
464		if (ch == 0x1b) {			/* ESC */
465			/* Abort sequence and begin with new. */
466			input_state(ictx, input_state_escape);
467		} else if (ch != 0x18 && ch != 0x1a) {	/* CAN and SUB */
468			/* Handle C0 immediately. */
469			input_handle_c0_control(ch, ictx);
470		}
471		return;
472	}
473
474	input_state(ictx, input_state_first);
475}
476
477void
478input_state_sequence_intermediate(u_char ch, struct input_ctx *ictx)
479{
480	if (INPUT_INTERMEDIATE(ch)) {
481		log_debug2(":: si2 %zu: %hhu (%c)", ictx->off, ch, ch);
482		return;
483	}
484
485	if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
486		input_state(ictx, input_state_first);
487		input_handle_sequence(ch, ictx);
488		return;
489	}
490
491	input_state(ictx, input_state_first);
492}
493
494void
495input_state_string_next(u_char ch, struct input_ctx *ictx)
496{
497	if (ch == 0x1b) {
498		input_state(ictx, input_state_string_escape);
499		return;
500	}
501	if (ch == 0x07) {
502		input_state_string_escape(ch, ictx);
503		return;
504	}
505
506	if (ch >= 0x20) {
507		if (input_add_string(ictx, ch) != 0)
508			input_state(ictx, input_state_first);
509		return;
510	}
511}
512
513void
514input_state_string_escape(u_char ch, struct input_ctx *ictx)
515{
516	char	*s;
517
518	if (ch == '\007' || ch == '\\') {
519		input_state(ictx, input_state_first);
520		switch (ictx->string_type) {
521		case STRING_SYSTEM:
522			if (ch != '\007')
523				return;
524			s = input_get_string(ictx);
525			if ((s[0] != '0' && s[0] != '2') || s[1] != ';') {
526				xfree(s);
527				return;
528			}
529			screen_set_title(ictx->ctx.s, s + 2);
530			server_status_window(ictx->wp->window);
531			xfree(s);
532			break;
533		case STRING_APPLICATION:
534			if (ch != '\\')
535				return;
536			s = input_get_string(ictx);
537			screen_set_title(ictx->ctx.s, s);
538			server_status_window(ictx->wp->window);
539			xfree(s);
540			break;
541		case STRING_NAME:
542			if (ch != '\\')
543				return;
544			xfree(ictx->wp->window->name);
545			ictx->wp->window->name = input_get_string(ictx);
546			server_status_window(ictx->wp->window);
547			break;
548		}
549		return;
550	}
551
552	input_state(ictx, input_state_string_next);
553	input_state_string_next(ch, ictx);
554}
555
556void
557input_state_utf8(u_char ch, struct input_ctx *ictx)
558{
559	log_debug2("-- un %zu: %hhu (%c)", ictx->off, ch, ch);
560
561	ictx->utf8_buf[ictx->utf8_off++] = ch;
562	if (--ictx->utf8_len != 0)
563		return;
564	input_state(ictx, input_state_first);
565
566	ictx->cell.flags |= GRID_FLAG_UTF8;
567	screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf);
568	ictx->cell.flags &= ~GRID_FLAG_UTF8;
569}
570
571void
572input_handle_character(u_char ch, struct input_ctx *ictx)
573{
574	struct window_pane	*wp = ictx->wp;
575
576	if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) {
577		/*
578		 * UTF-8 sequence.
579		 *
580		 * 11000010-11011111 C2-DF start of 2-byte sequence
581		 * 11100000-11101111 E0-EF start of 3-byte sequence
582		 * 11110000-11110100 F0-F4 start of 4-byte sequence
583		 */
584		memset(ictx->utf8_buf, 0xff, sizeof ictx->utf8_buf);
585		ictx->utf8_buf[0] = ch;
586		ictx->utf8_off = 1;
587
588		if (ch >= 0xc2 && ch <= 0xdf) {
589			log_debug2("-- u2 %zu: %hhu (%c)", ictx->off, ch, ch);
590			input_state(ictx, input_state_utf8);
591			ictx->utf8_len = 1;
592			return;
593		}
594		if (ch >= 0xe0 && ch <= 0xef) {
595			log_debug2("-- u3 %zu: %hhu (%c)", ictx->off, ch, ch);
596			input_state(ictx, input_state_utf8);
597			ictx->utf8_len = 2;
598			return;
599		}
600		if (ch >= 0xf0 && ch <= 0xf4) {
601			log_debug2("-- u4 %zu: %hhu (%c)", ictx->off, ch, ch);
602			input_state(ictx, input_state_utf8);
603			ictx->utf8_len = 3;
604			return;
605		}
606	}
607	log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch);
608
609	ictx->cell.data = ch;
610	screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf);
611}
612
613void
614input_handle_c0_control(u_char ch, struct input_ctx *ictx)
615{
616	struct screen	*s = ictx->ctx.s;
617
618	log_debug2("-- c0 %zu: %hhu", ictx->off, ch);
619
620	switch (ch) {
621	case '\0':	/* NUL */
622		break;
623	case '\n':	/* LF */
624		screen_write_linefeed(&ictx->ctx);
625		break;
626	case '\r':	/* CR */
627		screen_write_carriagereturn(&ictx->ctx);
628		break;
629	case '\007':	/* BELL */
630		ictx->wp->window->flags |= WINDOW_BELL;
631		break;
632	case '\010': 	/* BS */
633		screen_write_cursorleft(&ictx->ctx, 1);
634		break;
635	case '\011': 	/* TAB */
636		s->cx = ((s->cx / 8) * 8) + 8;
637		if (s->cx > screen_size_x(s) - 1) {
638			s->cx = 0;
639			screen_write_cursordown(&ictx->ctx, 1);
640		}
641		screen_write_cursormove(&ictx->ctx, s->cx, s->cy);
642		break;
643	case '\013':	/* VT */
644		screen_write_linefeed(&ictx->ctx);
645		break;
646	case '\016':	/* SO */
647		ictx->cell.attr |= GRID_ATTR_CHARSET;
648		break;
649	case '\017':	/* SI */
650		ictx->cell.attr &= ~GRID_ATTR_CHARSET;
651		break;
652	default:
653		log_debug("unknown c0: %hhu", ch);
654		break;
655	}
656}
657
658void
659input_handle_c1_control(u_char ch, struct input_ctx *ictx)
660{
661	log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch);
662
663	switch (ch) {
664	case 'D':	/* IND */
665		screen_write_linefeed(&ictx->ctx);
666		break;
667	case 'E': 	/* NEL */
668		screen_write_carriagereturn(&ictx->ctx);
669		screen_write_linefeed(&ictx->ctx);
670		break;
671	case 'M':	/* RI */
672		screen_write_reverseindex(&ictx->ctx);
673		break;
674	default:
675		log_debug("unknown c1: %hhu", ch);
676		break;
677	}
678}
679
680void
681input_handle_private_two(u_char ch, struct input_ctx *ictx)
682{
683	struct screen	*s = ictx->ctx.s;
684
685	log_debug2(
686	    "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
687
688	switch (ch) {
689	case '0':	/* SCS */
690		/*
691		 * Not really supported, but fake it up enough for those that
692		 * use it to switch character sets (by redefining G0 to
693		 * graphics set, rather than switching to G1).
694		 */
695		switch (ictx->intermediate) {
696		case '(':	/* G0 */
697			ictx->cell.attr |= GRID_ATTR_CHARSET;
698			break;
699		}
700		break;
701	case '=':	/* DECKPAM */
702		if (ictx->intermediate != '\0')
703			break;
704		screen_write_kkeypadmode(&ictx->ctx, 1);
705		log_debug("kkeypad on (application mode)");
706		break;
707	case '>':	/* DECKPNM */
708		if (ictx->intermediate != '\0')
709			break;
710		screen_write_kkeypadmode(&ictx->ctx, 0);
711		log_debug("kkeypad off (number mode)");
712		break;
713	case '7':	/* DECSC */
714		if (ictx->intermediate != '\0')
715			break;
716		memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
717		ictx->saved_cx = s->cx;
718		ictx->saved_cy = s->cy;
719		break;
720	case '8':
721		switch (ictx->intermediate) {
722		case '\0':	/* DECRC */
723			memcpy(
724			    &ictx->cell, &ictx->saved_cell, sizeof ictx->cell);
725			screen_write_cursormove(
726			    &ictx->ctx, ictx->saved_cx, ictx->saved_cy);
727			break;
728		case '#':	/* DECALN */
729			screen_write_alignmenttest(&ictx->ctx);
730			break;
731		}
732		break;
733	default:
734		log_debug("unknown p2: %hhu", ch);
735		break;
736	}
737}
738
739void
740input_handle_standard_two(u_char ch, struct input_ctx *ictx)
741{
742	log_debug2(
743	    "-- s2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
744
745	switch (ch) {
746	case 'B':	/* Dscs (ASCII) */
747		/*
748		 * Not really supported, but fake it up enough for those that
749		 * use it to switch character sets (by redefining G0 to
750		 * graphics set, rather than switching to G1).
751		 */
752		switch (ictx->intermediate) {
753		case '(':	/* G0 */
754			ictx->cell.attr &= ~GRID_ATTR_CHARSET;
755			break;
756		}
757		break;
758	case 'c':	/* RIS */
759		memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
760
761		memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
762		ictx->saved_cx = 0;
763		ictx->saved_cy = 0;
764
765		screen_write_scrollregion(
766		    &ictx->ctx, 0, screen_size_y(ictx->ctx.s) - 1);
767
768		screen_write_insertmode(&ictx->ctx, 0);
769		screen_write_kcursormode(&ictx->ctx, 0);
770		screen_write_kkeypadmode(&ictx->ctx, 0);
771		screen_write_mousemode(&ictx->ctx, 0);
772
773		screen_write_clearscreen(&ictx->ctx);
774		screen_write_cursormove(&ictx->ctx, 0, 0);
775		break;
776	case 'k':
777		input_start_string(ictx, STRING_NAME);
778		input_state(ictx, input_state_string_next);
779		break;
780	default:
781		log_debug("unknown s2: %hhu", ch);
782		break;
783	}
784}
785
786void
787input_handle_sequence(u_char ch, struct input_ctx *ictx)
788{
789	struct input_sequence_entry	*entry, find;
790	struct screen	 		*s = ictx->ctx.s;
791	u_int			         i;
792	struct input_arg 		*iarg;
793
794	log_debug2("-- sq %zu: %hhu (%c): %u [sx=%u, sy=%u, cx=%u, cy=%u, "
795	    "ru=%u, rl=%u]", ictx->off, ch, ch, ARRAY_LENGTH(&ictx->args),
796	    screen_size_x(s), screen_size_y(s), s->cx, s->cy, s->rupper,
797	    s->rlower);
798	for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
799		iarg = &ARRAY_ITEM(&ictx->args, i);
800		if (*iarg->data != '\0')
801			log_debug2("      ++ %u: %s", i, iarg->data);
802	}
803
804	find.ch = ch;
805	entry = bsearch(&find,
806	    input_sequence_table, nitems(input_sequence_table),
807	    sizeof input_sequence_table[0], input_sequence_cmp);
808	if (entry != NULL)
809		entry->fn(ictx);
810	else
811		log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private);
812}
813
814void
815input_handle_sequence_cuu(struct input_ctx *ictx)
816{
817	uint16_t	n;
818
819	if (ictx->private != '\0')
820		return;
821
822	if (ARRAY_LENGTH(&ictx->args) > 1)
823		return;
824	if (input_get_argument(ictx, 0, &n, 1) != 0)
825		return;
826	if (n == 0)
827		n = 1;
828
829	screen_write_cursorup(&ictx->ctx, n);
830}
831
832void
833input_handle_sequence_cud(struct input_ctx *ictx)
834{
835	uint16_t	n;
836
837	if (ictx->private != '\0')
838		return;
839
840	if (ARRAY_LENGTH(&ictx->args) > 1)
841		return;
842	if (input_get_argument(ictx, 0, &n, 1) != 0)
843		return;
844	if (n == 0)
845		n = 1;
846
847	screen_write_cursordown(&ictx->ctx, n);
848}
849
850void
851input_handle_sequence_cuf(struct input_ctx *ictx)
852{
853	uint16_t n;
854
855	if (ictx->private != '\0')
856		return;
857
858	if (ARRAY_LENGTH(&ictx->args) > 1)
859		return;
860	if (input_get_argument(ictx, 0, &n, 1) != 0)
861		return;
862	if (n == 0)
863		n = 1;
864
865	screen_write_cursorright(&ictx->ctx, n);
866}
867
868void
869input_handle_sequence_cub(struct input_ctx *ictx)
870{
871	uint16_t	n;
872
873	if (ictx->private != '\0')
874		return;
875
876	if (ARRAY_LENGTH(&ictx->args) > 1)
877		return;
878	if (input_get_argument(ictx, 0, &n, 1) != 0)
879		return;
880	if (n == 0)
881		n = 1;
882
883	screen_write_cursorleft(&ictx->ctx, n);
884}
885
886void
887input_handle_sequence_dch(struct input_ctx *ictx)
888{
889	uint16_t	n;
890
891	if (ictx->private != '\0')
892		return;
893
894	if (ARRAY_LENGTH(&ictx->args) > 1)
895		return;
896	if (input_get_argument(ictx, 0, &n, 1) != 0)
897		return;
898	if (n == 0)
899		n = 1;
900
901	screen_write_deletecharacter(&ictx->ctx, n);
902}
903
904void
905input_handle_sequence_dl(struct input_ctx *ictx)
906{
907	uint16_t	n;
908
909	if (ictx->private != '\0')
910		return;
911
912	if (ARRAY_LENGTH(&ictx->args) > 1)
913		return;
914	if (input_get_argument(ictx, 0, &n, 1) != 0)
915		return;
916	if (n == 0)
917		n = 1;
918
919	screen_write_deleteline(&ictx->ctx, n);
920}
921
922void
923input_handle_sequence_ich(struct input_ctx *ictx)
924{
925	uint16_t	n;
926
927	if (ictx->private != '\0')
928		return;
929
930	if (ARRAY_LENGTH(&ictx->args) > 1)
931		return;
932	if (input_get_argument(ictx, 0, &n, 1) != 0)
933		return;
934	if (n == 0)
935		n = 1;
936
937	screen_write_insertcharacter(&ictx->ctx, n);
938}
939
940void
941input_handle_sequence_il(struct input_ctx *ictx)
942{
943	uint16_t	n;
944
945	if (ictx->private != '\0')
946		return;
947
948	if (ARRAY_LENGTH(&ictx->args) > 1)
949		return;
950	if (input_get_argument(ictx, 0, &n, 1) != 0)
951		return;
952	if (n == 0)
953		n = 1;
954
955	screen_write_insertline(&ictx->ctx, n);
956}
957
958void
959input_handle_sequence_vpa(struct input_ctx *ictx)
960{
961	struct screen  *s = ictx->ctx.s;
962	uint16_t	n;
963
964	if (ictx->private != '\0')
965		return;
966
967	if (ARRAY_LENGTH(&ictx->args) > 1)
968		return;
969	if (input_get_argument(ictx, 0, &n, 1) != 0)
970		return;
971	if (n == 0)
972		n = 1;
973
974	screen_write_cursormove(&ictx->ctx, s->cx, n - 1);
975}
976
977void
978input_handle_sequence_hpa(struct input_ctx *ictx)
979{
980	struct screen  *s = ictx->ctx.s;
981	uint16_t	n;
982
983	if (ictx->private != '\0')
984		return;
985
986	if (ARRAY_LENGTH(&ictx->args) > 1)
987		return;
988	if (input_get_argument(ictx, 0, &n, 1) != 0)
989		return;
990	if (n == 0)
991		n = 1;
992
993	screen_write_cursormove(&ictx->ctx, n - 1, s->cy);
994}
995
996void
997input_handle_sequence_cup(struct input_ctx *ictx)
998{
999	uint16_t	n, m;
1000
1001	if (ictx->private != '\0')
1002		return;
1003
1004	if (ARRAY_LENGTH(&ictx->args) > 2)
1005		return;
1006	if (input_get_argument(ictx, 0, &n, 1) != 0)
1007		return;
1008	if (input_get_argument(ictx, 1, &m, 1) != 0)
1009		return;
1010	if (n == 0)
1011		n = 1;
1012	if (m == 0)
1013		m = 1;
1014
1015	screen_write_cursormove(&ictx->ctx, m - 1, n - 1);
1016}
1017
1018void
1019input_handle_sequence_ed(struct input_ctx *ictx)
1020{
1021	uint16_t	n;
1022
1023	if (ictx->private != '\0')
1024		return;
1025
1026	if (ARRAY_LENGTH(&ictx->args) > 1)
1027		return;
1028	if (input_get_argument(ictx, 0, &n, 0) != 0)
1029		return;
1030	if (n > 2)
1031		return;
1032
1033	switch (n) {
1034	case 0:
1035		screen_write_clearendofscreen(&ictx->ctx);
1036		break;
1037	case 1:
1038		screen_write_clearstartofscreen(&ictx->ctx);
1039		break;
1040	case 2:
1041		screen_write_clearscreen(&ictx->ctx);
1042		break;
1043	}
1044}
1045
1046void
1047input_handle_sequence_el(struct input_ctx *ictx)
1048{
1049	uint16_t	n;
1050
1051	if (ictx->private != '\0')
1052		return;
1053
1054	if (ARRAY_LENGTH(&ictx->args) > 1)
1055		return;
1056	if (input_get_argument(ictx, 0, &n, 0) != 0)
1057		return;
1058	if (n > 2)
1059		return;
1060
1061	switch (n) {
1062	case 0:
1063		screen_write_clearendofline(&ictx->ctx);
1064		break;
1065	case 1:
1066		screen_write_clearstartofline(&ictx->ctx);
1067		break;
1068	case 2:
1069		screen_write_clearline(&ictx->ctx);
1070		break;
1071	}
1072}
1073
1074void
1075input_handle_sequence_sm(struct input_ctx *ictx)
1076{
1077	uint16_t	n;
1078
1079	if (ARRAY_LENGTH(&ictx->args) > 1)
1080		return;
1081	if (input_get_argument(ictx, 0, &n, 0) != 0)
1082		return;
1083
1084	if (ictx->private == '?') {
1085		switch (n) {
1086		case 1:		/* GATM */
1087			screen_write_kcursormode(&ictx->ctx, 1);
1088			log_debug("kcursor on");
1089			break;
1090		case 25:	/* TCEM */
1091			screen_write_cursormode(&ictx->ctx, 1);
1092			log_debug("cursor on");
1093			break;
1094		case 1000:
1095			screen_write_mousemode(&ictx->ctx, 1);
1096			log_debug("mouse on");
1097			break;
1098		default:
1099			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1100			break;
1101		}
1102	} else {
1103		switch (n) {
1104		case 4:		/* IRM */
1105			screen_write_insertmode(&ictx->ctx, 1);
1106			log_debug("insert on");
1107			break;
1108		case 34:
1109			/* Cursor high visibility not supported. */
1110			break;
1111		default:
1112			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1113			break;
1114		}
1115	}
1116}
1117
1118void
1119input_handle_sequence_rm(struct input_ctx *ictx)
1120{
1121	uint16_t	 n;
1122
1123	if (ARRAY_LENGTH(&ictx->args) > 1)
1124		return;
1125	if (input_get_argument(ictx, 0, &n, 0) != 0)
1126		return;
1127
1128	if (ictx->private == '?') {
1129		switch (n) {
1130		case 1:		/* GATM */
1131			screen_write_kcursormode(&ictx->ctx, 0);
1132			log_debug("kcursor off");
1133			break;
1134		case 25:	/* TCEM */
1135			screen_write_cursormode(&ictx->ctx, 0);
1136			log_debug("cursor off");
1137			break;
1138		case 1000:
1139			screen_write_mousemode(&ictx->ctx, 0);
1140			log_debug("mouse off");
1141			break;
1142		default:
1143			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1144			break;
1145		}
1146	} else if (ictx->private == '\0') {
1147		switch (n) {
1148		case 4:		/* IRM */
1149			screen_write_insertmode(&ictx->ctx, 0);
1150			log_debug("insert off");
1151			break;
1152		case 34:
1153			/* Cursor high visibility not supported. */
1154			break;
1155		default:
1156			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1157			break;
1158		}
1159	}
1160}
1161
1162void
1163input_handle_sequence_dsr(struct input_ctx *ictx)
1164{
1165	struct screen  *s = ictx->ctx.s;
1166	uint16_t	n;
1167	char		reply[32];
1168
1169	if (ARRAY_LENGTH(&ictx->args) > 1)
1170		return;
1171	if (input_get_argument(ictx, 0, &n, 0) != 0)
1172		return;
1173
1174	if (ictx->private == '\0') {
1175		switch (n) {
1176		case 6:	/* cursor position */
1177			xsnprintf(reply, sizeof reply,
1178			    "\033[%u;%uR", s->cy + 1, s->cx + 1);
1179			log_debug("cursor request, reply: %s", reply);
1180			buffer_write(ictx->wp->out, reply, strlen(reply));
1181			break;
1182		}
1183	}
1184
1185}
1186
1187void
1188input_handle_sequence_decstbm(struct input_ctx *ictx)
1189{
1190	struct screen  *s = ictx->ctx.s;
1191	uint16_t	n, m;
1192
1193	if (ictx->private != '\0')
1194		return;
1195
1196	if (ARRAY_LENGTH(&ictx->args) > 2)
1197		return;
1198	if (input_get_argument(ictx, 0, &n, 0) != 0)
1199		return;
1200	if (input_get_argument(ictx, 1, &m, 0) != 0)
1201		return;
1202	if (n == 0)
1203		n = 1;
1204	if (m == 0)
1205		m = screen_size_y(s);
1206
1207	screen_write_scrollregion(&ictx->ctx, n - 1, m - 1);
1208}
1209
1210void
1211input_handle_sequence_sgr(struct input_ctx *ictx)
1212{
1213	struct grid_cell       *gc = &ictx->cell;
1214	u_int			i;
1215	uint16_t		m, o;
1216	u_char			attr;
1217
1218	if (ARRAY_LENGTH(&ictx->args) == 0) {
1219		attr = gc->attr;
1220		memcpy(gc, &grid_default_cell, sizeof *gc);
1221 		gc->attr |= (attr & GRID_ATTR_CHARSET);
1222		return;
1223	}
1224
1225	for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
1226		if (input_get_argument(ictx, i, &m, 0) != 0)
1227			return;
1228
1229		if (m == 38 || m == 48) {
1230			i++;
1231			if (input_get_argument(ictx, i, &o, 0) != 0)
1232				return;
1233			if (o != 5)
1234				continue;
1235
1236			i++;
1237			if (input_get_argument(ictx, i, &o, 0) != 0)
1238				return;
1239			if (m == 38) {
1240				gc->flags |= GRID_FLAG_FG256;
1241				gc->fg = o;
1242			} else if (m == 48) {
1243				gc->flags |= GRID_FLAG_BG256;
1244				gc->bg = o;
1245			}
1246			continue;
1247		}
1248
1249		switch (m) {
1250		case 0:
1251		case 10:
1252			attr = gc->attr;
1253			memcpy(gc, &grid_default_cell, sizeof *gc);
1254			gc->attr |= (attr & GRID_ATTR_CHARSET);
1255			break;
1256		case 1:
1257			gc->attr |= GRID_ATTR_BRIGHT;
1258			break;
1259		case 2:
1260			gc->attr |= GRID_ATTR_DIM;
1261			break;
1262		case 3:
1263			gc->attr |= GRID_ATTR_ITALICS;
1264			break;
1265		case 4:
1266			gc->attr |= GRID_ATTR_UNDERSCORE;
1267			break;
1268		case 5:
1269			gc->attr |= GRID_ATTR_BLINK;
1270			break;
1271		case 7:
1272			gc->attr |= GRID_ATTR_REVERSE;
1273			break;
1274		case 8:
1275			gc->attr |= GRID_ATTR_HIDDEN;
1276			break;
1277		case 22:
1278			gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
1279			break;
1280		case 23:
1281			gc->attr &= ~GRID_ATTR_ITALICS;
1282			break;
1283		case 24:
1284			gc->attr &= ~GRID_ATTR_UNDERSCORE;
1285			break;
1286		case 25:
1287			gc->attr &= ~GRID_ATTR_BLINK;
1288			break;
1289		case 27:
1290			gc->attr &= ~GRID_ATTR_REVERSE;
1291			break;
1292		case 30:
1293		case 31:
1294		case 32:
1295		case 33:
1296		case 34:
1297		case 35:
1298		case 36:
1299		case 37:
1300			gc->flags &= ~GRID_FLAG_FG256;
1301			gc->fg = m - 30;
1302			break;
1303		case 39:
1304			gc->flags &= ~GRID_FLAG_FG256;
1305			gc->fg = 8;
1306			break;
1307		case 40:
1308		case 41:
1309		case 42:
1310		case 43:
1311		case 44:
1312		case 45:
1313		case 46:
1314		case 47:
1315			gc->flags &= ~GRID_FLAG_BG256;
1316			gc->bg = m - 40;
1317			break;
1318		case 49:
1319			gc->flags &= ~GRID_FLAG_BG256;
1320			gc->bg = 8;
1321			break;
1322		}
1323	}
1324}
1325