xref: /freebsd/sys/teken/teken.c (revision 41059135ce931c0f1014a999ffabc6bc470ce856)
1 /*-
2  * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/cdefs.h>
30 #if defined(__FreeBSD__) && defined(_KERNEL)
31 #include <sys/param.h>
32 #include <sys/limits.h>
33 #include <sys/lock.h>
34 #include <sys/systm.h>
35 #define	teken_assert(x)		MPASS(x)
36 #else /* !(__FreeBSD__ && _KERNEL) */
37 #include <sys/types.h>
38 #include <assert.h>
39 #include <limits.h>
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <string.h>
43 #define	teken_assert(x)		assert(x)
44 #endif /* __FreeBSD__ && _KERNEL */
45 
46 /* debug messages */
47 #define	teken_printf(x,...)
48 
49 /* Private flags for t_stateflags. */
50 #define	TS_FIRSTDIGIT	0x0001	/* First numeric digit in escape sequence. */
51 #define	TS_INSERT	0x0002	/* Insert mode. */
52 #define	TS_AUTOWRAP	0x0004	/* Autowrap. */
53 #define	TS_ORIGIN	0x0008	/* Origin mode. */
54 #define	TS_WRAPPED	0x0010	/* Next character should be printed on col 0. */
55 #define	TS_8BIT		0x0020	/* UTF-8 disabled. */
56 #define	TS_CONS25	0x0040	/* cons25 emulation. */
57 #define	TS_INSTRING	0x0080	/* Inside string. */
58 #define	TS_CURSORKEYS	0x0100	/* Cursor keys mode. */
59 
60 /* Character that blanks a cell. */
61 #define	BLANK	' '
62 
63 #include "teken.h"
64 #include "teken_wcwidth.h"
65 #include "teken_scs.h"
66 
67 static teken_state_t	teken_state_init;
68 
69 /*
70  * Wrappers for hooks.
71  */
72 
73 static inline void
74 teken_funcs_bell(teken_t *t)
75 {
76 
77 	t->t_funcs->tf_bell(t->t_softc);
78 }
79 
80 static inline void
81 teken_funcs_cursor(teken_t *t)
82 {
83 
84 	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
85 	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
86 
87 	t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor);
88 }
89 
90 static inline void
91 teken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c,
92     const teken_attr_t *a)
93 {
94 
95 	teken_assert(p->tp_row < t->t_winsize.tp_row);
96 	teken_assert(p->tp_col < t->t_winsize.tp_col);
97 
98 	t->t_funcs->tf_putchar(t->t_softc, p, c, a);
99 }
100 
101 static inline void
102 teken_funcs_fill(teken_t *t, const teken_rect_t *r,
103     const teken_char_t c, const teken_attr_t *a)
104 {
105 
106 	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
107 	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
108 	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
109 	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
110 
111 	t->t_funcs->tf_fill(t->t_softc, r, c, a);
112 }
113 
114 static inline void
115 teken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
116 {
117 
118 	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
119 	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
120 	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
121 	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
122 	teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row);
123 	teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col);
124 
125 	t->t_funcs->tf_copy(t->t_softc, r, p);
126 }
127 
128 static inline void
129 teken_funcs_param(teken_t *t, int cmd, unsigned int value)
130 {
131 
132 	t->t_funcs->tf_param(t->t_softc, cmd, value);
133 }
134 
135 static inline void
136 teken_funcs_respond(teken_t *t, const void *buf, size_t len)
137 {
138 
139 	t->t_funcs->tf_respond(t->t_softc, buf, len);
140 }
141 
142 #include "teken_subr.h"
143 #include "teken_subr_compat.h"
144 
145 /*
146  * Programming interface.
147  */
148 
149 void
150 teken_init(teken_t *t, const teken_funcs_t *tf, void *softc)
151 {
152 	teken_pos_t tp = { .tp_row = 24, .tp_col = 80 };
153 
154 	t->t_funcs = tf;
155 	t->t_softc = softc;
156 
157 	t->t_nextstate = teken_state_init;
158 	t->t_stateflags = 0;
159 	t->t_utf8_left = 0;
160 
161 	t->t_defattr.ta_format = 0;
162 	t->t_defattr.ta_fgcolor = TC_WHITE;
163 	t->t_defattr.ta_bgcolor = TC_BLACK;
164 	teken_subr_do_reset(t);
165 
166 	teken_set_winsize(t, &tp);
167 }
168 
169 static void
170 teken_input_char(teken_t *t, teken_char_t c)
171 {
172 
173 	/*
174 	 * There is no support for DCS and OSC.  Just discard strings
175 	 * until we receive characters that may indicate string
176 	 * termination.
177 	 */
178 	if (t->t_stateflags & TS_INSTRING) {
179 		switch (c) {
180 		case '\x1B':
181 			t->t_stateflags &= ~TS_INSTRING;
182 			break;
183 		case '\a':
184 			t->t_stateflags &= ~TS_INSTRING;
185 			return;
186 		default:
187 			return;
188 		}
189 	}
190 
191 	switch (c) {
192 	case '\0':
193 		break;
194 	case '\a':
195 		teken_subr_bell(t);
196 		break;
197 	case '\b':
198 		teken_subr_backspace(t);
199 		break;
200 	case '\n':
201 	case '\x0B':
202 		teken_subr_newline(t);
203 		break;
204 	case '\x0C':
205 		teken_subr_newpage(t);
206 		break;
207 	case '\x0E':
208 		if (t->t_stateflags & TS_CONS25)
209 			t->t_nextstate(t, c);
210 		else
211 			t->t_curscs = 1;
212 		break;
213 	case '\x0F':
214 		if (t->t_stateflags & TS_CONS25)
215 			t->t_nextstate(t, c);
216 		else
217 			t->t_curscs = 0;
218 		break;
219 	case '\r':
220 		teken_subr_carriage_return(t);
221 		break;
222 	case '\t':
223 		teken_subr_horizontal_tab(t);
224 		break;
225 	default:
226 		t->t_nextstate(t, c);
227 		break;
228 	}
229 
230 	/* Post-processing assertions. */
231 	teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin);
232 	teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end);
233 	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
234 	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
235 	teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row);
236 	teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col);
237 	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
238 	teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end);
239 	/* Origin region has to be window size or the same as scrollreg. */
240 	teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin &&
241 	    t->t_originreg.ts_end == t->t_scrollreg.ts_end) ||
242 	    (t->t_originreg.ts_begin == 0 &&
243 	    t->t_originreg.ts_end == t->t_winsize.tp_row));
244 }
245 
246 static void
247 teken_input_byte(teken_t *t, unsigned char c)
248 {
249 
250 	/*
251 	 * UTF-8 handling.
252 	 */
253 	if ((c & 0x80) == 0x00 || t->t_stateflags & TS_8BIT) {
254 		/* One-byte sequence. */
255 		t->t_utf8_left = 0;
256 		teken_input_char(t, c);
257 	} else if ((c & 0xe0) == 0xc0) {
258 		/* Two-byte sequence. */
259 		t->t_utf8_left = 1;
260 		t->t_utf8_partial = c & 0x1f;
261 	} else if ((c & 0xf0) == 0xe0) {
262 		/* Three-byte sequence. */
263 		t->t_utf8_left = 2;
264 		t->t_utf8_partial = c & 0x0f;
265 	} else if ((c & 0xf8) == 0xf0) {
266 		/* Four-byte sequence. */
267 		t->t_utf8_left = 3;
268 		t->t_utf8_partial = c & 0x07;
269 	} else if ((c & 0xc0) == 0x80) {
270 		if (t->t_utf8_left == 0)
271 			return;
272 		t->t_utf8_left--;
273 		t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f);
274 		if (t->t_utf8_left == 0) {
275 			teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial);
276 			teken_input_char(t, t->t_utf8_partial);
277 		}
278 	}
279 }
280 
281 void
282 teken_input(teken_t *t, const void *buf, size_t len)
283 {
284 	const char *c = buf;
285 
286 	while (len-- > 0)
287 		teken_input_byte(t, *c++);
288 }
289 
290 const teken_pos_t *
291 teken_get_cursor(teken_t *t)
292 {
293 
294 	return (&t->t_cursor);
295 }
296 
297 void
298 teken_set_cursor(teken_t *t, const teken_pos_t *p)
299 {
300 
301 	/* XXX: bounds checking with originreg! */
302 	teken_assert(p->tp_row < t->t_winsize.tp_row);
303 	teken_assert(p->tp_col < t->t_winsize.tp_col);
304 
305 	t->t_cursor = *p;
306 }
307 
308 const teken_attr_t *
309 teken_get_curattr(teken_t *t)
310 {
311 
312 	return (&t->t_curattr);
313 }
314 
315 void
316 teken_set_curattr(teken_t *t, const teken_attr_t *a)
317 {
318 
319 	t->t_curattr = *a;
320 }
321 
322 const teken_attr_t *
323 teken_get_defattr(teken_t *t)
324 {
325 
326 	return (&t->t_defattr);
327 }
328 
329 void
330 teken_set_defattr(teken_t *t, const teken_attr_t *a)
331 {
332 
333 	t->t_curattr = t->t_saved_curattr = t->t_defattr = *a;
334 }
335 
336 const teken_pos_t *
337 teken_get_winsize(teken_t *t)
338 {
339 
340 	return (&t->t_winsize);
341 }
342 
343 static void
344 teken_trim_cursor_pos(teken_t *t, const teken_pos_t *new)
345 {
346 	const teken_pos_t *cur;
347 
348 	cur = &t->t_winsize;
349 
350 	if (cur->tp_row < new->tp_row || cur->tp_col < new->tp_col)
351 		return;
352 	if (t->t_cursor.tp_row >= new->tp_row)
353 		t->t_cursor.tp_row = new->tp_row - 1;
354 	if (t->t_cursor.tp_col >= new->tp_col)
355 		t->t_cursor.tp_col = new->tp_col - 1;
356 }
357 
358 void
359 teken_set_winsize(teken_t *t, const teken_pos_t *p)
360 {
361 
362 	teken_trim_cursor_pos(t, p);
363 	t->t_winsize = *p;
364 	teken_subr_do_reset(t);
365 }
366 
367 void
368 teken_set_winsize_noreset(teken_t *t, const teken_pos_t *p)
369 {
370 
371 	teken_trim_cursor_pos(t, p);
372 	t->t_winsize = *p;
373 	teken_subr_do_resize(t);
374 }
375 
376 void
377 teken_set_8bit(teken_t *t)
378 {
379 
380 	t->t_stateflags |= TS_8BIT;
381 }
382 
383 void
384 teken_set_cons25(teken_t *t)
385 {
386 
387 	t->t_stateflags |= TS_CONS25;
388 }
389 
390 /*
391  * State machine.
392  */
393 
394 static void
395 teken_state_switch(teken_t *t, teken_state_t *s)
396 {
397 
398 	t->t_nextstate = s;
399 	t->t_curnum = 0;
400 	t->t_stateflags |= TS_FIRSTDIGIT;
401 }
402 
403 static int
404 teken_state_numbers(teken_t *t, teken_char_t c)
405 {
406 
407 	teken_assert(t->t_curnum < T_NUMSIZE);
408 
409 	if (c >= '0' && c <= '9') {
410 		if (t->t_stateflags & TS_FIRSTDIGIT) {
411 			/* First digit. */
412 			t->t_stateflags &= ~TS_FIRSTDIGIT;
413 			t->t_nums[t->t_curnum] = c - '0';
414 		} else if (t->t_nums[t->t_curnum] < UINT_MAX / 100) {
415 			/*
416 			 * There is no need to continue parsing input
417 			 * once the value exceeds the size of the
418 			 * terminal. It would only allow for integer
419 			 * overflows when performing arithmetic on the
420 			 * cursor position.
421 			 *
422 			 * Ignore any further digits if the value is
423 			 * already UINT_MAX / 100.
424 			 */
425 			t->t_nums[t->t_curnum] =
426 			    t->t_nums[t->t_curnum] * 10 + c - '0';
427 		}
428 		return (1);
429 	} else if (c == ';') {
430 		if (t->t_stateflags & TS_FIRSTDIGIT)
431 			t->t_nums[t->t_curnum] = 0;
432 
433 		/* Only allow a limited set of arguments. */
434 		if (++t->t_curnum == T_NUMSIZE) {
435 			teken_state_switch(t, teken_state_init);
436 			return (1);
437 		}
438 
439 		t->t_stateflags |= TS_FIRSTDIGIT;
440 		return (1);
441 	} else {
442 		if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
443 			/* Finish off the last empty argument. */
444 			t->t_nums[t->t_curnum] = 0;
445 			t->t_curnum++;
446 		} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
447 			/* Also count the last argument. */
448 			t->t_curnum++;
449 		}
450 	}
451 
452 	return (0);
453 }
454 
455 #define	k	TC_BLACK
456 #define	b	TC_BLUE
457 #define	y	TC_BROWN
458 #define	c	TC_CYAN
459 #define	g	TC_GREEN
460 #define	m	TC_MAGENTA
461 #define	r	TC_RED
462 #define	w	TC_WHITE
463 #define	K	(TC_BLACK | TC_LIGHT)
464 #define	B	(TC_BLUE | TC_LIGHT)
465 #define	Y	(TC_BROWN | TC_LIGHT)
466 #define	C	(TC_CYAN | TC_LIGHT)
467 #define	G	(TC_GREEN | TC_LIGHT)
468 #define	M	(TC_MAGENTA | TC_LIGHT)
469 #define	R	(TC_RED | TC_LIGHT)
470 #define	W	(TC_WHITE | TC_LIGHT)
471 
472 /**
473  * The xterm-256 color map has steps of 0x28 (in the range 0-0xff), except
474  * for the first step which is 0x5f.  Scale to the range 0-6 by dividing
475  * by 0x28 and rounding down.  The range of 0-5 cannot represent the
476  * larger first step.
477  *
478  * This table is generated by the follow rules:
479  * - if all components are equal, the result is black for (0, 0, 0) and
480  *   (2, 2, 2), else white; otherwise:
481  * - subtract the smallest component from all components
482  * - if this gives only one nonzero component, then that is the color
483  * - else if one component is 2 or more larger than the other nonzero one,
484  *   then that component gives the color
485  * - else there are 2 nonzero components.  The color is that of a small
486  *   equal mixture of these components (cyan, yellow or magenta).  E.g.,
487  *   (0, 5, 6) (Turquoise2) is a much purer cyan than (0, 2, 3)
488  *   (DeepSkyBlue4), but we map both to cyan since we can't represent
489  *   delicate shades of either blue or cyan and blue would be worse.
490  *   Here it is important that components of 1 never occur.  Blue would
491  *   be twice as large as green in (0, 1, 2).
492  */
493 static const teken_color_t teken_256to8tab[] = {
494 	/* xterm normal colors: */
495 	k, r, g, y, b, m, c, w,
496 
497 	/* xterm bright colors: */
498 	k, r, g, y, b, m, c, w,
499 
500 	/* Red0 submap. */
501 	k, b, b, b, b, b,
502 	g, c, c, b, b, b,
503 	g, c, c, c, b, b,
504 	g, g, c, c, c, b,
505 	g, g, g, c, c, c,
506 	g, g, g, g, c, c,
507 
508 	/* Red2 submap. */
509 	r, m, m, b, b, b,
510 	y, k, b, b, b, b,
511 	y, g, c, c, b, b,
512 	g, g, c, c, c, b,
513 	g, g, g, c, c, c,
514 	g, g, g, g, c, c,
515 
516 	/* Red3 submap. */
517 	r, m, m, m, b, b,
518 	y, r, m, m, b, b,
519 	y, y, w, b, b, b,
520 	y, y, g, c, c, b,
521 	g, g, g, c, c, c,
522 	g, g, g, g, c, c,
523 
524 	/* Red4 submap. */
525 	r, r, m, m, m, b,
526 	r, r, m, m, m, b,
527 	y, y, r, m, m, b,
528 	y, y, y, w, b, b,
529 	y, y, y, g, c, c,
530 	g, g, g, g, c, c,
531 
532 	/* Red5 submap. */
533 	r, r, r, m, m, m,
534 	r, r, r, m, m, m,
535 	r, r, r, m, m, m,
536 	y, y, y, r, m, m,
537 	y, y, y, y, w, b,
538 	y, y, y, y, g, c,
539 
540 	/* Red6 submap. */
541 	r, r, r, r, m, m,
542 	r, r, r, r, m, m,
543 	r, r, r, r, m, m,
544 	r, r, r, r, m, m,
545 	y, y, y, y, r, m,
546 	y, y, y, y, y, w,
547 
548 	/* Grey submap. */
549 	k, k, k, k, k, k,
550 	k, k, k, k, k, k,
551 	w, w, w, w, w, w,
552 	w, w, w, w, w, w,
553 };
554 
555 /*
556  * This table is generated from the previous one by setting TC_LIGHT for
557  * entries whose luminosity in the xterm256 color map is 60% or larger.
558  * Thus the previous table is currently not really needed.  It will be
559  * used for different fine tuning of the tables.
560  */
561 static const teken_color_t teken_256to16tab[] = {
562 	/* xterm normal colors: */
563 	k, r, g, y, b, m, c, w,
564 
565 	/* xterm bright colors: */
566 	K, R, G, Y, B, M, C, W,
567 
568 	/* Red0 submap. */
569 	k, b, b, b, b, b,
570 	g, c, c, b, b, b,
571 	g, c, c, c, b, b,
572 	g, g, c, c, c, b,
573 	g, g, g, c, c, c,
574 	g, g, g, g, c, c,
575 
576 	/* Red2 submap. */
577 	r, m, m, b, b, b,
578 	y, K, b, b, B, B,
579 	y, g, c, c, B, B,
580 	g, g, c, c, C, B,
581 	g, G, G, C, C, C,
582 	g, G, G, G, C, C,
583 
584 	/* Red3 submap. */
585 	r, m, m, m, b, b,
586 	y, r, m, m, B, B,
587 	y, y, w, B, B, B,
588 	y, y, G, C, C, B,
589 	g, G, G, C, C, C,
590 	g, G, G, G, C, C,
591 
592 	/* Red4 submap. */
593 	r, r, m, m, m, b,
594 	r, r, m, m, M, B,
595 	y, y, R, M, M, B,
596 	y, y, Y, W, B, B,
597 	y, Y, Y, G, C, C,
598 	g, G, G, G, C, C,
599 
600 	/* Red5 submap. */
601 	r, r, r, m, m, m,
602 	r, R, R, M, M, M,
603 	r, R, R, M, M, M,
604 	y, Y, Y, R, M, M,
605 	y, Y, Y, Y, W, B,
606 	y, Y, Y, Y, G, C,
607 
608 	/* Red6 submap. */
609 	r, r, r, r, m, m,
610 	r, R, R, R, M, M,
611 	r, R, R, R, M, M,
612 	r, R, R, R, M, M,
613 	y, Y, Y, Y, R, M,
614 	y, Y, Y, Y, Y, W,
615 
616 	/* Grey submap. */
617 	k, k, k, k, k, k,
618 	K, K, K, K, K, K,
619 	w, w, w, w, w, w,
620 	W, W, W, W, W, W,
621 };
622 
623 #undef	k
624 #undef	b
625 #undef	y
626 #undef	c
627 #undef	g
628 #undef	m
629 #undef	r
630 #undef	w
631 #undef	K
632 #undef	B
633 #undef	Y
634 #undef	C
635 #undef	G
636 #undef	M
637 #undef	R
638 #undef	W
639 
640 teken_color_t
641 teken_256to8(teken_color_t c)
642 {
643 
644 	return (teken_256to8tab[c % 256]);
645 }
646 
647 teken_color_t
648 teken_256to16(teken_color_t c)
649 {
650 
651 	return (teken_256to16tab[c % 256]);
652 }
653 
654 static const char * const special_strings_cons25[] = {
655 	[TKEY_UP] = "\x1B[A",		[TKEY_DOWN] = "\x1B[B",
656 	[TKEY_LEFT] = "\x1B[D",		[TKEY_RIGHT] = "\x1B[C",
657 
658 	[TKEY_HOME] = "\x1B[H",		[TKEY_END] = "\x1B[F",
659 	[TKEY_INSERT] = "\x1B[L",	[TKEY_DELETE] = "\x7F",
660 	[TKEY_PAGE_UP] = "\x1B[I",	[TKEY_PAGE_DOWN] = "\x1B[G",
661 
662 	[TKEY_F1] = "\x1B[M",		[TKEY_F2] = "\x1B[N",
663 	[TKEY_F3] = "\x1B[O",		[TKEY_F4] = "\x1B[P",
664 	[TKEY_F5] = "\x1B[Q",		[TKEY_F6] = "\x1B[R",
665 	[TKEY_F7] = "\x1B[S",		[TKEY_F8] = "\x1B[T",
666 	[TKEY_F9] = "\x1B[U",		[TKEY_F10] = "\x1B[V",
667 	[TKEY_F11] = "\x1B[W",		[TKEY_F12] = "\x1B[X",
668 };
669 
670 static const char * const special_strings_ckeys[] = {
671 	[TKEY_UP] = "\x1BOA",		[TKEY_DOWN] = "\x1BOB",
672 	[TKEY_LEFT] = "\x1BOD",		[TKEY_RIGHT] = "\x1BOC",
673 
674 	[TKEY_HOME] = "\x1BOH",		[TKEY_END] = "\x1BOF",
675 };
676 
677 static const char * const special_strings_normal[] = {
678 	[TKEY_UP] = "\x1B[A",		[TKEY_DOWN] = "\x1B[B",
679 	[TKEY_LEFT] = "\x1B[D",		[TKEY_RIGHT] = "\x1B[C",
680 
681 	[TKEY_HOME] = "\x1B[H",		[TKEY_END] = "\x1B[F",
682 	[TKEY_INSERT] = "\x1B[2~",	[TKEY_DELETE] = "\x1B[3~",
683 	[TKEY_PAGE_UP] = "\x1B[5~",	[TKEY_PAGE_DOWN] = "\x1B[6~",
684 
685 	[TKEY_F1] = "\x1BOP",		[TKEY_F2] = "\x1BOQ",
686 	[TKEY_F3] = "\x1BOR",		[TKEY_F4] = "\x1BOS",
687 	[TKEY_F5] = "\x1B[15~",		[TKEY_F6] = "\x1B[17~",
688 	[TKEY_F7] = "\x1B[18~",		[TKEY_F8] = "\x1B[19~",
689 	[TKEY_F9] = "\x1B[20~",		[TKEY_F10] = "\x1B[21~",
690 	[TKEY_F11] = "\x1B[23~",	[TKEY_F12] = "\x1B[24~",
691 };
692 
693 const char *
694 teken_get_sequence(teken_t *t, unsigned int k)
695 {
696 
697 	/* Cons25 mode. */
698 	if (t->t_stateflags & TS_CONS25 &&
699 	    k < sizeof special_strings_cons25 / sizeof(char *))
700 		return (special_strings_cons25[k]);
701 
702 	/* Cursor keys mode. */
703 	if (t->t_stateflags & TS_CURSORKEYS &&
704 	    k < sizeof special_strings_ckeys / sizeof(char *))
705 		return (special_strings_ckeys[k]);
706 
707 	/* Default xterm sequences. */
708 	if (k < sizeof special_strings_normal / sizeof(char *))
709 		return (special_strings_normal[k]);
710 
711 	return (NULL);
712 }
713 
714 #include "teken_state.h"
715