xref: /openbsd-src/usr.bin/tmux/input.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /* $OpenBSD: input.c,v 1.21 2009/10/26 21:42:04 deraadt 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 
37 int	 input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t);
38 void	 input_new_argument(struct input_ctx *);
39 int	 input_add_argument(struct input_ctx *, u_char);
40 
41 void	 input_start_string(struct input_ctx *, int);
42 void	 input_abort_string(struct input_ctx *);
43 int	 input_add_string(struct input_ctx *, u_char);
44 char	*input_get_string(struct input_ctx *);
45 
46 void	 input_state(struct input_ctx *, void *);
47 
48 void	 input_state_first(u_char, struct input_ctx *);
49 void	 input_state_escape(u_char, struct input_ctx *);
50 void	 input_state_intermediate(u_char, struct input_ctx *);
51 void	 input_state_sequence_first(u_char, struct input_ctx *);
52 void	 input_state_sequence_next(u_char, struct input_ctx *);
53 void	 input_state_sequence_intermediate(u_char, struct input_ctx *);
54 void	 input_state_string_next(u_char, struct input_ctx *);
55 void	 input_state_string_escape(u_char, struct input_ctx *);
56 void	 input_state_utf8(u_char, struct input_ctx *);
57 
58 void	 input_handle_character(u_char, struct input_ctx *);
59 void	 input_handle_c0_control(u_char, struct input_ctx *);
60 void	 input_handle_c1_control(u_char, struct input_ctx *);
61 void	 input_handle_private_two(u_char, struct input_ctx *);
62 void	 input_handle_standard_two(u_char, struct input_ctx *);
63 void	 input_handle_sequence(u_char, struct input_ctx *);
64 
65 void	 input_handle_sequence_cuu(struct input_ctx *);
66 void	 input_handle_sequence_cud(struct input_ctx *);
67 void	 input_handle_sequence_cuf(struct input_ctx *);
68 void	 input_handle_sequence_cub(struct input_ctx *);
69 void	 input_handle_sequence_dch(struct input_ctx *);
70 void	 input_handle_sequence_cbt(struct input_ctx *);
71 void	 input_handle_sequence_da(struct input_ctx *);
72 void	 input_handle_sequence_dl(struct input_ctx *);
73 void	 input_handle_sequence_ich(struct input_ctx *);
74 void	 input_handle_sequence_il(struct input_ctx *);
75 void	 input_handle_sequence_vpa(struct input_ctx *);
76 void	 input_handle_sequence_hpa(struct input_ctx *);
77 void	 input_handle_sequence_cup(struct input_ctx *);
78 void	 input_handle_sequence_cup(struct input_ctx *);
79 void	 input_handle_sequence_tbc(struct input_ctx *);
80 void	 input_handle_sequence_ed(struct input_ctx *);
81 void	 input_handle_sequence_el(struct input_ctx *);
82 void	 input_handle_sequence_sm(struct input_ctx *);
83 void	 input_handle_sequence_rm(struct input_ctx *);
84 void	 input_handle_sequence_decstbm(struct input_ctx *);
85 void	 input_handle_sequence_sgr(struct input_ctx *);
86 void	 input_handle_sequence_dsr(struct input_ctx *);
87 
88 int	 input_sequence_cmp(const void *, const void *);
89 
90 struct input_sequence_entry {
91 	u_char	ch;
92 	void	(*fn)(struct input_ctx *);
93 };
94 const struct input_sequence_entry input_sequence_table[] = {
95 	{ '@', input_handle_sequence_ich },
96 	{ 'A', input_handle_sequence_cuu },
97 	{ 'B', input_handle_sequence_cud },
98 	{ 'C', input_handle_sequence_cuf },
99 	{ 'D', input_handle_sequence_cub },
100 	{ 'G', input_handle_sequence_hpa },
101 	{ 'H', input_handle_sequence_cup },
102 	{ 'J', input_handle_sequence_ed },
103 	{ 'K', input_handle_sequence_el },
104 	{ 'L', input_handle_sequence_il },
105 	{ 'M', input_handle_sequence_dl },
106 	{ 'P', input_handle_sequence_dch },
107 	{ 'Z', input_handle_sequence_cbt },
108 	{ 'c', input_handle_sequence_da },
109 	{ 'd', input_handle_sequence_vpa },
110 	{ 'f', input_handle_sequence_cup },
111 	{ 'g', input_handle_sequence_tbc },
112 	{ 'h', input_handle_sequence_sm },
113 	{ 'l', input_handle_sequence_rm },
114 	{ 'm', input_handle_sequence_sgr },
115 	{ 'n', input_handle_sequence_dsr },
116 	{ 'r', input_handle_sequence_decstbm },
117 };
118 
119 int
120 input_sequence_cmp(const void *a, const void *b)
121 {
122 	int	ai = ((const struct input_sequence_entry *) a)->ch;
123 	int	bi = ((const struct input_sequence_entry *) b)->ch;
124 
125 	return (ai - bi);
126 }
127 
128 void
129 input_new_argument(struct input_ctx *ictx)
130 {
131 	struct input_arg	*arg;
132 
133 	ARRAY_EXPAND(&ictx->args, 1);
134 
135 	arg = &ARRAY_LAST(&ictx->args);
136 	arg->used = 0;
137 }
138 
139 int
140 input_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 
155 int
156 input_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 
175 void
176 input_start_string(struct input_ctx *ictx, int type)
177 {
178 	ictx->string_type = type;
179 	ictx->string_len = 0;
180 }
181 
182 void
183 input_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 
190 int
191 input_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 
204 char *
205 input_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 
217 void
218 input_state(struct input_ctx *ictx, void *state)
219 {
220 	ictx->state = state;
221 }
222 
223 void
224 input_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 	ictx->was = 0;
242 }
243 
244 void
245 input_free(struct window_pane *wp)
246 {
247 	if (wp->ictx.string_buf != NULL)
248 		xfree(wp->ictx.string_buf);
249 
250 	ARRAY_FREE(&wp->ictx.args);
251 }
252 
253 void
254 input_parse(struct window_pane *wp)
255 {
256 	struct input_ctx	*ictx = &wp->ictx;
257 	u_char			 ch;
258 
259 	if (BUFFER_USED(wp->in) == ictx->was)
260 		return;
261 	wp->window->flags |= WINDOW_ACTIVITY;
262 
263 	ictx->buf = BUFFER_OUT(wp->in);
264 	ictx->len = BUFFER_USED(wp->in);
265 	ictx->off = 0;
266 
267 	ictx->wp = wp;
268 
269 	if (wp->mode == NULL)
270 		screen_write_start(&ictx->ctx, wp, &wp->base);
271 	else
272 		screen_write_start(&ictx->ctx, NULL, &wp->base);
273 
274 	while (ictx->off < ictx->len) {
275 		ch = ictx->buf[ictx->off++];
276 		ictx->state(ch, ictx);
277 	}
278 
279 	screen_write_stop(&ictx->ctx);
280 
281 	buffer_remove(wp->in, ictx->len);
282 	ictx->was = BUFFER_USED(wp->in);
283 }
284 
285 void
286 input_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 
321 void
322 input_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 
371 void
372 input_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 
395 void
396 input_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 
435 void
436 input_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 
493 void
494 input_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 
510 void
511 input_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 
529 void
530 input_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 
572 void
573 input_state_utf8(u_char ch, struct input_ctx *ictx)
574 {
575 	log_debug2("-- utf8 next: %zu: %hhu (%c)", ictx->off, ch, ch);
576 
577 	if (utf8_append(&ictx->utf8data, ch))
578 		return;		/* more to come */
579 	input_state(ictx, input_state_first);
580 
581 	ictx->cell.flags |= GRID_FLAG_UTF8;
582 	screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data);
583 	ictx->cell.flags &= ~GRID_FLAG_UTF8;
584 }
585 
586 void
587 input_handle_character(u_char ch, struct input_ctx *ictx)
588 {
589 	struct window_pane	*wp = ictx->wp;
590 
591 	if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) {
592 		if (utf8_open(&ictx->utf8data, ch)) {
593 			log_debug2("-- utf8 size %zu: %zu: %hhu (%c)",
594 			    ictx->utf8data.size, ictx->off, ch, ch);
595 			input_state(ictx, input_state_utf8);
596 			return;
597 		}
598 	}
599 	log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch);
600 
601 	ictx->cell.data = ch;
602 	screen_write_cell(&ictx->ctx, &ictx->cell, NULL);
603 }
604 
605 void
606 input_handle_c0_control(u_char ch, struct input_ctx *ictx)
607 {
608 	struct screen	*s = ictx->ctx.s;
609 
610 	log_debug2("-- c0 %zu: %hhu", ictx->off, ch);
611 
612 	switch (ch) {
613 	case '\0':	/* NUL */
614 		break;
615 	case '\n':	/* LF */
616 		screen_write_linefeed(&ictx->ctx, 0);
617 		break;
618 	case '\r':	/* CR */
619 		screen_write_carriagereturn(&ictx->ctx);
620 		break;
621 	case '\007':	/* BELL */
622 		ictx->wp->window->flags |= WINDOW_BELL;
623 		break;
624 	case '\010': 	/* BS */
625 		screen_write_backspace(&ictx->ctx);
626 		break;
627 	case '\011': 	/* TAB */
628 		/* Don't tab beyond the end of the line. */
629 		if (s->cx >= screen_size_x(s) - 1)
630 			break;
631 
632 		/* Find the next tab point, or use the last column if none. */
633 		do {
634 			s->cx++;
635 			if (bit_test(s->tabs, s->cx))
636 				break;
637 		} while (s->cx < screen_size_x(s) - 1);
638 		break;
639 	case '\013':	/* VT */
640 		screen_write_linefeed(&ictx->ctx, 0);
641 		break;
642 	case '\016':	/* SO */
643 		ictx->cell.attr |= GRID_ATTR_CHARSET;
644 		break;
645 	case '\017':	/* SI */
646 		ictx->cell.attr &= ~GRID_ATTR_CHARSET;
647 		break;
648 	default:
649 		log_debug("unknown c0: %hhu", ch);
650 		break;
651 	}
652 }
653 
654 void
655 input_handle_c1_control(u_char ch, struct input_ctx *ictx)
656 {
657 	struct screen  *s = ictx->ctx.s;
658 
659 	log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch);
660 
661 	switch (ch) {
662 	case 'D':	/* IND */
663 		screen_write_linefeed(&ictx->ctx, 0);
664 		break;
665 	case 'E': 	/* NEL */
666 		screen_write_carriagereturn(&ictx->ctx);
667 		screen_write_linefeed(&ictx->ctx, 0);
668 		break;
669 	case 'H':	/* HTS */
670 		if (s->cx < screen_size_x(s))
671 			bit_set(s->tabs, s->cx);
672 		break;
673 	case 'M':	/* RI */
674 		screen_write_reverseindex(&ictx->ctx);
675 		break;
676 	default:
677 		log_debug("unknown c1: %hhu", ch);
678 		break;
679 	}
680 }
681 
682 void
683 input_handle_private_two(u_char ch, struct input_ctx *ictx)
684 {
685 	struct screen	*s = ictx->ctx.s;
686 
687 	log_debug2(
688 	    "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
689 
690 	switch (ch) {
691 	case '0':	/* SCS */
692 		/*
693 		 * Not really supported, but fake it up enough for those that
694 		 * use it to switch character sets (by redefining G0 to
695 		 * graphics set, rather than switching to G1).
696 		 */
697 		switch (ictx->intermediate) {
698 		case '(':	/* G0 */
699 			ictx->cell.attr |= GRID_ATTR_CHARSET;
700 			break;
701 		}
702 		break;
703 	case '=':	/* DECKPAM */
704 		if (ictx->intermediate != '\0')
705 			break;
706 		screen_write_kkeypadmode(&ictx->ctx, 1);
707 		log_debug("kkeypad on (application mode)");
708 		break;
709 	case '>':	/* DECKPNM */
710 		if (ictx->intermediate != '\0')
711 			break;
712 		screen_write_kkeypadmode(&ictx->ctx, 0);
713 		log_debug("kkeypad off (number mode)");
714 		break;
715 	case '7':	/* DECSC */
716 		if (ictx->intermediate != '\0')
717 			break;
718 		memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
719 		ictx->saved_cx = s->cx;
720 		ictx->saved_cy = s->cy;
721 		break;
722 	case '8':
723 		switch (ictx->intermediate) {
724 		case '\0':	/* DECRC */
725 			memcpy(
726 			    &ictx->cell, &ictx->saved_cell, sizeof ictx->cell);
727 			screen_write_cursormove(
728 			    &ictx->ctx, ictx->saved_cx, ictx->saved_cy);
729 			break;
730 		case '#':	/* DECALN */
731 			screen_write_alignmenttest(&ictx->ctx);
732 			break;
733 		}
734 		break;
735 	default:
736 		log_debug("unknown p2: %hhu", ch);
737 		break;
738 	}
739 }
740 
741 void
742 input_handle_standard_two(u_char ch, struct input_ctx *ictx)
743 {
744 	log_debug2(
745 	    "-- s2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
746 
747 	switch (ch) {
748 	case 'B':	/* SCS */
749 		/*
750 		 * Not really supported, but fake it up enough for those that
751 		 * use it to switch character sets (by redefining G0 to
752 		 * graphics set, rather than switching to G1).
753 		 */
754 		switch (ictx->intermediate) {
755 		case '(':	/* G0 */
756 			ictx->cell.attr &= ~GRID_ATTR_CHARSET;
757 			break;
758 		}
759 		break;
760 	case 'c':	/* RIS */
761 		memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
762 
763 		memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
764 		ictx->saved_cx = 0;
765 		ictx->saved_cy = 0;
766 
767 		screen_reset_tabs(ictx->ctx.s);
768 
769 		screen_write_scrollregion(
770 		    &ictx->ctx, 0, screen_size_y(ictx->ctx.s) - 1);
771 
772 		screen_write_insertmode(&ictx->ctx, 0);
773 		screen_write_kcursormode(&ictx->ctx, 0);
774 		screen_write_kkeypadmode(&ictx->ctx, 0);
775 		screen_write_mousemode(&ictx->ctx, 0);
776 
777 		screen_write_clearscreen(&ictx->ctx);
778 		screen_write_cursormove(&ictx->ctx, 0, 0);
779 		break;
780 	case 'k':
781 		input_start_string(ictx, STRING_NAME);
782 		input_state(ictx, input_state_string_next);
783 		break;
784 	default:
785 		log_debug("unknown s2: %hhu", ch);
786 		break;
787 	}
788 }
789 
790 void
791 input_handle_sequence(u_char ch, struct input_ctx *ictx)
792 {
793 	struct input_sequence_entry	*entry, find;
794 	struct screen	 		*s = ictx->ctx.s;
795 	u_int				 i;
796 	struct input_arg 		*iarg;
797 
798 	log_debug2("-- sq %zu: %hhu (%c): %u [sx=%u, sy=%u, cx=%u, cy=%u, "
799 	    "ru=%u, rl=%u]", ictx->off, ch, ch, ARRAY_LENGTH(&ictx->args),
800 	    screen_size_x(s), screen_size_y(s), s->cx, s->cy, s->rupper,
801 	    s->rlower);
802 	for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
803 		iarg = &ARRAY_ITEM(&ictx->args, i);
804 		if (*iarg->data != '\0')
805 			log_debug2("      ++ %u: %s", i, iarg->data);
806 	}
807 
808 	find.ch = ch;
809 	entry = bsearch(&find,
810 	    input_sequence_table, nitems(input_sequence_table),
811 	    sizeof input_sequence_table[0], input_sequence_cmp);
812 	if (entry != NULL)
813 		entry->fn(ictx);
814 	else
815 		log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private);
816 }
817 
818 void
819 input_handle_sequence_cuu(struct input_ctx *ictx)
820 {
821 	uint16_t	n;
822 
823 	if (ictx->private != '\0')
824 		return;
825 
826 	if (ARRAY_LENGTH(&ictx->args) > 1)
827 		return;
828 	if (input_get_argument(ictx, 0, &n, 1) != 0)
829 		return;
830 	if (n == 0)
831 		n = 1;
832 
833 	screen_write_cursorup(&ictx->ctx, n);
834 }
835 
836 void
837 input_handle_sequence_cud(struct input_ctx *ictx)
838 {
839 	uint16_t	n;
840 
841 	if (ictx->private != '\0')
842 		return;
843 
844 	if (ARRAY_LENGTH(&ictx->args) > 1)
845 		return;
846 	if (input_get_argument(ictx, 0, &n, 1) != 0)
847 		return;
848 	if (n == 0)
849 		n = 1;
850 
851 	screen_write_cursordown(&ictx->ctx, n);
852 }
853 
854 void
855 input_handle_sequence_cuf(struct input_ctx *ictx)
856 {
857 	uint16_t n;
858 
859 	if (ictx->private != '\0')
860 		return;
861 
862 	if (ARRAY_LENGTH(&ictx->args) > 1)
863 		return;
864 	if (input_get_argument(ictx, 0, &n, 1) != 0)
865 		return;
866 	if (n == 0)
867 		n = 1;
868 
869 	screen_write_cursorright(&ictx->ctx, n);
870 }
871 
872 void
873 input_handle_sequence_cub(struct input_ctx *ictx)
874 {
875 	uint16_t	n;
876 
877 	if (ictx->private != '\0')
878 		return;
879 
880 	if (ARRAY_LENGTH(&ictx->args) > 1)
881 		return;
882 	if (input_get_argument(ictx, 0, &n, 1) != 0)
883 		return;
884 	if (n == 0)
885 		n = 1;
886 
887 	screen_write_cursorleft(&ictx->ctx, n);
888 }
889 
890 void
891 input_handle_sequence_dch(struct input_ctx *ictx)
892 {
893 	uint16_t	n;
894 
895 	if (ictx->private != '\0')
896 		return;
897 
898 	if (ARRAY_LENGTH(&ictx->args) > 1)
899 		return;
900 	if (input_get_argument(ictx, 0, &n, 1) != 0)
901 		return;
902 	if (n == 0)
903 		n = 1;
904 
905 	screen_write_deletecharacter(&ictx->ctx, n);
906 }
907 
908 void
909 input_handle_sequence_cbt(struct input_ctx *ictx)
910 {
911 	struct screen  *s = ictx->ctx.s;
912 	uint16_t	n;
913 
914 	if (ictx->private != '\0')
915 		return;
916 
917 	if (ARRAY_LENGTH(&ictx->args) > 1)
918 		return;
919 	if (input_get_argument(ictx, 0, &n, 1) != 0)
920 		return;
921 	if (n == 0)
922 		n = 1;
923 
924 	/* Find the previous tab point, n times. */
925 	while (s->cx > 0 && n-- > 0) {
926 		do
927 			s->cx--;
928 		while (s->cx > 0 && !bit_test(s->tabs, s->cx));
929 	}
930 }
931 
932 void
933 input_handle_sequence_da(struct input_ctx *ictx)
934 {
935 	uint16_t	n;
936 
937 	if (ictx->private != '\0')
938 		return;
939 
940 	if (ARRAY_LENGTH(&ictx->args) > 1)
941 		return;
942 	if (input_get_argument(ictx, 0, &n, 0) != 0)
943 		return;
944 	if (n != 0)
945 		return;
946 
947 	buffer_write(ictx->wp->out, "\033[?1;2c", (sizeof "\033[?1;2c") - 1);
948 }
949 
950 void
951 input_handle_sequence_dl(struct input_ctx *ictx)
952 {
953 	uint16_t	n;
954 
955 	if (ictx->private != '\0')
956 		return;
957 
958 	if (ARRAY_LENGTH(&ictx->args) > 1)
959 		return;
960 	if (input_get_argument(ictx, 0, &n, 1) != 0)
961 		return;
962 	if (n == 0)
963 		n = 1;
964 
965 	screen_write_deleteline(&ictx->ctx, n);
966 }
967 
968 void
969 input_handle_sequence_ich(struct input_ctx *ictx)
970 {
971 	uint16_t	n;
972 
973 	if (ictx->private != '\0')
974 		return;
975 
976 	if (ARRAY_LENGTH(&ictx->args) > 1)
977 		return;
978 	if (input_get_argument(ictx, 0, &n, 1) != 0)
979 		return;
980 	if (n == 0)
981 		n = 1;
982 
983 	screen_write_insertcharacter(&ictx->ctx, n);
984 }
985 
986 void
987 input_handle_sequence_il(struct input_ctx *ictx)
988 {
989 	uint16_t	n;
990 
991 	if (ictx->private != '\0')
992 		return;
993 
994 	if (ARRAY_LENGTH(&ictx->args) > 1)
995 		return;
996 	if (input_get_argument(ictx, 0, &n, 1) != 0)
997 		return;
998 	if (n == 0)
999 		n = 1;
1000 
1001 	screen_write_insertline(&ictx->ctx, n);
1002 }
1003 
1004 void
1005 input_handle_sequence_vpa(struct input_ctx *ictx)
1006 {
1007 	struct screen  *s = ictx->ctx.s;
1008 	uint16_t	n;
1009 
1010 	if (ictx->private != '\0')
1011 		return;
1012 
1013 	if (ARRAY_LENGTH(&ictx->args) > 1)
1014 		return;
1015 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1016 		return;
1017 	if (n == 0)
1018 		n = 1;
1019 
1020 	screen_write_cursormove(&ictx->ctx, s->cx, n - 1);
1021 }
1022 
1023 void
1024 input_handle_sequence_hpa(struct input_ctx *ictx)
1025 {
1026 	struct screen  *s = ictx->ctx.s;
1027 	uint16_t	n;
1028 
1029 	if (ictx->private != '\0')
1030 		return;
1031 
1032 	if (ARRAY_LENGTH(&ictx->args) > 1)
1033 		return;
1034 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1035 		return;
1036 	if (n == 0)
1037 		n = 1;
1038 
1039 	screen_write_cursormove(&ictx->ctx, n - 1, s->cy);
1040 }
1041 
1042 void
1043 input_handle_sequence_cup(struct input_ctx *ictx)
1044 {
1045 	uint16_t	n, m;
1046 
1047 	if (ictx->private != '\0')
1048 		return;
1049 
1050 	if (ARRAY_LENGTH(&ictx->args) > 2)
1051 		return;
1052 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1053 		return;
1054 	if (input_get_argument(ictx, 1, &m, 1) != 0)
1055 		return;
1056 	if (n == 0)
1057 		n = 1;
1058 	if (m == 0)
1059 		m = 1;
1060 
1061 	screen_write_cursormove(&ictx->ctx, m - 1, n - 1);
1062 }
1063 
1064 void
1065 input_handle_sequence_tbc(struct input_ctx *ictx)
1066 {
1067 	struct screen  *s = ictx->ctx.s;
1068 	uint16_t	n;
1069 
1070 	if (ictx->private != '\0')
1071 		return;
1072 
1073 	if (ARRAY_LENGTH(&ictx->args) > 1)
1074 		return;
1075 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1076 		return;
1077 
1078 	switch (n) {
1079 	case 0:
1080 		if (s->cx < screen_size_x(s))
1081 			bit_clear(s->tabs, s->cx);
1082 		break;
1083 	case 3:
1084 		bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
1085 		break;
1086 	}
1087 }
1088 
1089 void
1090 input_handle_sequence_ed(struct input_ctx *ictx)
1091 {
1092 	uint16_t	n;
1093 
1094 	if (ictx->private != '\0')
1095 		return;
1096 
1097 	if (ARRAY_LENGTH(&ictx->args) > 1)
1098 		return;
1099 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1100 		return;
1101 	if (n > 2)
1102 		return;
1103 
1104 	switch (n) {
1105 	case 0:
1106 		screen_write_clearendofscreen(&ictx->ctx);
1107 		break;
1108 	case 1:
1109 		screen_write_clearstartofscreen(&ictx->ctx);
1110 		break;
1111 	case 2:
1112 		screen_write_clearscreen(&ictx->ctx);
1113 		break;
1114 	}
1115 }
1116 
1117 void
1118 input_handle_sequence_el(struct input_ctx *ictx)
1119 {
1120 	uint16_t	n;
1121 
1122 	if (ictx->private != '\0')
1123 		return;
1124 
1125 	if (ARRAY_LENGTH(&ictx->args) > 1)
1126 		return;
1127 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1128 		return;
1129 	if (n > 2)
1130 		return;
1131 
1132 	switch (n) {
1133 	case 0:
1134 		screen_write_clearendofline(&ictx->ctx);
1135 		break;
1136 	case 1:
1137 		screen_write_clearstartofline(&ictx->ctx);
1138 		break;
1139 	case 2:
1140 		screen_write_clearline(&ictx->ctx);
1141 		break;
1142 	}
1143 }
1144 
1145 void
1146 input_handle_sequence_sm(struct input_ctx *ictx)
1147 {
1148 	struct window_pane	*wp = ictx->wp;
1149 	struct screen		*s = &wp->base;
1150 	u_int			 sx, sy;
1151 	uint16_t		 n;
1152 
1153 	if (ARRAY_LENGTH(&ictx->args) > 1)
1154 		return;
1155 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1156 		return;
1157 
1158 	if (ictx->private == '?') {
1159 		switch (n) {
1160 		case 1:		/* GATM */
1161 			screen_write_kcursormode(&ictx->ctx, 1);
1162 			log_debug("kcursor on");
1163 			break;
1164 		case 3:		/* DECCOLM */
1165 			screen_write_cursormove(&ictx->ctx, 0, 0);
1166 			screen_write_clearscreen(&ictx->ctx);
1167 			break;
1168 		case 25:	/* TCEM */
1169 			screen_write_cursormode(&ictx->ctx, 1);
1170 			log_debug("cursor on");
1171 			break;
1172 		case 1000:
1173 			screen_write_mousemode(&ictx->ctx, 1);
1174 			log_debug("mouse on");
1175 			break;
1176 		case 1049:
1177 			if (wp->saved_grid != NULL)
1178 				break;
1179 			sx = screen_size_x(s);
1180 			sy = screen_size_y(s);
1181 
1182 			/*
1183 			 * Enter alternative screen mode. A copy of the visible
1184 			 * screen is saved and the history is not updated
1185 			 */
1186 
1187 			wp->saved_grid = grid_create(sx, sy, 0);
1188 			grid_duplicate_lines(
1189 			    wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
1190 			wp->saved_cx = s->cx;
1191 			wp->saved_cy = s->cy;
1192 			memcpy(&wp->saved_cell,
1193 			    &ictx->cell, sizeof wp->saved_cell);
1194 
1195 			grid_view_clear(s->grid, 0, 0, sx, sy);
1196 
1197 			wp->base.grid->flags &= ~GRID_HISTORY;
1198 
1199 			wp->flags |= PANE_REDRAW;
1200 			break;
1201 		default:
1202 			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1203 			break;
1204 		}
1205 	} else {
1206 		switch (n) {
1207 		case 4:		/* IRM */
1208 			screen_write_insertmode(&ictx->ctx, 1);
1209 			log_debug("insert on");
1210 			break;
1211 		case 34:
1212 			/* Cursor high visibility not supported. */
1213 			break;
1214 		default:
1215 			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1216 			break;
1217 		}
1218 	}
1219 }
1220 
1221 void
1222 input_handle_sequence_rm(struct input_ctx *ictx)
1223 {
1224 	struct window_pane	*wp = ictx->wp;
1225 	struct screen		*s = &wp->base;
1226 	u_int			 sx, sy;
1227 	uint16_t		 n;
1228 
1229 	if (ARRAY_LENGTH(&ictx->args) > 1)
1230 		return;
1231 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1232 		return;
1233 
1234 	if (ictx->private == '?') {
1235 		switch (n) {
1236 		case 1:		/* GATM */
1237 			screen_write_kcursormode(&ictx->ctx, 0);
1238 			log_debug("kcursor off");
1239 			break;
1240 		case 3:		/* DECCOLM */
1241 			screen_write_cursormove(&ictx->ctx, 0, 0);
1242 			screen_write_clearscreen(&ictx->ctx);
1243 			break;
1244 		case 25:	/* TCEM */
1245 			screen_write_cursormode(&ictx->ctx, 0);
1246 			log_debug("cursor off");
1247 			break;
1248 		case 1000:
1249 			screen_write_mousemode(&ictx->ctx, 0);
1250 			log_debug("mouse off");
1251 			break;
1252 		case 1049:
1253 			if (wp->saved_grid == NULL)
1254 				break;
1255 			sx = screen_size_x(s);
1256 			sy = screen_size_y(s);
1257 
1258 			/*
1259 			 * Exit alternative screen mode and restore the copied
1260 			 * grid.
1261 			 */
1262 
1263 			/*
1264 			 * If the current size is bigger, temporarily resize
1265 			 * to the old size before copying back.
1266 			 */
1267 			if (sy > wp->saved_grid->sy)
1268 				screen_resize(s, sx, wp->saved_grid->sy);
1269 
1270 			/* Restore the grid, cursor position and cell. */
1271 			grid_duplicate_lines(
1272 			    s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
1273 			s->cx = wp->saved_cx;
1274 			if (s->cx > screen_size_x(s) - 1)
1275 				s->cx = screen_size_x(s) - 1;
1276 			s->cy = wp->saved_cy;
1277 			if (s->cy > screen_size_y(s) - 1)
1278 				s->cy = screen_size_y(s) - 1;
1279 			memcpy(&ictx->cell, &wp->saved_cell, sizeof ictx->cell);
1280 
1281 			/*
1282 			 * Turn history back on (so resize can use it) and then
1283 			 * resize back to the current size.
1284 			 */
1285   			wp->base.grid->flags |= GRID_HISTORY;
1286 			if (sy > wp->saved_grid->sy)
1287 				screen_resize(s, sx, sy);
1288 
1289 			grid_destroy(wp->saved_grid);
1290 			wp->saved_grid = NULL;
1291 
1292 			wp->flags |= PANE_REDRAW;
1293 			break;
1294 		default:
1295 			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1296 			break;
1297 		}
1298 	} else if (ictx->private == '\0') {
1299 		switch (n) {
1300 		case 4:		/* IRM */
1301 			screen_write_insertmode(&ictx->ctx, 0);
1302 			log_debug("insert off");
1303 			break;
1304 		case 34:
1305 			/* Cursor high visibility not supported. */
1306 			break;
1307 		default:
1308 			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1309 			break;
1310 		}
1311 	}
1312 }
1313 
1314 void
1315 input_handle_sequence_dsr(struct input_ctx *ictx)
1316 {
1317 	struct screen  *s = ictx->ctx.s;
1318 	uint16_t	n;
1319 	char		reply[32];
1320 
1321 	if (ARRAY_LENGTH(&ictx->args) > 1)
1322 		return;
1323 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1324 		return;
1325 
1326 	if (ictx->private == '\0') {
1327 		switch (n) {
1328 		case 6:	/* cursor position */
1329 			xsnprintf(reply, sizeof reply,
1330 			    "\033[%u;%uR", s->cy + 1, s->cx + 1);
1331 			log_debug("cursor request, reply: %s", reply);
1332 			buffer_write(ictx->wp->out, reply, strlen(reply));
1333 			break;
1334 		}
1335 	}
1336 }
1337 
1338 void
1339 input_handle_sequence_decstbm(struct input_ctx *ictx)
1340 {
1341 	struct screen  *s = ictx->ctx.s;
1342 	uint16_t	n, m;
1343 
1344 	if (ictx->private != '\0')
1345 		return;
1346 
1347 	if (ARRAY_LENGTH(&ictx->args) > 2)
1348 		return;
1349 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1350 		return;
1351 	if (input_get_argument(ictx, 1, &m, 0) != 0)
1352 		return;
1353 	if (n == 0)
1354 		n = 1;
1355 	if (m == 0)
1356 		m = screen_size_y(s);
1357 
1358 	screen_write_scrollregion(&ictx->ctx, n - 1, m - 1);
1359 }
1360 
1361 void
1362 input_handle_sequence_sgr(struct input_ctx *ictx)
1363 {
1364 	struct grid_cell       *gc = &ictx->cell;
1365 	u_int			i;
1366 	uint16_t		m, o;
1367 	u_char			attr;
1368 
1369 	if (ARRAY_LENGTH(&ictx->args) == 0) {
1370 		attr = gc->attr;
1371 		memcpy(gc, &grid_default_cell, sizeof *gc);
1372  		gc->attr |= (attr & GRID_ATTR_CHARSET);
1373 		return;
1374 	}
1375 
1376 	for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
1377 		if (input_get_argument(ictx, i, &m, 0) != 0)
1378 			return;
1379 
1380 		if (m == 38 || m == 48) {
1381 			i++;
1382 			if (input_get_argument(ictx, i, &o, 0) != 0)
1383 				return;
1384 			if (o != 5)
1385 				continue;
1386 
1387 			i++;
1388 			if (input_get_argument(ictx, i, &o, 0) != 0)
1389 				return;
1390 			if (m == 38) {
1391 				gc->flags |= GRID_FLAG_FG256;
1392 				gc->fg = o;
1393 			} else if (m == 48) {
1394 				gc->flags |= GRID_FLAG_BG256;
1395 				gc->bg = o;
1396 			}
1397 			continue;
1398 		}
1399 
1400 		switch (m) {
1401 		case 0:
1402 		case 10:
1403 			attr = gc->attr;
1404 			memcpy(gc, &grid_default_cell, sizeof *gc);
1405 			gc->attr |= (attr & GRID_ATTR_CHARSET);
1406 			break;
1407 		case 1:
1408 			gc->attr |= GRID_ATTR_BRIGHT;
1409 			break;
1410 		case 2:
1411 			gc->attr |= GRID_ATTR_DIM;
1412 			break;
1413 		case 3:
1414 			gc->attr |= GRID_ATTR_ITALICS;
1415 			break;
1416 		case 4:
1417 			gc->attr |= GRID_ATTR_UNDERSCORE;
1418 			break;
1419 		case 5:
1420 			gc->attr |= GRID_ATTR_BLINK;
1421 			break;
1422 		case 7:
1423 			gc->attr |= GRID_ATTR_REVERSE;
1424 			break;
1425 		case 8:
1426 			gc->attr |= GRID_ATTR_HIDDEN;
1427 			break;
1428 		case 22:
1429 			gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
1430 			break;
1431 		case 23:
1432 			gc->attr &= ~GRID_ATTR_ITALICS;
1433 			break;
1434 		case 24:
1435 			gc->attr &= ~GRID_ATTR_UNDERSCORE;
1436 			break;
1437 		case 25:
1438 			gc->attr &= ~GRID_ATTR_BLINK;
1439 			break;
1440 		case 27:
1441 			gc->attr &= ~GRID_ATTR_REVERSE;
1442 			break;
1443 		case 30:
1444 		case 31:
1445 		case 32:
1446 		case 33:
1447 		case 34:
1448 		case 35:
1449 		case 36:
1450 		case 37:
1451 			gc->flags &= ~GRID_FLAG_FG256;
1452 			gc->fg = m - 30;
1453 			break;
1454 		case 39:
1455 			gc->flags &= ~GRID_FLAG_FG256;
1456 			gc->fg = 8;
1457 			break;
1458 		case 40:
1459 		case 41:
1460 		case 42:
1461 		case 43:
1462 		case 44:
1463 		case 45:
1464 		case 46:
1465 		case 47:
1466 			gc->flags &= ~GRID_FLAG_BG256;
1467 			gc->bg = m - 40;
1468 			break;
1469 		case 49:
1470 			gc->flags &= ~GRID_FLAG_BG256;
1471 			gc->bg = 8;
1472 			break;
1473 		case 90:
1474 		case 91:
1475 		case 92:
1476 		case 93:
1477 		case 94:
1478 		case 95:
1479 		case 96:
1480 		case 97:
1481 			gc->flags |= GRID_FLAG_FG256;
1482 			gc->fg = m - 82;
1483 			break;
1484 		case 100:
1485 		case 101:
1486 		case 102:
1487 		case 103:
1488 		case 104:
1489 		case 105:
1490 		case 106:
1491 		case 107:
1492 			gc->flags |= GRID_FLAG_BG256;
1493 			gc->bg = m - 92;
1494 			break;
1495 		}
1496 	}
1497 }
1498