xref: /freebsd/sys/teken/teken.c (revision 0957b409)
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;
416 }
417 
418 void
419 teken_set_cons25keys(teken_t *t)
420 {
421 
422 	t->t_stateflags |= TS_CONS25KEYS;
423 }
424 
425 /*
426  * State machine.
427  */
428 
429 static void
430 teken_state_switch(teken_t *t, teken_state_t *s)
431 {
432 
433 	t->t_nextstate = s;
434 	t->t_curnum = 0;
435 	t->t_stateflags |= TS_FIRSTDIGIT;
436 }
437 
438 static int
439 teken_state_numbers(teken_t *t, teken_char_t c)
440 {
441 
442 	teken_assert(t->t_curnum < T_NUMSIZE);
443 
444 	if (c >= '0' && c <= '9') {
445 		if (t->t_stateflags & TS_FIRSTDIGIT) {
446 			/* First digit. */
447 			t->t_stateflags &= ~TS_FIRSTDIGIT;
448 			t->t_nums[t->t_curnum] = c - '0';
449 		} else if (t->t_nums[t->t_curnum] < UINT_MAX / 100) {
450 			/*
451 			 * There is no need to continue parsing input
452 			 * once the value exceeds the size of the
453 			 * terminal. It would only allow for integer
454 			 * overflows when performing arithmetic on the
455 			 * cursor position.
456 			 *
457 			 * Ignore any further digits if the value is
458 			 * already UINT_MAX / 100.
459 			 */
460 			t->t_nums[t->t_curnum] =
461 			    t->t_nums[t->t_curnum] * 10 + c - '0';
462 		}
463 		return (1);
464 	} else if (c == ';') {
465 		if (t->t_stateflags & TS_FIRSTDIGIT)
466 			t->t_nums[t->t_curnum] = 0;
467 
468 		/* Only allow a limited set of arguments. */
469 		if (++t->t_curnum == T_NUMSIZE) {
470 			teken_state_switch(t, teken_state_init);
471 			return (1);
472 		}
473 
474 		t->t_stateflags |= TS_FIRSTDIGIT;
475 		return (1);
476 	} else {
477 		if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
478 			/* Finish off the last empty argument. */
479 			t->t_nums[t->t_curnum] = 0;
480 			t->t_curnum++;
481 		} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
482 			/* Also count the last argument. */
483 			t->t_curnum++;
484 		}
485 	}
486 
487 	return (0);
488 }
489 
490 #define	k	TC_BLACK
491 #define	b	TC_BLUE
492 #define	y	TC_BROWN
493 #define	c	TC_CYAN
494 #define	g	TC_GREEN
495 #define	m	TC_MAGENTA
496 #define	r	TC_RED
497 #define	w	TC_WHITE
498 #define	K	(TC_BLACK | TC_LIGHT)
499 #define	B	(TC_BLUE | TC_LIGHT)
500 #define	Y	(TC_BROWN | TC_LIGHT)
501 #define	C	(TC_CYAN | TC_LIGHT)
502 #define	G	(TC_GREEN | TC_LIGHT)
503 #define	M	(TC_MAGENTA | TC_LIGHT)
504 #define	R	(TC_RED | TC_LIGHT)
505 #define	W	(TC_WHITE | TC_LIGHT)
506 
507 /**
508  * The xterm-256 color map has steps of 0x28 (in the range 0-0xff), except
509  * for the first step which is 0x5f.  Scale to the range 0-6 by dividing
510  * by 0x28 and rounding down.  The range of 0-5 cannot represent the
511  * larger first step.
512  *
513  * This table is generated by the follow rules:
514  * - if all components are equal, the result is black for (0, 0, 0) and
515  *   (2, 2, 2), else white; otherwise:
516  * - subtract the smallest component from all components
517  * - if this gives only one nonzero component, then that is the color
518  * - else if one component is 2 or more larger than the other nonzero one,
519  *   then that component gives the color
520  * - else there are 2 nonzero components.  The color is that of a small
521  *   equal mixture of these components (cyan, yellow or magenta).  E.g.,
522  *   (0, 5, 6) (Turquoise2) is a much purer cyan than (0, 2, 3)
523  *   (DeepSkyBlue4), but we map both to cyan since we can't represent
524  *   delicate shades of either blue or cyan and blue would be worse.
525  *   Here it is important that components of 1 never occur.  Blue would
526  *   be twice as large as green in (0, 1, 2).
527  */
528 static const teken_color_t teken_256to8tab[] = {
529 	/* xterm normal colors: */
530 	k, r, g, y, b, m, c, w,
531 
532 	/* xterm bright colors: */
533 	k, r, g, y, b, m, c, w,
534 
535 	/* Red0 submap. */
536 	k, b, b, b, b, b,
537 	g, c, c, b, b, b,
538 	g, c, 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 	/* Red2 submap. */
544 	r, m, m, b, b, b,
545 	y, k, b, b, b, b,
546 	y, g, c, c, b, b,
547 	g, g, c, c, c, b,
548 	g, g, g, c, c, c,
549 	g, g, g, g, c, c,
550 
551 	/* Red3 submap. */
552 	r, m, m, m, b, b,
553 	y, r, m, m, b, b,
554 	y, y, w, b, b, b,
555 	y, y, g, c, c, b,
556 	g, g, g, c, c, c,
557 	g, g, g, g, c, c,
558 
559 	/* Red4 submap. */
560 	r, r, m, m, m, b,
561 	r, r, m, m, m, b,
562 	y, y, r, m, m, b,
563 	y, y, y, w, b, b,
564 	y, y, y, g, c, c,
565 	g, g, g, g, c, c,
566 
567 	/* Red5 submap. */
568 	r, r, r, m, m, m,
569 	r, r, r, m, m, m,
570 	r, r, r, m, m, m,
571 	y, y, y, r, m, m,
572 	y, y, y, y, w, b,
573 	y, y, y, y, g, c,
574 
575 	/* Red6 submap. */
576 	r, r, r, r, m, m,
577 	r, r, r, r, m, m,
578 	r, r, r, r, m, m,
579 	r, r, r, r, m, m,
580 	y, y, y, y, r, m,
581 	y, y, y, y, y, w,
582 
583 	/* Grey submap. */
584 	k, k, k, k, k, k,
585 	k, k, k, k, k, k,
586 	w, w, w, w, w, w,
587 	w, w, w, w, w, w,
588 };
589 
590 /*
591  * This table is generated from the previous one by setting TC_LIGHT for
592  * entries whose luminosity in the xterm256 color map is 60% or larger.
593  * Thus the previous table is currently not really needed.  It will be
594  * used for different fine tuning of the tables.
595  */
596 static const teken_color_t teken_256to16tab[] = {
597 	/* xterm normal colors: */
598 	k, r, g, y, b, m, c, w,
599 
600 	/* xterm bright colors: */
601 	K, R, G, Y, B, M, C, W,
602 
603 	/* Red0 submap. */
604 	k, b, b, b, b, b,
605 	g, c, c, b, b, b,
606 	g, c, 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 	/* Red2 submap. */
612 	r, m, m, b, b, b,
613 	y, K, b, b, B, B,
614 	y, g, c, c, B, B,
615 	g, g, c, c, C, B,
616 	g, G, G, C, C, C,
617 	g, G, G, G, C, C,
618 
619 	/* Red3 submap. */
620 	r, m, m, m, b, b,
621 	y, r, m, m, B, B,
622 	y, y, w, B, B, B,
623 	y, y, G, C, C, B,
624 	g, G, G, C, C, C,
625 	g, G, G, G, C, C,
626 
627 	/* Red4 submap. */
628 	r, r, m, m, m, b,
629 	r, r, m, m, M, B,
630 	y, y, R, M, M, B,
631 	y, y, Y, W, B, B,
632 	y, Y, Y, G, C, C,
633 	g, G, G, G, C, C,
634 
635 	/* Red5 submap. */
636 	r, r, r, m, m, m,
637 	r, R, R, M, M, M,
638 	r, R, R, M, M, M,
639 	y, Y, Y, R, M, M,
640 	y, Y, Y, Y, W, B,
641 	y, Y, Y, Y, G, C,
642 
643 	/* Red6 submap. */
644 	r, r, r, r, m, m,
645 	r, R, R, R, M, M,
646 	r, R, R, R, M, M,
647 	r, R, R, R, M, M,
648 	y, Y, Y, Y, R, M,
649 	y, Y, Y, Y, Y, W,
650 
651 	/* Grey submap. */
652 	k, k, k, k, k, k,
653 	K, K, K, K, K, K,
654 	w, w, w, w, w, w,
655 	W, W, W, W, W, W,
656 };
657 
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 #undef	K
667 #undef	B
668 #undef	Y
669 #undef	C
670 #undef	G
671 #undef	M
672 #undef	R
673 #undef	W
674 
675 teken_color_t
676 teken_256to8(teken_color_t c)
677 {
678 
679 	return (teken_256to8tab[c % 256]);
680 }
681 
682 teken_color_t
683 teken_256to16(teken_color_t c)
684 {
685 
686 	return (teken_256to16tab[c % 256]);
687 }
688 
689 static const char * const special_strings_cons25[] = {
690 	[TKEY_UP] = "\x1B[A",		[TKEY_DOWN] = "\x1B[B",
691 	[TKEY_LEFT] = "\x1B[D",		[TKEY_RIGHT] = "\x1B[C",
692 
693 	[TKEY_HOME] = "\x1B[H",		[TKEY_END] = "\x1B[F",
694 	[TKEY_INSERT] = "\x1B[L",	[TKEY_DELETE] = "\x7F",
695 	[TKEY_PAGE_UP] = "\x1B[I",	[TKEY_PAGE_DOWN] = "\x1B[G",
696 
697 	[TKEY_F1] = "\x1B[M",		[TKEY_F2] = "\x1B[N",
698 	[TKEY_F3] = "\x1B[O",		[TKEY_F4] = "\x1B[P",
699 	[TKEY_F5] = "\x1B[Q",		[TKEY_F6] = "\x1B[R",
700 	[TKEY_F7] = "\x1B[S",		[TKEY_F8] = "\x1B[T",
701 	[TKEY_F9] = "\x1B[U",		[TKEY_F10] = "\x1B[V",
702 	[TKEY_F11] = "\x1B[W",		[TKEY_F12] = "\x1B[X",
703 };
704 
705 static const char * const special_strings_ckeys[] = {
706 	[TKEY_UP] = "\x1BOA",		[TKEY_DOWN] = "\x1BOB",
707 	[TKEY_LEFT] = "\x1BOD",		[TKEY_RIGHT] = "\x1BOC",
708 
709 	[TKEY_HOME] = "\x1BOH",		[TKEY_END] = "\x1BOF",
710 };
711 
712 static const char * const special_strings_normal[] = {
713 	[TKEY_UP] = "\x1B[A",		[TKEY_DOWN] = "\x1B[B",
714 	[TKEY_LEFT] = "\x1B[D",		[TKEY_RIGHT] = "\x1B[C",
715 
716 	[TKEY_HOME] = "\x1B[H",		[TKEY_END] = "\x1B[F",
717 	[TKEY_INSERT] = "\x1B[2~",	[TKEY_DELETE] = "\x1B[3~",
718 	[TKEY_PAGE_UP] = "\x1B[5~",	[TKEY_PAGE_DOWN] = "\x1B[6~",
719 
720 	[TKEY_F1] = "\x1BOP",		[TKEY_F2] = "\x1BOQ",
721 	[TKEY_F3] = "\x1BOR",		[TKEY_F4] = "\x1BOS",
722 	[TKEY_F5] = "\x1B[15~",		[TKEY_F6] = "\x1B[17~",
723 	[TKEY_F7] = "\x1B[18~",		[TKEY_F8] = "\x1B[19~",
724 	[TKEY_F9] = "\x1B[20~",		[TKEY_F10] = "\x1B[21~",
725 	[TKEY_F11] = "\x1B[23~",	[TKEY_F12] = "\x1B[24~",
726 };
727 
728 const char *
729 teken_get_sequence(const teken_t *t, unsigned int k)
730 {
731 
732 	/* Cons25 mode. */
733 	if ((t->t_stateflags & (TS_CONS25 | TS_CONS25KEYS)) ==
734 	    (TS_CONS25 | TS_CONS25KEYS))
735 		return (NULL);	/* Don't override good kbd(4) strings. */
736 	if (t->t_stateflags & TS_CONS25 &&
737 	    k < sizeof special_strings_cons25 / sizeof(char *))
738 		return (special_strings_cons25[k]);
739 
740 	/* Cursor keys mode. */
741 	if (t->t_stateflags & TS_CURSORKEYS &&
742 	    k < sizeof special_strings_ckeys / sizeof(char *))
743 		return (special_strings_ckeys[k]);
744 
745 	/* Default xterm sequences. */
746 	if (k < sizeof special_strings_normal / sizeof(char *))
747 		return (special_strings_normal[k]);
748 
749 	return (NULL);
750 }
751 
752 #include "teken_state.h"
753