teken.c revision 186729
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/dev/syscons/teken/teken.c 186729 2009-01-03 22:51:54Z 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" 52186681Sed#ifdef TEKEN_UTF8 53186681Sed#include "teken_wcwidth.h" 54186681Sed#else /* !TEKEN_UTF8 */ 55186681Sedstatic inline int 56186681Sedteken_wcwidth(teken_char_t c) 57186681Sed{ 58186681Sed 59186681Sed return (c <= 0x1B) ? -1 : 1; 60186681Sed} 61186681Sed#endif /* TEKEN_UTF8 */ 62186681Sed 63186681Sed/* Private flags for teken_format_t. */ 64186681Sed#define TF_REVERSE 0x08 65186681Sed 66186681Sed/* Private flags for t_stateflags. */ 67186681Sed#define TS_FIRSTDIGIT 0x01 /* First numeric digit in escape sequence. */ 68186681Sed#define TS_INSERT 0x02 /* Insert mode. */ 69186681Sed#define TS_AUTOWRAP 0x04 /* Autowrap. */ 70186681Sed#define TS_ORIGIN 0x08 /* Origin mode. */ 71186729Sed#ifdef TEKEN_CONS25 72186729Sed#define TS_WRAPPED 0x00 /* Simple line wrapping. */ 73186729Sed#else /* !TEKEN_CONS25 */ 74186681Sed#define TS_WRAPPED 0x10 /* Next character should be printed on col 0. */ 75186729Sed#endif /* TEKEN_CONS25 */ 76186681Sed 77186681Sed/* Character that blanks a cell. */ 78186681Sed#define BLANK ' ' 79186681Sed 80186681Sedstatic teken_state_t teken_state_init; 81186681Sed 82186681Sed/* 83186681Sed * Wrappers for hooks. 84186681Sed */ 85186681Sed 86186681Sedstatic inline void 87186681Sedteken_funcs_bell(teken_t *t) 88186681Sed{ 89186681Sed 90186681Sed t->t_funcs->tf_bell(t->t_softc); 91186681Sed} 92186681Sed 93186681Sedstatic inline void 94186681Sedteken_funcs_cursor(teken_t *t) 95186681Sed{ 96186681Sed 97186681Sed teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 98186681Sed teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 99186681Sed 100186681Sed t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor); 101186681Sed} 102186681Sed 103186681Sedstatic inline void 104186681Sedteken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c, 105186681Sed const teken_attr_t *a) 106186681Sed{ 107186681Sed teken_attr_t ta; 108186681Sed 109186681Sed teken_assert(p->tp_row < t->t_winsize.tp_row); 110186681Sed teken_assert(p->tp_col < t->t_winsize.tp_col); 111186681Sed 112186681Sed /* Apply inversion. */ 113186681Sed if (a->ta_format & TF_REVERSE) { 114186681Sed ta.ta_format = a->ta_format; 115186681Sed ta.ta_fgcolor = a->ta_bgcolor; 116186681Sed ta.ta_bgcolor = a->ta_fgcolor; 117186681Sed a = &ta; 118186681Sed } 119186681Sed 120186681Sed t->t_funcs->tf_putchar(t->t_softc, p, c, a); 121186681Sed} 122186681Sed 123186681Sedstatic inline void 124186681Sedteken_funcs_fill(teken_t *t, const teken_rect_t *r, 125186681Sed const teken_char_t c, const teken_attr_t *a) 126186681Sed{ 127186681Sed teken_attr_t ta; 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 134186681Sed /* Apply inversion. */ 135186681Sed if (a->ta_format & TF_REVERSE) { 136186681Sed ta.ta_format = a->ta_format; 137186681Sed ta.ta_fgcolor = a->ta_bgcolor; 138186681Sed ta.ta_bgcolor = a->ta_fgcolor; 139186681Sed a = &ta; 140186681Sed } 141186681Sed 142186681Sed t->t_funcs->tf_fill(t->t_softc, r, c, a); 143186681Sed} 144186681Sed 145186681Sedstatic inline void 146186681Sedteken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p) 147186681Sed{ 148186681Sed 149186681Sed teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); 150186681Sed teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); 151186681Sed teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); 152186681Sed teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); 153186681Sed teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row); 154186681Sed teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col); 155186681Sed 156186681Sed t->t_funcs->tf_copy(t->t_softc, r, p); 157186681Sed} 158186681Sed 159186681Sedstatic inline void 160186681Sedteken_funcs_param(teken_t *t, int cmd, int value) 161186681Sed{ 162186681Sed 163186681Sed t->t_funcs->tf_param(t->t_softc, cmd, value); 164186681Sed} 165186681Sed 166186681Sedstatic inline void 167186681Sedteken_funcs_respond(teken_t *t, const void *buf, size_t len) 168186681Sed{ 169186681Sed 170186681Sed t->t_funcs->tf_respond(t->t_softc, buf, len); 171186681Sed} 172186681Sed 173186681Sed#include "teken_subr.h" 174186681Sed#include "teken_subr_compat.h" 175186681Sed 176186681Sed/* 177186681Sed * Programming interface. 178186681Sed */ 179186681Sed 180186681Sedvoid 181186681Sedteken_init(teken_t *t, const teken_funcs_t *tf, void *softc) 182186681Sed{ 183186681Sed teken_pos_t tp = { .tp_row = 24, .tp_col = 80 }; 184186681Sed 185186681Sed#if !(defined(__FreeBSD__) && defined(_KERNEL)) 186186681Sed df = fopen("teken.log", "w"); 187186681Sed if (df != NULL) 188186681Sed setvbuf(df, NULL, _IOLBF, BUFSIZ); 189186681Sed#endif /* !(__FreeBSD__ && _KERNEL) */ 190186681Sed 191186681Sed t->t_funcs = tf; 192186681Sed t->t_softc = softc; 193186681Sed 194186681Sed t->t_nextstate = teken_state_init; 195186681Sed 196186681Sed t->t_defattr.ta_format = 0; 197186681Sed t->t_defattr.ta_fgcolor = TC_WHITE; 198186681Sed t->t_defattr.ta_bgcolor = TC_BLACK; 199186681Sed teken_subr_do_reset(t); 200186681Sed 201186681Sed#ifdef TEKEN_UTF8 202186681Sed t->t_utf8_left = 0; 203186681Sed#endif /* TEKEN_UTF8 */ 204186681Sed 205186681Sed teken_set_winsize(t, &tp); 206186681Sed} 207186681Sed 208186681Sedstatic void 209186681Sedteken_input_char(teken_t *t, teken_char_t c) 210186681Sed{ 211186681Sed 212186681Sed switch (c) { 213186681Sed case '\0': 214186681Sed break; 215186681Sed case '\a': 216186681Sed teken_subr_bell(t); 217186681Sed break; 218186681Sed case '\b': 219186681Sed teken_subr_backspace(t); 220186681Sed break; 221186681Sed case '\n': 222186681Sed case '\x0B': 223186681Sed teken_subr_newline(t); 224186681Sed break; 225186681Sed case '\r': 226186681Sed teken_subr_carriage_return(t); 227186681Sed break; 228186681Sed case '\t': 229186681Sed teken_subr_horizontal_tab(t); 230186681Sed break; 231186681Sed default: 232186681Sed t->t_nextstate(t, c); 233186681Sed break; 234186681Sed } 235186681Sed 236186681Sed /* Post-processing assertions. */ 237186681Sed teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin); 238186681Sed teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end); 239186681Sed teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 240186681Sed teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 241186681Sed teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row); 242186681Sed teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col); 243186681Sed teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); 244186681Sed teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end); 245186681Sed /* Origin region has to be window size or the same as scrollreg. */ 246186681Sed teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin && 247186681Sed t->t_originreg.ts_end == t->t_scrollreg.ts_end) || 248186681Sed (t->t_originreg.ts_begin == 0 && 249186681Sed t->t_originreg.ts_end == t->t_winsize.tp_row)); 250186681Sed} 251186681Sed 252186681Sedstatic void 253186681Sedteken_input_byte(teken_t *t, unsigned char c) 254186681Sed{ 255186681Sed 256186681Sed#ifdef TEKEN_UTF8 257186681Sed /* 258186681Sed * UTF-8 handling. 259186681Sed */ 260186681Sed if ((c & 0x80) == 0x00) { 261186681Sed /* One-byte sequence. */ 262186681Sed t->t_utf8_left = 0; 263186681Sed teken_input_char(t, c); 264186681Sed } else if ((c & 0xe0) == 0xc0) { 265186681Sed /* Two-byte sequence. */ 266186681Sed t->t_utf8_left = 1; 267186681Sed t->t_utf8_partial = c & 0x1f; 268186681Sed } else if ((c & 0xf0) == 0xe0) { 269186681Sed /* Three-byte sequence. */ 270186681Sed t->t_utf8_left = 2; 271186681Sed t->t_utf8_partial = c & 0x0f; 272186681Sed } else if ((c & 0xf8) == 0xf0) { 273186681Sed /* Four-byte sequence. */ 274186681Sed t->t_utf8_left = 3; 275186681Sed t->t_utf8_partial = c & 0x07; 276186681Sed } else if ((c & 0xc0) == 0x80) { 277186681Sed if (t->t_utf8_left == 0) 278186681Sed return; 279186681Sed t->t_utf8_left--; 280186681Sed t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f); 281186681Sed if (t->t_utf8_left == 0) { 282186681Sed teken_printf("Got UTF-8 char %u\n", t->t_utf8_partial); 283186681Sed teken_input_char(t, t->t_utf8_partial); 284186681Sed } 285186681Sed } 286186681Sed#else /* !TEKEN_UTF8 */ 287186681Sed teken_input_char(t, c); 288186681Sed#endif /* TEKEN_UTF8 */ 289186681Sed} 290186681Sed 291186681Sedvoid 292186681Sedteken_input(teken_t *t, const void *buf, size_t len) 293186681Sed{ 294186681Sed const char *c = buf; 295186681Sed 296186681Sed while (len-- > 0) 297186681Sed teken_input_byte(t, *c++); 298186681Sed} 299186681Sed 300186681Sedvoid 301186681Sedteken_set_cursor(teken_t *t, const teken_pos_t *p) 302186681Sed{ 303186681Sed 304186681Sed /* XXX: bounds checking with originreg! */ 305186681Sed teken_assert(p->tp_row < t->t_winsize.tp_row); 306186681Sed teken_assert(p->tp_col < t->t_winsize.tp_col); 307186681Sed 308186681Sed t->t_cursor = *p; 309186681Sed} 310186681Sed 311186681Sedvoid 312186681Sedteken_set_defattr(teken_t *t, const teken_attr_t *a) 313186681Sed{ 314186681Sed 315186681Sed t->t_curattr = t->t_saved_curattr = t->t_defattr = *a; 316186681Sed} 317186681Sed 318186681Sedvoid 319186681Sedteken_set_winsize(teken_t *t, const teken_pos_t *p) 320186681Sed{ 321186681Sed 322186681Sed teken_assert(p->tp_col <= T_NUMCOL); 323186681Sed 324186681Sed t->t_winsize = *p; 325186681Sed /* XXX: bounds checking with cursor/etc! */ 326186681Sed t->t_scrollreg.ts_begin = 0; 327186681Sed t->t_scrollreg.ts_end = t->t_winsize.tp_row; 328186681Sed t->t_originreg = t->t_scrollreg; 329186681Sed} 330186681Sed 331186681Sed/* 332186681Sed * State machine. 333186681Sed */ 334186681Sed 335186681Sedstatic void 336186681Sedteken_state_switch(teken_t *t, teken_state_t *s) 337186681Sed{ 338186681Sed 339186681Sed t->t_nextstate = s; 340186681Sed t->t_curnum = 0; 341186681Sed t->t_stateflags |= TS_FIRSTDIGIT; 342186681Sed} 343186681Sed 344186681Sedstatic int 345186681Sedteken_state_numbers(teken_t *t, teken_char_t c) 346186681Sed{ 347186681Sed 348186681Sed teken_assert(t->t_curnum < T_NUMSIZE); 349186681Sed 350186681Sed if (c >= '0' && c <= '9') { 351186681Sed /* 352186681Sed * Don't do math with the default value of 1 when a 353186681Sed * custom number is inserted. 354186681Sed */ 355186681Sed if (t->t_stateflags & TS_FIRSTDIGIT) { 356186681Sed t->t_stateflags &= ~TS_FIRSTDIGIT; 357186681Sed t->t_nums[t->t_curnum] = 0; 358186681Sed } else { 359186681Sed t->t_nums[t->t_curnum] *= 10; 360186681Sed } 361186681Sed 362186681Sed t->t_nums[t->t_curnum] += c - '0'; 363186681Sed return (1); 364186681Sed } else if (c == ';') { 365186681Sed if (t->t_stateflags & TS_FIRSTDIGIT) 366186681Sed t->t_nums[t->t_curnum] = 0; 367186681Sed 368186681Sed /* Only allow a limited set of arguments. */ 369186681Sed if (++t->t_curnum == T_NUMSIZE) { 370186681Sed teken_state_switch(t, teken_state_init); 371186681Sed return (1); 372186681Sed } 373186681Sed 374186681Sed t->t_stateflags |= TS_FIRSTDIGIT; 375186681Sed return (1); 376186681Sed } else { 377186681Sed if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) { 378186681Sed /* Finish off the last empty argument. */ 379186681Sed t->t_nums[t->t_curnum] = 0; 380186681Sed t->t_curnum++; 381186681Sed } else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) { 382186681Sed /* Also count the last argument. */ 383186681Sed t->t_curnum++; 384186681Sed } 385186681Sed } 386186681Sed 387186681Sed return (0); 388186681Sed} 389186681Sed 390186681Sed#include "teken_state.h" 391