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