xref: /openbsd-src/usr.bin/tmux/input.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: input.c,v 1.16 2009/10/12 16:59:55 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 
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("-- 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 
587 void
588 input_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 
629 void
630 input_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, 0);
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_backspace(&ictx->ctx);
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, 0);
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 
678 void
679 input_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, 0);
688 		break;
689 	case 'E': 	/* NEL */
690 		screen_write_carriagereturn(&ictx->ctx);
691 		screen_write_linefeed(&ictx->ctx, 0);
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 
706 void
707 input_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 
765 void
766 input_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 
814 void
815 input_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 
842 void
843 input_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 
860 void
861 input_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 
878 void
879 input_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 
896 void
897 input_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 
914 void
915 input_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 
932 void
933 input_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 
956 void
957 input_handle_sequence_da(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, 0) != 0)
967 		return;
968 	if (n != 0)
969 		return;
970 
971 	buffer_write(ictx->wp->out, "\033[?1;2c", (sizeof "\033[?1;2c") - 1);
972 }
973 
974 void
975 input_handle_sequence_dl(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_deleteline(&ictx->ctx, n);
990 }
991 
992 void
993 input_handle_sequence_ich(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_insertcharacter(&ictx->ctx, n);
1008 }
1009 
1010 void
1011 input_handle_sequence_il(struct input_ctx *ictx)
1012 {
1013 	uint16_t	n;
1014 
1015 	if (ictx->private != '\0')
1016 		return;
1017 
1018 	if (ARRAY_LENGTH(&ictx->args) > 1)
1019 		return;
1020 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1021 		return;
1022 	if (n == 0)
1023 		n = 1;
1024 
1025 	screen_write_insertline(&ictx->ctx, n);
1026 }
1027 
1028 void
1029 input_handle_sequence_vpa(struct input_ctx *ictx)
1030 {
1031 	struct screen  *s = ictx->ctx.s;
1032 	uint16_t	n;
1033 
1034 	if (ictx->private != '\0')
1035 		return;
1036 
1037 	if (ARRAY_LENGTH(&ictx->args) > 1)
1038 		return;
1039 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1040 		return;
1041 	if (n == 0)
1042 		n = 1;
1043 
1044 	screen_write_cursormove(&ictx->ctx, s->cx, n - 1);
1045 }
1046 
1047 void
1048 input_handle_sequence_hpa(struct input_ctx *ictx)
1049 {
1050 	struct screen  *s = ictx->ctx.s;
1051 	uint16_t	n;
1052 
1053 	if (ictx->private != '\0')
1054 		return;
1055 
1056 	if (ARRAY_LENGTH(&ictx->args) > 1)
1057 		return;
1058 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1059 		return;
1060 	if (n == 0)
1061 		n = 1;
1062 
1063 	screen_write_cursormove(&ictx->ctx, n - 1, s->cy);
1064 }
1065 
1066 void
1067 input_handle_sequence_cup(struct input_ctx *ictx)
1068 {
1069 	uint16_t	n, m;
1070 
1071 	if (ictx->private != '\0')
1072 		return;
1073 
1074 	if (ARRAY_LENGTH(&ictx->args) > 2)
1075 		return;
1076 	if (input_get_argument(ictx, 0, &n, 1) != 0)
1077 		return;
1078 	if (input_get_argument(ictx, 1, &m, 1) != 0)
1079 		return;
1080 	if (n == 0)
1081 		n = 1;
1082 	if (m == 0)
1083 		m = 1;
1084 
1085 	screen_write_cursormove(&ictx->ctx, m - 1, n - 1);
1086 }
1087 
1088 void
1089 input_handle_sequence_tbc(struct input_ctx *ictx)
1090 {
1091 	struct screen  *s = ictx->ctx.s;
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, 1) != 0)
1100 		return;
1101 
1102 	switch (n) {
1103 	case 0:
1104 		if (s->cx < screen_size_x(s))
1105 			bit_clear(s->tabs, s->cx);
1106 		break;
1107 	case 3:
1108 		bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
1109 		break;
1110 	}
1111 }
1112 
1113 void
1114 input_handle_sequence_ed(struct input_ctx *ictx)
1115 {
1116 	uint16_t	n;
1117 
1118 	if (ictx->private != '\0')
1119 		return;
1120 
1121 	if (ARRAY_LENGTH(&ictx->args) > 1)
1122 		return;
1123 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1124 		return;
1125 	if (n > 2)
1126 		return;
1127 
1128 	switch (n) {
1129 	case 0:
1130 		screen_write_clearendofscreen(&ictx->ctx);
1131 		break;
1132 	case 1:
1133 		screen_write_clearstartofscreen(&ictx->ctx);
1134 		break;
1135 	case 2:
1136 		screen_write_clearscreen(&ictx->ctx);
1137 		break;
1138 	}
1139 }
1140 
1141 void
1142 input_handle_sequence_el(struct input_ctx *ictx)
1143 {
1144 	uint16_t	n;
1145 
1146 	if (ictx->private != '\0')
1147 		return;
1148 
1149 	if (ARRAY_LENGTH(&ictx->args) > 1)
1150 		return;
1151 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1152 		return;
1153 	if (n > 2)
1154 		return;
1155 
1156 	switch (n) {
1157 	case 0:
1158 		screen_write_clearendofline(&ictx->ctx);
1159 		break;
1160 	case 1:
1161 		screen_write_clearstartofline(&ictx->ctx);
1162 		break;
1163 	case 2:
1164 		screen_write_clearline(&ictx->ctx);
1165 		break;
1166 	}
1167 }
1168 
1169 void
1170 input_handle_sequence_sm(struct input_ctx *ictx)
1171 {
1172 	struct window_pane	*wp = ictx->wp;
1173 	struct screen		*s = &wp->base;
1174 	u_int			 sx, sy;
1175 	uint16_t		 n;
1176 
1177 	if (ARRAY_LENGTH(&ictx->args) > 1)
1178 		return;
1179 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1180 		return;
1181 
1182 	if (ictx->private == '?') {
1183 		switch (n) {
1184 		case 1:		/* GATM */
1185 			screen_write_kcursormode(&ictx->ctx, 1);
1186 			log_debug("kcursor on");
1187 			break;
1188 		case 25:	/* TCEM */
1189 			screen_write_cursormode(&ictx->ctx, 1);
1190 			log_debug("cursor on");
1191 			break;
1192 		case 1000:
1193 			screen_write_mousemode(&ictx->ctx, 1);
1194 			log_debug("mouse on");
1195 			break;
1196 		case 1049:
1197 			if (wp->saved_grid != NULL)
1198 				break;
1199 			sx = screen_size_x(s);
1200 			sy = screen_size_y(s);
1201 
1202 			/*
1203 			 * Enter alternative screen mode. A copy of the visible
1204 			 * screen is saved and the history is not updated
1205 			 */
1206 
1207 			wp->saved_grid = grid_create(sx, sy, 0);
1208 			grid_duplicate_lines(
1209 			    wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
1210 			wp->saved_cx = s->cx;
1211 			wp->saved_cy = s->cy;
1212 			memcpy(&wp->saved_cell,
1213 			    &ictx->cell, sizeof wp->saved_cell);
1214 
1215 			grid_view_clear(s->grid, 0, 0, sx, sy);
1216 
1217 			wp->base.grid->flags &= ~GRID_HISTORY;
1218 
1219 			wp->flags |= PANE_REDRAW;
1220 			break;
1221 		default:
1222 			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1223 			break;
1224 		}
1225 	} else {
1226 		switch (n) {
1227 		case 4:		/* IRM */
1228 			screen_write_insertmode(&ictx->ctx, 1);
1229 			log_debug("insert on");
1230 			break;
1231 		case 34:
1232 			/* Cursor high visibility not supported. */
1233 			break;
1234 		default:
1235 			log_debug("unknown SM [%hhu]: %u", ictx->private, n);
1236 			break;
1237 		}
1238 	}
1239 }
1240 
1241 void
1242 input_handle_sequence_rm(struct input_ctx *ictx)
1243 {
1244 	struct window_pane	*wp = ictx->wp;
1245 	struct screen		*s = &wp->base;
1246 	u_int			 sx, sy;
1247 	uint16_t		 n;
1248 
1249 	if (ARRAY_LENGTH(&ictx->args) > 1)
1250 		return;
1251 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1252 		return;
1253 
1254 	if (ictx->private == '?') {
1255 		switch (n) {
1256 		case 1:		/* GATM */
1257 			screen_write_kcursormode(&ictx->ctx, 0);
1258 			log_debug("kcursor off");
1259 			break;
1260 		case 25:	/* TCEM */
1261 			screen_write_cursormode(&ictx->ctx, 0);
1262 			log_debug("cursor off");
1263 			break;
1264 		case 1000:
1265 			screen_write_mousemode(&ictx->ctx, 0);
1266 			log_debug("mouse off");
1267 			break;
1268 		case 1049:
1269 			if (wp->saved_grid == NULL)
1270 				break;
1271 			sx = screen_size_x(s);
1272 			sy = screen_size_y(s);
1273 
1274 			/*
1275 			 * Exit alternative screen mode and restore the copied
1276 			 * grid.
1277 			 */
1278 
1279 			/*
1280 			 * If the current size is bigger, temporarily resize
1281 			 * to the old size before copying back.
1282 			 */
1283 			if (sy > wp->saved_grid->sy)
1284 				screen_resize(s, sx, wp->saved_grid->sy);
1285 
1286 			/* Restore the grid, cursor position and cell. */
1287 			grid_duplicate_lines(
1288 			    s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
1289 			s->cx = wp->saved_cx;
1290 			if (s->cx > screen_size_x(s) - 1)
1291 				s->cx = screen_size_x(s) - 1;
1292 			s->cy = wp->saved_cy;
1293 			if (s->cy > screen_size_y(s) - 1)
1294 				s->cy = screen_size_y(s) - 1;
1295 			memcpy(&ictx->cell, &wp->saved_cell, sizeof ictx->cell);
1296 
1297 			/*
1298 			 * Turn history back on (so resize can use it) and then
1299 			 * resize back to the current size.
1300 			 */
1301   			wp->base.grid->flags |= GRID_HISTORY;
1302 			if (sy > wp->saved_grid->sy)
1303 				screen_resize(s, sx, sy);
1304 
1305 			grid_destroy(wp->saved_grid);
1306 			wp->saved_grid = NULL;
1307 
1308 			wp->flags |= PANE_REDRAW;
1309 			break;
1310 		default:
1311 			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1312 			break;
1313 		}
1314 	} else if (ictx->private == '\0') {
1315 		switch (n) {
1316 		case 4:		/* IRM */
1317 			screen_write_insertmode(&ictx->ctx, 0);
1318 			log_debug("insert off");
1319 			break;
1320 		case 34:
1321 			/* Cursor high visibility not supported. */
1322 			break;
1323 		default:
1324 			log_debug("unknown RM [%hhu]: %u", ictx->private, n);
1325 			break;
1326 		}
1327 	}
1328 }
1329 
1330 void
1331 input_handle_sequence_dsr(struct input_ctx *ictx)
1332 {
1333 	struct screen  *s = ictx->ctx.s;
1334 	uint16_t	n;
1335 	char		reply[32];
1336 
1337 	if (ARRAY_LENGTH(&ictx->args) > 1)
1338 		return;
1339 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1340 		return;
1341 
1342 	if (ictx->private == '\0') {
1343 		switch (n) {
1344 		case 6:	/* cursor position */
1345 			xsnprintf(reply, sizeof reply,
1346 			    "\033[%u;%uR", s->cy + 1, s->cx + 1);
1347 			log_debug("cursor request, reply: %s", reply);
1348 			buffer_write(ictx->wp->out, reply, strlen(reply));
1349 			break;
1350 		}
1351 	}
1352 }
1353 
1354 void
1355 input_handle_sequence_decstbm(struct input_ctx *ictx)
1356 {
1357 	struct screen  *s = ictx->ctx.s;
1358 	uint16_t	n, m;
1359 
1360 	if (ictx->private != '\0')
1361 		return;
1362 
1363 	if (ARRAY_LENGTH(&ictx->args) > 2)
1364 		return;
1365 	if (input_get_argument(ictx, 0, &n, 0) != 0)
1366 		return;
1367 	if (input_get_argument(ictx, 1, &m, 0) != 0)
1368 		return;
1369 	if (n == 0)
1370 		n = 1;
1371 	if (m == 0)
1372 		m = screen_size_y(s);
1373 
1374 	screen_write_scrollregion(&ictx->ctx, n - 1, m - 1);
1375 }
1376 
1377 void
1378 input_handle_sequence_sgr(struct input_ctx *ictx)
1379 {
1380 	struct grid_cell       *gc = &ictx->cell;
1381 	u_int			i;
1382 	uint16_t		m, o;
1383 	u_char			attr;
1384 
1385 	if (ARRAY_LENGTH(&ictx->args) == 0) {
1386 		attr = gc->attr;
1387 		memcpy(gc, &grid_default_cell, sizeof *gc);
1388  		gc->attr |= (attr & GRID_ATTR_CHARSET);
1389 		return;
1390 	}
1391 
1392 	for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
1393 		if (input_get_argument(ictx, i, &m, 0) != 0)
1394 			return;
1395 
1396 		if (m == 38 || m == 48) {
1397 			i++;
1398 			if (input_get_argument(ictx, i, &o, 0) != 0)
1399 				return;
1400 			if (o != 5)
1401 				continue;
1402 
1403 			i++;
1404 			if (input_get_argument(ictx, i, &o, 0) != 0)
1405 				return;
1406 			if (m == 38) {
1407 				gc->flags |= GRID_FLAG_FG256;
1408 				gc->fg = o;
1409 			} else if (m == 48) {
1410 				gc->flags |= GRID_FLAG_BG256;
1411 				gc->bg = o;
1412 			}
1413 			continue;
1414 		}
1415 
1416 		switch (m) {
1417 		case 0:
1418 		case 10:
1419 			attr = gc->attr;
1420 			memcpy(gc, &grid_default_cell, sizeof *gc);
1421 			gc->attr |= (attr & GRID_ATTR_CHARSET);
1422 			break;
1423 		case 1:
1424 			gc->attr |= GRID_ATTR_BRIGHT;
1425 			break;
1426 		case 2:
1427 			gc->attr |= GRID_ATTR_DIM;
1428 			break;
1429 		case 3:
1430 			gc->attr |= GRID_ATTR_ITALICS;
1431 			break;
1432 		case 4:
1433 			gc->attr |= GRID_ATTR_UNDERSCORE;
1434 			break;
1435 		case 5:
1436 			gc->attr |= GRID_ATTR_BLINK;
1437 			break;
1438 		case 7:
1439 			gc->attr |= GRID_ATTR_REVERSE;
1440 			break;
1441 		case 8:
1442 			gc->attr |= GRID_ATTR_HIDDEN;
1443 			break;
1444 		case 22:
1445 			gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
1446 			break;
1447 		case 23:
1448 			gc->attr &= ~GRID_ATTR_ITALICS;
1449 			break;
1450 		case 24:
1451 			gc->attr &= ~GRID_ATTR_UNDERSCORE;
1452 			break;
1453 		case 25:
1454 			gc->attr &= ~GRID_ATTR_BLINK;
1455 			break;
1456 		case 27:
1457 			gc->attr &= ~GRID_ATTR_REVERSE;
1458 			break;
1459 		case 30:
1460 		case 31:
1461 		case 32:
1462 		case 33:
1463 		case 34:
1464 		case 35:
1465 		case 36:
1466 		case 37:
1467 			gc->flags &= ~GRID_FLAG_FG256;
1468 			gc->fg = m - 30;
1469 			break;
1470 		case 39:
1471 			gc->flags &= ~GRID_FLAG_FG256;
1472 			gc->fg = 8;
1473 			break;
1474 		case 40:
1475 		case 41:
1476 		case 42:
1477 		case 43:
1478 		case 44:
1479 		case 45:
1480 		case 46:
1481 		case 47:
1482 			gc->flags &= ~GRID_FLAG_BG256;
1483 			gc->bg = m - 40;
1484 			break;
1485 		case 49:
1486 			gc->flags &= ~GRID_FLAG_BG256;
1487 			gc->bg = 8;
1488 			break;
1489 		}
1490 	}
1491 }
1492