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