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