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