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