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