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