teken.c revision 197522
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 197522 2009-09-26 15:26:32Z 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/* Private flags for t_stateflags. */ 52186681Sed#define TS_FIRSTDIGIT 0x01 /* First numeric digit in escape sequence. */ 53186681Sed#define TS_INSERT 0x02 /* Insert mode. */ 54186681Sed#define TS_AUTOWRAP 0x04 /* Autowrap. */ 55186681Sed#define TS_ORIGIN 0x08 /* Origin mode. */ 56187367Sed#define TS_WRAPPED 0x10 /* Next character should be printed on col 0. */ 57197117Sed#define TS_8BIT 0x20 /* UTF-8 disabled. */ 58197117Sed#define TS_CONS25 0x40 /* cons25 emulation. */ 59186681Sed 60186681Sed/* Character that blanks a cell. */ 61186681Sed#define BLANK ' ' 62186681Sed 63197470Sed#include "teken.h" 64197470Sed#include "teken_wcwidth.h" 65197470Sed#include "teken_scs.h" 66197470Sed 67186681Sedstatic teken_state_t teken_state_init; 68186681Sed 69186681Sed/* 70186681Sed * Wrappers for hooks. 71186681Sed */ 72186681Sed 73186681Sedstatic inline void 74186681Sedteken_funcs_bell(teken_t *t) 75186681Sed{ 76186681Sed 77186681Sed t->t_funcs->tf_bell(t->t_softc); 78186681Sed} 79186681Sed 80186681Sedstatic inline void 81186681Sedteken_funcs_cursor(teken_t *t) 82186681Sed{ 83186681Sed 84186681Sed teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 85186681Sed teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 86186681Sed 87186681Sed t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor); 88186681Sed} 89186681Sed 90186681Sedstatic inline void 91186681Sedteken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c, 92186681Sed const teken_attr_t *a) 93186681Sed{ 94186681Sed 95186681Sed teken_assert(p->tp_row < t->t_winsize.tp_row); 96186681Sed teken_assert(p->tp_col < t->t_winsize.tp_col); 97186681Sed 98186681Sed t->t_funcs->tf_putchar(t->t_softc, p, c, a); 99186681Sed} 100186681Sed 101186681Sedstatic inline void 102186681Sedteken_funcs_fill(teken_t *t, const teken_rect_t *r, 103186681Sed const teken_char_t c, const teken_attr_t *a) 104186681Sed{ 105186681Sed 106186681Sed teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); 107186681Sed teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); 108186681Sed teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); 109186681Sed teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); 110186681Sed 111186681Sed t->t_funcs->tf_fill(t->t_softc, r, c, a); 112186681Sed} 113186681Sed 114186681Sedstatic inline void 115186681Sedteken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p) 116186681Sed{ 117186681Sed 118186681Sed teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); 119186681Sed teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); 120186681Sed teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); 121186681Sed teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); 122186681Sed teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row); 123186681Sed teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col); 124186681Sed 125186681Sed t->t_funcs->tf_copy(t->t_softc, r, p); 126186681Sed} 127186681Sed 128186681Sedstatic inline void 129193184Sedteken_funcs_param(teken_t *t, int cmd, unsigned int value) 130186681Sed{ 131186681Sed 132186681Sed t->t_funcs->tf_param(t->t_softc, cmd, value); 133186681Sed} 134186681Sed 135186681Sedstatic inline void 136186681Sedteken_funcs_respond(teken_t *t, const void *buf, size_t len) 137186681Sed{ 138186681Sed 139186681Sed t->t_funcs->tf_respond(t->t_softc, buf, len); 140186681Sed} 141186681Sed 142186681Sed#include "teken_subr.h" 143186681Sed#include "teken_subr_compat.h" 144186681Sed 145186681Sed/* 146186681Sed * Programming interface. 147186681Sed */ 148186681Sed 149186681Sedvoid 150186681Sedteken_init(teken_t *t, const teken_funcs_t *tf, void *softc) 151186681Sed{ 152186681Sed teken_pos_t tp = { .tp_row = 24, .tp_col = 80 }; 153186681Sed 154186681Sed#if !(defined(__FreeBSD__) && defined(_KERNEL)) 155186681Sed df = fopen("teken.log", "w"); 156186681Sed if (df != NULL) 157186681Sed setvbuf(df, NULL, _IOLBF, BUFSIZ); 158186681Sed#endif /* !(__FreeBSD__ && _KERNEL) */ 159186681Sed 160186681Sed t->t_funcs = tf; 161186681Sed t->t_softc = softc; 162186681Sed 163186681Sed t->t_nextstate = teken_state_init; 164197117Sed t->t_stateflags = 0; 165197117Sed t->t_utf8_left = 0; 166186681Sed 167186681Sed t->t_defattr.ta_format = 0; 168186681Sed t->t_defattr.ta_fgcolor = TC_WHITE; 169186681Sed t->t_defattr.ta_bgcolor = TC_BLACK; 170186681Sed teken_subr_do_reset(t); 171186681Sed 172186681Sed teken_set_winsize(t, &tp); 173186681Sed} 174186681Sed 175186681Sedstatic void 176186681Sedteken_input_char(teken_t *t, teken_char_t c) 177186681Sed{ 178186681Sed 179186681Sed switch (c) { 180186681Sed case '\0': 181186681Sed break; 182186681Sed case '\a': 183186681Sed teken_subr_bell(t); 184186681Sed break; 185186681Sed case '\b': 186186681Sed teken_subr_backspace(t); 187186681Sed break; 188186681Sed case '\n': 189186681Sed case '\x0B': 190186681Sed teken_subr_newline(t); 191186681Sed break; 192186798Sed case '\x0C': 193186798Sed teken_subr_newpage(t); 194186798Sed break; 195187469Sed case '\x0E': 196197117Sed if (t->t_stateflags & TS_CONS25) 197197117Sed t->t_nextstate(t, c); 198197117Sed else 199197520Sed t->t_curscs = 1; 200187469Sed break; 201187469Sed case '\x0F': 202197117Sed if (t->t_stateflags & TS_CONS25) 203197117Sed t->t_nextstate(t, c); 204197117Sed else 205197520Sed t->t_curscs = 0; 206187469Sed break; 207186681Sed case '\r': 208186681Sed teken_subr_carriage_return(t); 209186681Sed break; 210186681Sed case '\t': 211186681Sed teken_subr_horizontal_tab(t); 212186681Sed break; 213186681Sed default: 214186681Sed t->t_nextstate(t, c); 215186681Sed break; 216186681Sed } 217186681Sed 218186681Sed /* Post-processing assertions. */ 219186681Sed teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin); 220186681Sed teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end); 221186681Sed teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 222186681Sed teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 223186681Sed teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row); 224186681Sed teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col); 225186681Sed teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); 226186681Sed teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end); 227186681Sed /* Origin region has to be window size or the same as scrollreg. */ 228186681Sed teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin && 229186681Sed t->t_originreg.ts_end == t->t_scrollreg.ts_end) || 230186681Sed (t->t_originreg.ts_begin == 0 && 231186681Sed t->t_originreg.ts_end == t->t_winsize.tp_row)); 232186681Sed} 233186681Sed 234186681Sedstatic void 235186681Sedteken_input_byte(teken_t *t, unsigned char c) 236186681Sed{ 237186681Sed 238186681Sed /* 239186681Sed * UTF-8 handling. 240186681Sed */ 241197117Sed if ((c & 0x80) == 0x00 || t->t_stateflags & TS_8BIT) { 242186681Sed /* One-byte sequence. */ 243186681Sed t->t_utf8_left = 0; 244186681Sed teken_input_char(t, c); 245186681Sed } else if ((c & 0xe0) == 0xc0) { 246186681Sed /* Two-byte sequence. */ 247186681Sed t->t_utf8_left = 1; 248186681Sed t->t_utf8_partial = c & 0x1f; 249186681Sed } else if ((c & 0xf0) == 0xe0) { 250186681Sed /* Three-byte sequence. */ 251186681Sed t->t_utf8_left = 2; 252186681Sed t->t_utf8_partial = c & 0x0f; 253186681Sed } else if ((c & 0xf8) == 0xf0) { 254186681Sed /* Four-byte sequence. */ 255186681Sed t->t_utf8_left = 3; 256186681Sed t->t_utf8_partial = c & 0x07; 257186681Sed } else if ((c & 0xc0) == 0x80) { 258186681Sed if (t->t_utf8_left == 0) 259186681Sed return; 260186681Sed t->t_utf8_left--; 261186681Sed t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f); 262186681Sed if (t->t_utf8_left == 0) { 263194293Sed teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial); 264186681Sed teken_input_char(t, t->t_utf8_partial); 265186681Sed } 266186681Sed } 267186681Sed} 268186681Sed 269186681Sedvoid 270186681Sedteken_input(teken_t *t, const void *buf, size_t len) 271186681Sed{ 272186681Sed const char *c = buf; 273186681Sed 274186681Sed while (len-- > 0) 275186681Sed teken_input_byte(t, *c++); 276186681Sed} 277186681Sed 278197117Sedconst teken_pos_t * 279197117Sedteken_get_cursor(teken_t *t) 280197117Sed{ 281197117Sed 282197117Sed return (&t->t_cursor); 283197117Sed} 284197117Sed 285186681Sedvoid 286186681Sedteken_set_cursor(teken_t *t, const teken_pos_t *p) 287186681Sed{ 288186681Sed 289186681Sed /* XXX: bounds checking with originreg! */ 290186681Sed teken_assert(p->tp_row < t->t_winsize.tp_row); 291186681Sed teken_assert(p->tp_col < t->t_winsize.tp_col); 292186681Sed 293186681Sed t->t_cursor = *p; 294186681Sed} 295186681Sed 296188391Sedconst teken_attr_t * 297188391Sedteken_get_curattr(teken_t *t) 298188391Sed{ 299188391Sed 300188391Sed return (&t->t_curattr); 301188391Sed} 302188391Sed 303189617Sedvoid 304189617Sedteken_set_curattr(teken_t *t, const teken_attr_t *a) 305189617Sed{ 306189617Sed 307189617Sed t->t_curattr = *a; 308189617Sed} 309189617Sed 310188391Sedconst teken_attr_t * 311188391Sedteken_get_defattr(teken_t *t) 312188391Sed{ 313188391Sed 314188391Sed return (&t->t_defattr); 315188391Sed} 316188391Sed 317186681Sedvoid 318186681Sedteken_set_defattr(teken_t *t, const teken_attr_t *a) 319186681Sed{ 320186681Sed 321186681Sed t->t_curattr = t->t_saved_curattr = t->t_defattr = *a; 322186681Sed} 323186681Sed 324197117Sedconst teken_pos_t * 325197117Sedteken_get_winsize(teken_t *t) 326197117Sed{ 327197117Sed 328197117Sed return (&t->t_winsize); 329197117Sed} 330197117Sed 331186681Sedvoid 332186681Sedteken_set_winsize(teken_t *t, const teken_pos_t *p) 333186681Sed{ 334186681Sed 335186681Sed t->t_winsize = *p; 336197114Sed teken_subr_do_reset(t); 337186681Sed} 338186681Sed 339197115Sedvoid 340197115Sedteken_set_8bit(teken_t *t) 341197115Sed{ 342197115Sed 343197117Sed t->t_stateflags |= TS_8BIT; 344197115Sed} 345197115Sed 346197117Sedvoid 347197117Sedteken_set_cons25(teken_t *t) 348197117Sed{ 349197117Sed 350197117Sed t->t_stateflags |= TS_CONS25; 351197117Sed} 352197117Sed 353186681Sed/* 354186681Sed * State machine. 355186681Sed */ 356186681Sed 357186681Sedstatic void 358186681Sedteken_state_switch(teken_t *t, teken_state_t *s) 359186681Sed{ 360186681Sed 361186681Sed t->t_nextstate = s; 362186681Sed t->t_curnum = 0; 363186681Sed t->t_stateflags |= TS_FIRSTDIGIT; 364186681Sed} 365186681Sed 366186681Sedstatic int 367186681Sedteken_state_numbers(teken_t *t, teken_char_t c) 368186681Sed{ 369186681Sed 370186681Sed teken_assert(t->t_curnum < T_NUMSIZE); 371186681Sed 372186681Sed if (c >= '0' && c <= '9') { 373186681Sed /* 374186681Sed * Don't do math with the default value of 1 when a 375186681Sed * custom number is inserted. 376186681Sed */ 377186681Sed if (t->t_stateflags & TS_FIRSTDIGIT) { 378186681Sed t->t_stateflags &= ~TS_FIRSTDIGIT; 379186681Sed t->t_nums[t->t_curnum] = 0; 380186681Sed } else { 381186681Sed t->t_nums[t->t_curnum] *= 10; 382186681Sed } 383186681Sed 384186681Sed t->t_nums[t->t_curnum] += c - '0'; 385186681Sed return (1); 386186681Sed } else if (c == ';') { 387186681Sed if (t->t_stateflags & TS_FIRSTDIGIT) 388186681Sed t->t_nums[t->t_curnum] = 0; 389186681Sed 390186681Sed /* Only allow a limited set of arguments. */ 391186681Sed if (++t->t_curnum == T_NUMSIZE) { 392186681Sed teken_state_switch(t, teken_state_init); 393186681Sed return (1); 394186681Sed } 395186681Sed 396186681Sed t->t_stateflags |= TS_FIRSTDIGIT; 397186681Sed return (1); 398186681Sed } else { 399186681Sed if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) { 400186681Sed /* Finish off the last empty argument. */ 401186681Sed t->t_nums[t->t_curnum] = 0; 402186681Sed t->t_curnum++; 403186681Sed } else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) { 404186681Sed /* Also count the last argument. */ 405186681Sed t->t_curnum++; 406186681Sed } 407186681Sed } 408186681Sed 409186681Sed return (0); 410186681Sed} 411186681Sed 412197522Sedteken_color_t 413197522Sedteken_256to8(teken_color_t c) 414197522Sed{ 415197522Sed unsigned int r, g, b; 416197522Sed 417197522Sed if (c < 16) { 418197522Sed /* Traditional color indices. */ 419197522Sed return (c % 8); 420197522Sed } else if (c >= 244) { 421197522Sed /* Upper grayscale colors. */ 422197522Sed return (TC_WHITE); 423197522Sed } else if (c >= 232) { 424197522Sed /* Lower grayscale colors. */ 425197522Sed return (TC_BLACK); 426197522Sed } 427197522Sed 428197522Sed /* Convert to RGB. */ 429197522Sed c -= 16; 430197522Sed b = c % 6; 431197522Sed g = (c / 6) % 6; 432197522Sed r = c / 36; 433197522Sed 434197522Sed if (r < g) { 435197522Sed /* Possibly green. */ 436197522Sed if (g < b) 437197522Sed return (TC_BLUE); 438197522Sed else if (g > b) 439197522Sed return (TC_GREEN); 440197522Sed else 441197522Sed return (TC_CYAN); 442197522Sed } else if (r > g) { 443197522Sed /* Possibly red. */ 444197522Sed if (r < b) 445197522Sed return (TC_BLUE); 446197522Sed else if (r > b) 447197522Sed return (TC_RED); 448197522Sed else 449197522Sed return (TC_MAGENTA); 450197522Sed } else { 451197522Sed /* Possibly brown. */ 452197522Sed if (g < b) 453197522Sed return (TC_BLUE); 454197522Sed else if (g > b) 455197522Sed return (TC_BROWN); 456197522Sed else if (r < 3) 457197522Sed return (TC_BLACK); 458197522Sed else 459197522Sed return (TC_WHITE); 460197522Sed } 461197522Sed} 462197522Sed 463186681Sed#include "teken_state.h" 464