teken.c revision 197115
1186681Sed/*-
2186681Sed * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
3186681Sed * All rights reserved.
4186681Sed *
5186681Sed * Redistribution and use in source and binary forms, with or without
6186681Sed * modification, are permitted provided that the following conditions
7186681Sed * are met:
8186681Sed * 1. Redistributions of source code must retain the above copyright
9186681Sed *    notice, this list of conditions and the following disclaimer.
10186681Sed * 2. Redistributions in binary form must reproduce the above copyright
11186681Sed *    notice, this list of conditions and the following disclaimer in the
12186681Sed *    documentation and/or other materials provided with the distribution.
13186681Sed *
14186681Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15186681Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16186681Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17186681Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18186681Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19186681Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20186681Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21186681Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22186681Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23186681Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24186681Sed * SUCH DAMAGE.
25186681Sed *
26186681Sed * $FreeBSD: head/sys/teken/teken.c 197115 2009-09-12 10:34:34Z ed $
27186681Sed */
28186681Sed
29186681Sed#include <sys/cdefs.h>
30186681Sed#if defined(__FreeBSD__) && defined(_KERNEL)
31186681Sed#include <sys/param.h>
32186681Sed#include <sys/lock.h>
33186681Sed#include <sys/systm.h>
34186681Sed#define	teken_assert(x)		MPASS(x)
35186681Sed#define	teken_printf(x,...)
36186681Sed#else /* !(__FreeBSD__ && _KERNEL) */
37186681Sed#include <sys/types.h>
38186681Sed#include <assert.h>
39186681Sed#include <inttypes.h>
40186681Sed#include <stdio.h>
41186681Sed#include <string.h>
42186681Sed#define	teken_assert(x)		assert(x)
43186681Sed#define	teken_printf(x,...)	do { \
44186681Sed	if (df != NULL) \
45186681Sed		fprintf(df, x, ## __VA_ARGS__); \
46186681Sed} while (0)
47186681Sed/* debug messages */
48186681Sedstatic FILE *df;
49186681Sed#endif /* __FreeBSD__ && _KERNEL */
50186681Sed
51186681Sed#include "teken.h"
52197115Sed#include "teken_wcwidth.h"
53187469Sed
54187367Sed#ifdef TEKEN_XTERM
55197115Sed#include "teken_scs.h"
56187367Sed#else /* !TEKEN_XTERM */
57187469Sed#define	teken_scs_process(t, c)	(c)
58187469Sed#define	teken_scs_restore(t)
59187469Sed#define	teken_scs_save(t)
60187469Sed#define	teken_scs_set(t, g, ts)
61187469Sed#define	teken_scs_switch(t, g)
62197115Sed#endif /* TEKEN_XTERM */
63187469Sed
64186681Sed/* Private flags for t_stateflags. */
65186681Sed#define	TS_FIRSTDIGIT	0x01	/* First numeric digit in escape sequence. */
66186681Sed#define	TS_INSERT	0x02	/* Insert mode. */
67186681Sed#define	TS_AUTOWRAP	0x04	/* Autowrap. */
68186681Sed#define	TS_ORIGIN	0x08	/* Origin mode. */
69187367Sed#ifdef TEKEN_XTERM
70187367Sed#define	TS_WRAPPED	0x10	/* Next character should be printed on col 0. */
71187367Sed#else /* !TEKEN_XTERM */
72186729Sed#define	TS_WRAPPED	0x00	/* Simple line wrapping. */
73187367Sed#endif /* TEKEN_XTERM */
74186681Sed
75186681Sed/* Character that blanks a cell. */
76186681Sed#define	BLANK	' '
77186681Sed
78186681Sedstatic teken_state_t	teken_state_init;
79186681Sed
80186681Sed/*
81186681Sed * Wrappers for hooks.
82186681Sed */
83186681Sed
84186681Sedstatic inline void
85186681Sedteken_funcs_bell(teken_t *t)
86186681Sed{
87186681Sed
88186681Sed	t->t_funcs->tf_bell(t->t_softc);
89186681Sed}
90186681Sed
91186681Sedstatic inline void
92186681Sedteken_funcs_cursor(teken_t *t)
93186681Sed{
94186681Sed
95186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
96186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
97186681Sed
98186681Sed	t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor);
99186681Sed}
100186681Sed
101186681Sedstatic inline void
102186681Sedteken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c,
103186681Sed    const teken_attr_t *a)
104186681Sed{
105186681Sed
106186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
107186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
108186681Sed
109186681Sed	t->t_funcs->tf_putchar(t->t_softc, p, c, a);
110186681Sed}
111186681Sed
112186681Sedstatic inline void
113186681Sedteken_funcs_fill(teken_t *t, const teken_rect_t *r,
114186681Sed    const teken_char_t c, const teken_attr_t *a)
115186681Sed{
116186681Sed
117186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
118186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
119186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
120186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
121186681Sed
122186681Sed	t->t_funcs->tf_fill(t->t_softc, r, c, a);
123186681Sed}
124186681Sed
125186681Sedstatic inline void
126186681Sedteken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
127186681Sed{
128186681Sed
129186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
130186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
131186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
132186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
133186681Sed	teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row);
134186681Sed	teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col);
135186681Sed
136186681Sed	t->t_funcs->tf_copy(t->t_softc, r, p);
137186681Sed}
138186681Sed
139186681Sedstatic inline void
140193184Sedteken_funcs_param(teken_t *t, int cmd, unsigned int value)
141186681Sed{
142186681Sed
143186681Sed	t->t_funcs->tf_param(t->t_softc, cmd, value);
144186681Sed}
145186681Sed
146186681Sedstatic inline void
147186681Sedteken_funcs_respond(teken_t *t, const void *buf, size_t len)
148186681Sed{
149186681Sed
150186681Sed	t->t_funcs->tf_respond(t->t_softc, buf, len);
151186681Sed}
152186681Sed
153186681Sed#include "teken_subr.h"
154186681Sed#include "teken_subr_compat.h"
155186681Sed
156186681Sed/*
157186681Sed * Programming interface.
158186681Sed */
159186681Sed
160186681Sedvoid
161186681Sedteken_init(teken_t *t, const teken_funcs_t *tf, void *softc)
162186681Sed{
163186681Sed	teken_pos_t tp = { .tp_row = 24, .tp_col = 80 };
164186681Sed
165186681Sed#if !(defined(__FreeBSD__) && defined(_KERNEL))
166186681Sed	df = fopen("teken.log", "w");
167186681Sed	if (df != NULL)
168186681Sed		setvbuf(df, NULL, _IOLBF, BUFSIZ);
169186681Sed#endif /* !(__FreeBSD__ && _KERNEL) */
170186681Sed
171186681Sed	t->t_funcs = tf;
172186681Sed	t->t_softc = softc;
173186681Sed
174186681Sed	t->t_nextstate = teken_state_init;
175186681Sed
176186681Sed	t->t_defattr.ta_format = 0;
177186681Sed	t->t_defattr.ta_fgcolor = TC_WHITE;
178186681Sed	t->t_defattr.ta_bgcolor = TC_BLACK;
179186681Sed	teken_subr_do_reset(t);
180186681Sed
181186681Sed	t->t_utf8_left = 0;
182186681Sed
183186681Sed	teken_set_winsize(t, &tp);
184186681Sed}
185186681Sed
186186681Sedstatic void
187186681Sedteken_input_char(teken_t *t, teken_char_t c)
188186681Sed{
189186681Sed
190186681Sed	switch (c) {
191186681Sed	case '\0':
192186681Sed		break;
193186681Sed	case '\a':
194186681Sed		teken_subr_bell(t);
195186681Sed		break;
196186681Sed	case '\b':
197186681Sed		teken_subr_backspace(t);
198186681Sed		break;
199186681Sed	case '\n':
200186681Sed	case '\x0B':
201186681Sed		teken_subr_newline(t);
202186681Sed		break;
203186798Sed	case '\x0C':
204186798Sed		teken_subr_newpage(t);
205186798Sed		break;
206197115Sed#ifdef TEKEN_XTERM
207187469Sed	case '\x0E':
208187469Sed		teken_scs_switch(t, 1);
209187469Sed		break;
210187469Sed	case '\x0F':
211187469Sed		teken_scs_switch(t, 0);
212187469Sed		break;
213197115Sed#endif /* TEKEN_XTERM */
214186681Sed	case '\r':
215186681Sed		teken_subr_carriage_return(t);
216186681Sed		break;
217186681Sed	case '\t':
218186681Sed		teken_subr_horizontal_tab(t);
219186681Sed		break;
220186681Sed	default:
221186681Sed		t->t_nextstate(t, c);
222186681Sed		break;
223186681Sed	}
224186681Sed
225186681Sed	/* Post-processing assertions. */
226186681Sed	teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin);
227186681Sed	teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end);
228186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
229186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
230186681Sed	teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row);
231186681Sed	teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col);
232186681Sed	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
233186681Sed	teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end);
234186681Sed	/* Origin region has to be window size or the same as scrollreg. */
235186681Sed	teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin &&
236186681Sed	    t->t_originreg.ts_end == t->t_scrollreg.ts_end) ||
237186681Sed	    (t->t_originreg.ts_begin == 0 &&
238186681Sed	    t->t_originreg.ts_end == t->t_winsize.tp_row));
239186681Sed}
240186681Sed
241186681Sedstatic void
242186681Sedteken_input_byte(teken_t *t, unsigned char c)
243186681Sed{
244186681Sed
245186681Sed	/*
246186681Sed	 * UTF-8 handling.
247186681Sed	 */
248197115Sed	 if (t->t_utf8_left == -1) {
249197115Sed	 	/* UTF-8 disabled. */
250197115Sed		teken_input_char(t, c);
251197115Sed	} else if ((c & 0x80) == 0x00) {
252186681Sed		/* One-byte sequence. */
253186681Sed		t->t_utf8_left = 0;
254186681Sed		teken_input_char(t, c);
255186681Sed	} else if ((c & 0xe0) == 0xc0) {
256186681Sed		/* Two-byte sequence. */
257186681Sed		t->t_utf8_left = 1;
258186681Sed		t->t_utf8_partial = c & 0x1f;
259186681Sed	} else if ((c & 0xf0) == 0xe0) {
260186681Sed		/* Three-byte sequence. */
261186681Sed		t->t_utf8_left = 2;
262186681Sed		t->t_utf8_partial = c & 0x0f;
263186681Sed	} else if ((c & 0xf8) == 0xf0) {
264186681Sed		/* Four-byte sequence. */
265186681Sed		t->t_utf8_left = 3;
266186681Sed		t->t_utf8_partial = c & 0x07;
267186681Sed	} else if ((c & 0xc0) == 0x80) {
268186681Sed		if (t->t_utf8_left == 0)
269186681Sed			return;
270186681Sed		t->t_utf8_left--;
271186681Sed		t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f);
272186681Sed		if (t->t_utf8_left == 0) {
273194293Sed			teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial);
274186681Sed			teken_input_char(t, t->t_utf8_partial);
275186681Sed		}
276186681Sed	}
277186681Sed}
278186681Sed
279186681Sedvoid
280186681Sedteken_input(teken_t *t, const void *buf, size_t len)
281186681Sed{
282186681Sed	const char *c = buf;
283186681Sed
284186681Sed	while (len-- > 0)
285186681Sed		teken_input_byte(t, *c++);
286186681Sed}
287186681Sed
288186681Sedvoid
289186681Sedteken_set_cursor(teken_t *t, const teken_pos_t *p)
290186681Sed{
291186681Sed
292186681Sed	/* XXX: bounds checking with originreg! */
293186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
294186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
295186681Sed
296186681Sed	t->t_cursor = *p;
297186681Sed}
298186681Sed
299188391Sedconst teken_attr_t *
300188391Sedteken_get_curattr(teken_t *t)
301188391Sed{
302188391Sed
303188391Sed	return (&t->t_curattr);
304188391Sed}
305188391Sed
306189617Sedvoid
307189617Sedteken_set_curattr(teken_t *t, const teken_attr_t *a)
308189617Sed{
309189617Sed
310189617Sed	t->t_curattr = *a;
311189617Sed}
312189617Sed
313188391Sedconst teken_attr_t *
314188391Sedteken_get_defattr(teken_t *t)
315188391Sed{
316188391Sed
317188391Sed	return (&t->t_defattr);
318188391Sed}
319188391Sed
320186681Sedvoid
321186681Sedteken_set_defattr(teken_t *t, const teken_attr_t *a)
322186681Sed{
323186681Sed
324186681Sed	t->t_curattr = t->t_saved_curattr = t->t_defattr = *a;
325186681Sed}
326186681Sed
327186681Sedvoid
328186681Sedteken_set_winsize(teken_t *t, const teken_pos_t *p)
329186681Sed{
330186681Sed
331186681Sed	t->t_winsize = *p;
332197114Sed	teken_subr_do_reset(t);
333186681Sed}
334186681Sed
335197115Sedvoid
336197115Sedteken_set_8bit(teken_t *t)
337197115Sed{
338197115Sed
339197115Sed	t->t_utf8_left = -1;
340197115Sed}
341197115Sed
342186681Sed/*
343186681Sed * State machine.
344186681Sed */
345186681Sed
346186681Sedstatic void
347186681Sedteken_state_switch(teken_t *t, teken_state_t *s)
348186681Sed{
349186681Sed
350186681Sed	t->t_nextstate = s;
351186681Sed	t->t_curnum = 0;
352186681Sed	t->t_stateflags |= TS_FIRSTDIGIT;
353186681Sed}
354186681Sed
355186681Sedstatic int
356186681Sedteken_state_numbers(teken_t *t, teken_char_t c)
357186681Sed{
358186681Sed
359186681Sed	teken_assert(t->t_curnum < T_NUMSIZE);
360186681Sed
361186681Sed	if (c >= '0' && c <= '9') {
362186681Sed		/*
363186681Sed		 * Don't do math with the default value of 1 when a
364186681Sed		 * custom number is inserted.
365186681Sed		 */
366186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT) {
367186681Sed			t->t_stateflags &= ~TS_FIRSTDIGIT;
368186681Sed			t->t_nums[t->t_curnum] = 0;
369186681Sed		} else {
370186681Sed			t->t_nums[t->t_curnum] *= 10;
371186681Sed		}
372186681Sed
373186681Sed		t->t_nums[t->t_curnum] += c - '0';
374186681Sed		return (1);
375186681Sed	} else if (c == ';') {
376186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT)
377186681Sed			t->t_nums[t->t_curnum] = 0;
378186681Sed
379186681Sed		/* Only allow a limited set of arguments. */
380186681Sed		if (++t->t_curnum == T_NUMSIZE) {
381186681Sed			teken_state_switch(t, teken_state_init);
382186681Sed			return (1);
383186681Sed		}
384186681Sed
385186681Sed		t->t_stateflags |= TS_FIRSTDIGIT;
386186681Sed		return (1);
387186681Sed	} else {
388186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
389186681Sed			/* Finish off the last empty argument. */
390186681Sed			t->t_nums[t->t_curnum] = 0;
391186681Sed			t->t_curnum++;
392186681Sed		} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
393186681Sed			/* Also count the last argument. */
394186681Sed			t->t_curnum++;
395186681Sed		}
396186681Sed	}
397186681Sed
398186681Sed	return (0);
399186681Sed}
400186681Sed
401186681Sed#include "teken_state.h"
402