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