1 /* $OpenBSD: wsemul_vt100.c,v 1.32 2014/07/12 18:48:53 tedu Exp $ */ 2 /* $NetBSD: wsemul_vt100.c,v 1.13 2000/04/28 21:56:16 mycroft Exp $ */ 3 4 /* 5 * Copyright (c) 2007, 2013 Miodrag Vallat. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice, this permission notice, and the disclaimer below 10 * appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 /* 21 * Copyright (c) 1998 22 * Matthias Drochner. All rights reserved. 23 * 24 * Redistribution and use in source and binary forms, with or without 25 * modification, are permitted provided that the following conditions 26 * are met: 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 2. Redistributions in binary form must reproduce the above copyright 30 * notice, this list of conditions and the following disclaimer in the 31 * documentation and/or other materials provided with the distribution. 32 * 33 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 34 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 36 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 37 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 39 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 40 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 42 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 * 44 */ 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/time.h> 49 #include <sys/malloc.h> 50 #include <sys/fcntl.h> 51 52 #include <dev/wscons/wscons_features.h> 53 #include <dev/wscons/wsconsio.h> 54 #include <dev/wscons/wsdisplayvar.h> 55 #include <dev/wscons/wsemulvar.h> 56 #include <dev/wscons/wsemul_vt100var.h> 57 #include <dev/wscons/ascii.h> 58 59 void *wsemul_vt100_cnattach(const struct wsscreen_descr *, void *, 60 int, int, long); 61 void *wsemul_vt100_attach(int, const struct wsscreen_descr *, 62 void *, int, int, void *, long); 63 u_int wsemul_vt100_output(void *, const u_char *, u_int, int); 64 void wsemul_vt100_detach(void *, u_int *, u_int *); 65 void wsemul_vt100_resetop(void *, enum wsemul_resetops); 66 67 const struct wsemul_ops wsemul_vt100_ops = { 68 "vt100", 69 wsemul_vt100_cnattach, 70 wsemul_vt100_attach, 71 wsemul_vt100_output, 72 wsemul_vt100_translate, 73 wsemul_vt100_detach, 74 wsemul_vt100_resetop 75 }; 76 77 struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata; 78 79 void wsemul_vt100_init(struct wsemul_vt100_emuldata *, 80 const struct wsscreen_descr *, void *, int, int, long); 81 int wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *, 82 const u_char *, u_int, int); 83 int wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *, 84 struct wsemul_inputstate *, int); 85 int wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *, 86 struct wsemul_inputstate *, int); 87 int wsemul_vt100_nextline(struct wsemul_vt100_emuldata *); 88 89 typedef int vt100_handler(struct wsemul_vt100_emuldata *, struct 90 wsemul_inputstate *); 91 vt100_handler 92 wsemul_vt100_output_esc, 93 wsemul_vt100_output_csi, 94 wsemul_vt100_output_scs94, 95 wsemul_vt100_output_scs94_percent, 96 wsemul_vt100_output_scs96, 97 wsemul_vt100_output_scs96_percent, 98 wsemul_vt100_output_esc_hash, 99 wsemul_vt100_output_esc_spc, 100 wsemul_vt100_output_string, 101 wsemul_vt100_output_string_esc, 102 wsemul_vt100_output_dcs, 103 wsemul_vt100_output_dcs_dollar, 104 wsemul_vt100_output_esc_percent; 105 106 #define VT100_EMUL_STATE_NORMAL 0 /* normal processing */ 107 #define VT100_EMUL_STATE_ESC 1 /* got ESC */ 108 #define VT100_EMUL_STATE_CSI 2 /* got CSI (ESC[) */ 109 #define VT100_EMUL_STATE_SCS94 3 /* got ESC{()*+} */ 110 #define VT100_EMUL_STATE_SCS94_PERCENT 4 /* got ESC{()*+}% */ 111 #define VT100_EMUL_STATE_SCS96 5 /* got ESC{-./} */ 112 #define VT100_EMUL_STATE_SCS96_PERCENT 6 /* got ESC{-./}% */ 113 #define VT100_EMUL_STATE_ESC_HASH 7 /* got ESC# */ 114 #define VT100_EMUL_STATE_ESC_SPC 8 /* got ESC<SPC> */ 115 #define VT100_EMUL_STATE_STRING 9 /* waiting for ST (ESC\) */ 116 #define VT100_EMUL_STATE_STRING_ESC 10 /* waiting for ST, got ESC */ 117 #define VT100_EMUL_STATE_DCS 11 /* got DCS (ESC P) */ 118 #define VT100_EMUL_STATE_DCS_DOLLAR 12 /* got DCS<p>$ */ 119 #define VT100_EMUL_STATE_ESC_PERCENT 13 /* got ESC% */ 120 121 vt100_handler *vt100_output[] = { 122 wsemul_vt100_output_esc, 123 wsemul_vt100_output_csi, 124 wsemul_vt100_output_scs94, 125 wsemul_vt100_output_scs94_percent, 126 wsemul_vt100_output_scs96, 127 wsemul_vt100_output_scs96_percent, 128 wsemul_vt100_output_esc_hash, 129 wsemul_vt100_output_esc_spc, 130 wsemul_vt100_output_string, 131 wsemul_vt100_output_string_esc, 132 wsemul_vt100_output_dcs, 133 wsemul_vt100_output_dcs_dollar, 134 wsemul_vt100_output_esc_percent, 135 }; 136 137 void 138 wsemul_vt100_init(struct wsemul_vt100_emuldata *edp, 139 const struct wsscreen_descr *type, void *cookie, int ccol, int crow, 140 long defattr) 141 { 142 edp->emulops = type->textops; 143 edp->emulcookie = cookie; 144 edp->scrcapabilities = type->capabilities; 145 edp->nrows = type->nrows; 146 edp->ncols = type->ncols; 147 edp->crow = crow; 148 edp->ccol = ccol; 149 edp->defattr = defattr; 150 wsemul_reset_abortstate(&edp->abortstate); 151 } 152 153 void * 154 wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol, 155 int crow, long defattr) 156 { 157 struct wsemul_vt100_emuldata *edp; 158 int res; 159 160 edp = &wsemul_vt100_console_emuldata; 161 wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr); 162 #ifdef DIAGNOSTIC 163 edp->console = 1; 164 #endif 165 edp->cbcookie = NULL; 166 167 #ifndef WS_KERNEL_FG 168 #define WS_KERNEL_FG WSCOL_WHITE 169 #endif 170 #ifndef WS_KERNEL_BG 171 #define WS_KERNEL_BG WSCOL_BLUE 172 #endif 173 #ifndef WS_KERNEL_COLATTR 174 #define WS_KERNEL_COLATTR 0 175 #endif 176 #ifndef WS_KERNEL_MONOATTR 177 #define WS_KERNEL_MONOATTR 0 178 #endif 179 if (type->capabilities & WSSCREEN_WSCOLORS) 180 res = (*edp->emulops->alloc_attr)(cookie, 181 WS_KERNEL_FG, WS_KERNEL_BG, 182 WS_KERNEL_COLATTR | WSATTR_WSCOLORS, &edp->kernattr); 183 else 184 res = (*edp->emulops->alloc_attr)(cookie, 0, 0, 185 WS_KERNEL_MONOATTR, &edp->kernattr); 186 if (res) 187 edp->kernattr = defattr; 188 189 edp->tabs = NULL; 190 edp->dblwid = NULL; 191 edp->dw = 0; 192 edp->dcsarg = 0; 193 edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = NULL; 194 edp->nrctab = NULL; 195 wsemul_vt100_reset(edp); 196 return (edp); 197 } 198 199 void * 200 wsemul_vt100_attach(int console, const struct wsscreen_descr *type, 201 void *cookie, int ccol, int crow, void *cbcookie, long defattr) 202 { 203 struct wsemul_vt100_emuldata *edp; 204 205 if (console) { 206 edp = &wsemul_vt100_console_emuldata; 207 #ifdef DIAGNOSTIC 208 KASSERT(edp->console == 1); 209 #endif 210 } else { 211 edp = malloc(sizeof *edp, M_DEVBUF, M_NOWAIT); 212 if (edp == NULL) 213 return (NULL); 214 wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr); 215 #ifdef DIAGNOSTIC 216 edp->console = 0; 217 #endif 218 } 219 edp->cbcookie = cbcookie; 220 221 edp->tabs = malloc(edp->ncols, M_DEVBUF, M_NOWAIT); 222 edp->dblwid = malloc(edp->nrows, M_DEVBUF, M_NOWAIT | M_ZERO); 223 edp->dw = 0; 224 edp->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT); 225 edp->isolatin1tab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT); 226 edp->decgraphtab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT); 227 edp->dectechtab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT); 228 edp->nrctab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT); 229 vt100_initchartables(edp); 230 wsemul_vt100_reset(edp); 231 return (edp); 232 } 233 234 void 235 wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp) 236 { 237 struct wsemul_vt100_emuldata *edp = cookie; 238 239 *crowp = edp->crow; 240 *ccolp = edp->ccol; 241 #define f(ptr) if (ptr) {free(ptr, M_DEVBUF, 0); ptr = NULL;} 242 f(edp->tabs) 243 f(edp->dblwid) 244 f(edp->dcsarg) 245 f(edp->isolatin1tab) 246 f(edp->decgraphtab) 247 f(edp->dectechtab) 248 f(edp->nrctab) 249 #undef f 250 if (edp != &wsemul_vt100_console_emuldata) 251 free(edp, M_DEVBUF, 0); 252 } 253 254 void 255 wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op) 256 { 257 struct wsemul_vt100_emuldata *edp = cookie; 258 259 switch (op) { 260 case WSEMUL_RESET: 261 wsemul_vt100_reset(edp); 262 break; 263 case WSEMUL_SYNCFONT: 264 vt100_initchartables(edp); 265 break; 266 case WSEMUL_CLEARSCREEN: 267 (void)wsemul_vt100_ed(edp, 2); 268 edp->ccol = edp->crow = 0; 269 (*edp->emulops->cursor)(edp->emulcookie, 270 edp->flags & VTFL_CURSORON, 0, 0); 271 break; 272 case WSEMUL_CLEARCURSOR: 273 (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow, 274 edp->ccol); 275 break; 276 default: 277 break; 278 } 279 } 280 281 void 282 wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp) 283 { 284 int i; 285 286 edp->state = VT100_EMUL_STATE_NORMAL; 287 edp->flags = VTFL_DECAWM | VTFL_CURSORON; 288 edp->bkgdattr = edp->curattr = edp->defattr; 289 edp->attrflags = 0; 290 edp->fgcol = WSCOL_WHITE; 291 edp->bgcol = WSCOL_BLACK; 292 edp->scrreg_startrow = 0; 293 edp->scrreg_nrows = edp->nrows; 294 if (edp->tabs) { 295 memset(edp->tabs, 0, edp->ncols); 296 for (i = 8; i < edp->ncols; i += 8) 297 edp->tabs[i] = 1; 298 } 299 edp->dcspos = 0; 300 edp->dcstype = 0; 301 edp->chartab_G[0] = NULL; 302 edp->chartab_G[1] = edp->nrctab; /* ??? */ 303 edp->chartab_G[2] = edp->isolatin1tab; 304 edp->chartab_G[3] = edp->isolatin1tab; 305 edp->chartab0 = 0; 306 edp->chartab1 = 2; 307 edp->sschartab = 0; 308 } 309 310 /* 311 * Move the cursor to the next line if possible. If the cursor is at 312 * the bottom of the scroll area, then scroll it up. If the cursor is 313 * at the bottom of the screen then don't move it down. 314 */ 315 int 316 wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp) 317 { 318 int rc; 319 320 if (ROWS_BELOW == 0) { 321 /* Bottom of the scroll region. */ 322 rc = wsemul_vt100_scrollup(edp, 1); 323 } else { 324 if ((edp->crow+1) < edp->nrows) 325 /* Cursor not at the bottom of the screen. */ 326 edp->crow++; 327 CHECK_DW; 328 rc = 0; 329 } 330 331 return rc; 332 } 333 334 /* 335 * now all the state machine bits 336 */ 337 338 int 339 wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, 340 struct wsemul_inputstate *instate, int kernel) 341 { 342 u_int *ct, dc; 343 u_char c; 344 int oldsschartab = edp->sschartab; 345 int rc = 0; 346 347 if ((edp->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) == 348 (VTFL_LASTCHAR | VTFL_DECAWM)) { 349 rc = wsemul_vt100_nextline(edp); 350 if (rc != 0) 351 return rc; 352 edp->ccol = 0; 353 edp->flags &= ~VTFL_LASTCHAR; 354 } 355 356 #ifdef HAVE_UTF8_SUPPORT 357 if (edp->flags & VTFL_UTF8) { 358 (*edp->emulops->mapchar)(edp->emulcookie, instate->inchar, &dc); 359 } else 360 #endif 361 { 362 c = instate->inchar & 0xff; 363 if (c & 0x80) { 364 c &= 0x7f; 365 ct = edp->chartab_G[edp->chartab1]; 366 } else { 367 if (edp->sschartab) { 368 ct = edp->chartab_G[edp->sschartab]; 369 edp->sschartab = 0; 370 } else 371 ct = edp->chartab_G[edp->chartab0]; 372 } 373 dc = ct ? ct[c] : c; 374 } 375 376 if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT) { 377 WSEMULOP(rc, edp, &edp->abortstate, copycols, 378 COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT)); 379 if (rc != 0) { 380 /* undo potential sschartab update */ 381 edp->sschartab = oldsschartab; 382 383 return rc; 384 } 385 } 386 387 WSEMULOP(rc, edp, &edp->abortstate, putchar, 388 (edp->emulcookie, edp->crow, edp->ccol << edp->dw, dc, 389 kernel ? edp->kernattr : edp->curattr)); 390 if (rc != 0) { 391 /* undo potential sschartab update */ 392 edp->sschartab = oldsschartab; 393 394 return rc; 395 } 396 397 if (COLS_LEFT) 398 edp->ccol++; 399 else 400 edp->flags |= VTFL_LASTCHAR; 401 402 return 0; 403 } 404 405 int 406 wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, 407 struct wsemul_inputstate *instate, int kernel) 408 { 409 u_int n; 410 int rc = 0; 411 412 switch (instate->inchar) { 413 case ASCII_NUL: 414 default: 415 /* ignore */ 416 break; 417 case ASCII_BEL: 418 if (edp->state == VT100_EMUL_STATE_STRING) { 419 /* acts as an equivalent to the ``ESC \'' string end */ 420 wsemul_vt100_handle_dcs(edp); 421 edp->state = VT100_EMUL_STATE_NORMAL; 422 } else { 423 wsdisplay_emulbell(edp->cbcookie); 424 } 425 break; 426 case ASCII_BS: 427 if (edp->ccol > 0) { 428 edp->ccol--; 429 edp->flags &= ~VTFL_LASTCHAR; 430 } 431 break; 432 case ASCII_CR: 433 edp->ccol = 0; 434 break; 435 case ASCII_HT: 436 if (edp->tabs) { 437 if (!COLS_LEFT) 438 break; 439 for (n = edp->ccol + 1; n < NCOLS - 1; n++) 440 if (edp->tabs[n]) 441 break; 442 } else { 443 n = edp->ccol + min(8 - (edp->ccol & 7), COLS_LEFT); 444 } 445 edp->ccol = n; 446 break; 447 case ASCII_SO: /* LS1 */ 448 edp->flags &= ~VTFL_UTF8; 449 edp->chartab0 = 1; 450 break; 451 case ASCII_SI: /* LS0 */ 452 edp->flags &= ~VTFL_UTF8; 453 edp->chartab0 = 0; 454 break; 455 case ASCII_ESC: 456 if (kernel) { 457 printf("wsemul_vt100_output_c0c1: ESC in kernel " 458 "output ignored\n"); 459 break; /* ignore the ESC */ 460 } 461 462 if (edp->state == VT100_EMUL_STATE_STRING) { 463 /* might be a string end */ 464 edp->state = VT100_EMUL_STATE_STRING_ESC; 465 } else { 466 /* XXX cancel current escape sequence */ 467 edp->state = VT100_EMUL_STATE_ESC; 468 } 469 break; 470 case ASCII_CAN: 471 case ASCII_SUB: 472 /* cancel current escape sequence */ 473 edp->state = VT100_EMUL_STATE_NORMAL; 474 break; 475 #if 0 476 case CSI: /* 8-bit */ 477 /* XXX cancel current escape sequence */ 478 edp->nargs = 0; 479 memset(edp->args, 0, sizeof (edp->args)); 480 edp->modif1 = edp->modif2 = '\0'; 481 edp->state = VT100_EMUL_STATE_CSI; 482 break; 483 case DCS: /* 8-bit */ 484 /* XXX cancel current escape sequence */ 485 edp->nargs = 0; 486 memset(edp->args, 0, sizeof (edp->args)); 487 edp->state = VT100_EMUL_STATE_DCS; 488 break; 489 case ST: /* string end 8-bit */ 490 /* XXX only in VT100_EMUL_STATE_STRING */ 491 wsemul_vt100_handle_dcs(edp); 492 return (VT100_EMUL_STATE_NORMAL); 493 #endif 494 case ASCII_LF: 495 case ASCII_VT: 496 case ASCII_FF: 497 rc = wsemul_vt100_nextline(edp); 498 break; 499 } 500 501 if (COLS_LEFT != 0) 502 edp->flags &= ~VTFL_LASTCHAR; 503 504 return rc; 505 } 506 507 int 508 wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, 509 struct wsemul_inputstate *instate) 510 { 511 u_int newstate = VT100_EMUL_STATE_NORMAL; 512 int rc = 0; 513 int i; 514 515 switch (instate->inchar) { 516 case '[': /* CSI */ 517 edp->nargs = 0; 518 memset(edp->args, 0, sizeof (edp->args)); 519 edp->modif1 = edp->modif2 = '\0'; 520 newstate = VT100_EMUL_STATE_CSI; 521 break; 522 case '7': /* DECSC */ 523 edp->flags |= VTFL_SAVEDCURS; 524 edp->savedcursor_row = edp->crow; 525 edp->savedcursor_col = edp->ccol; 526 edp->savedattr = edp->curattr; 527 edp->savedbkgdattr = edp->bkgdattr; 528 edp->savedattrflags = edp->attrflags; 529 edp->savedfgcol = edp->fgcol; 530 edp->savedbgcol = edp->bgcol; 531 for (i = 0; i < 4; i++) 532 edp->savedchartab_G[i] = edp->chartab_G[i]; 533 edp->savedchartab0 = edp->chartab0; 534 edp->savedchartab1 = edp->chartab1; 535 break; 536 case '8': /* DECRC */ 537 if ((edp->flags & VTFL_SAVEDCURS) == 0) 538 break; 539 edp->crow = edp->savedcursor_row; 540 edp->ccol = edp->savedcursor_col; 541 edp->curattr = edp->savedattr; 542 edp->bkgdattr = edp->savedbkgdattr; 543 edp->attrflags = edp->savedattrflags; 544 edp->fgcol = edp->savedfgcol; 545 edp->bgcol = edp->savedbgcol; 546 for (i = 0; i < 4; i++) 547 edp->chartab_G[i] = edp->savedchartab_G[i]; 548 edp->chartab0 = edp->savedchartab0; 549 edp->chartab1 = edp->savedchartab1; 550 break; 551 case '=': /* DECKPAM application mode */ 552 edp->flags |= VTFL_APPLKEYPAD; 553 break; 554 case '>': /* DECKPNM numeric mode */ 555 edp->flags &= ~VTFL_APPLKEYPAD; 556 break; 557 case 'E': /* NEL */ 558 edp->ccol = 0; 559 /* FALLTHROUGH */ 560 case 'D': /* IND */ 561 rc = wsemul_vt100_nextline(edp); 562 break; 563 case 'H': /* HTS */ 564 if (edp->tabs != NULL) 565 edp->tabs[edp->ccol] = 1; 566 break; 567 case '~': /* LS1R */ 568 edp->flags &= ~VTFL_UTF8; 569 edp->chartab1 = 1; 570 break; 571 case 'n': /* LS2 */ 572 edp->flags &= ~VTFL_UTF8; 573 edp->chartab0 = 2; 574 break; 575 case '}': /* LS2R */ 576 edp->flags &= ~VTFL_UTF8; 577 edp->chartab1 = 2; 578 break; 579 case 'o': /* LS3 */ 580 edp->flags &= ~VTFL_UTF8; 581 edp->chartab0 = 3; 582 break; 583 case '|': /* LS3R */ 584 edp->flags &= ~VTFL_UTF8; 585 edp->chartab1 = 3; 586 break; 587 case 'N': /* SS2 */ 588 edp->flags &= ~VTFL_UTF8; 589 edp->sschartab = 2; 590 break; 591 case 'O': /* SS3 */ 592 edp->flags &= ~VTFL_UTF8; 593 edp->sschartab = 3; 594 break; 595 case 'M': /* RI */ 596 if (ROWS_ABOVE > 0) { 597 edp->crow--; 598 CHECK_DW; 599 break; 600 } 601 rc = wsemul_vt100_scrolldown(edp, 1); 602 break; 603 case 'P': /* DCS */ 604 edp->nargs = 0; 605 memset(edp->args, 0, sizeof (edp->args)); 606 newstate = VT100_EMUL_STATE_DCS; 607 break; 608 case 'c': /* RIS */ 609 wsemul_vt100_reset(edp); 610 rc = wsemul_vt100_ed(edp, 2); 611 if (rc != 0) 612 break; 613 edp->ccol = edp->crow = 0; 614 break; 615 case '(': case ')': case '*': case '+': /* SCS */ 616 edp->designating = instate->inchar - '('; 617 newstate = VT100_EMUL_STATE_SCS94; 618 break; 619 case '-': case '.': case '/': /* SCS */ 620 edp->designating = instate->inchar - '-' + 1; 621 newstate = VT100_EMUL_STATE_SCS96; 622 break; 623 case '#': 624 newstate = VT100_EMUL_STATE_ESC_HASH; 625 break; 626 case ' ': /* 7/8 bit */ 627 newstate = VT100_EMUL_STATE_ESC_SPC; 628 break; 629 case ']': /* OSC operating system command */ 630 case '^': /* PM privacy message */ 631 case '_': /* APC application program command */ 632 /* ignored */ 633 newstate = VT100_EMUL_STATE_STRING; 634 break; 635 case '<': /* exit VT52 mode - ignored */ 636 break; 637 case '%': /* UTF-8 encoding sequences */ 638 newstate = VT100_EMUL_STATE_ESC_PERCENT; 639 break; 640 default: 641 #ifdef VT100_PRINTUNKNOWN 642 printf("ESC %x unknown\n", instate->inchar); 643 #endif 644 break; 645 } 646 647 if (COLS_LEFT != 0) 648 edp->flags &= ~VTFL_LASTCHAR; 649 650 if (rc != 0) 651 return rc; 652 653 edp->state = newstate; 654 return 0; 655 } 656 657 int 658 wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, 659 struct wsemul_inputstate *instate) 660 { 661 u_int newstate = VT100_EMUL_STATE_NORMAL; 662 663 switch (instate->inchar) { 664 case '%': /* probably DEC supplemental graphic */ 665 newstate = VT100_EMUL_STATE_SCS94_PERCENT; 666 break; 667 case 'A': /* british / national */ 668 edp->flags &= ~VTFL_UTF8; 669 edp->chartab_G[edp->designating] = edp->nrctab; 670 break; 671 case 'B': /* ASCII */ 672 edp->flags &= ~VTFL_UTF8; 673 edp->chartab_G[edp->designating] = 0; 674 break; 675 case '<': /* user preferred supplemental */ 676 /* XXX not really "user" preferred */ 677 edp->flags &= ~VTFL_UTF8; 678 edp->chartab_G[edp->designating] = edp->isolatin1tab; 679 break; 680 case '0': /* DEC special graphic */ 681 edp->flags &= ~VTFL_UTF8; 682 edp->chartab_G[edp->designating] = edp->decgraphtab; 683 break; 684 case '>': /* DEC tech */ 685 edp->flags &= ~VTFL_UTF8; 686 edp->chartab_G[edp->designating] = edp->dectechtab; 687 break; 688 default: 689 #ifdef VT100_PRINTUNKNOWN 690 printf("ESC%c %x unknown\n", edp->designating + '(', 691 instate->inchar); 692 #endif 693 break; 694 } 695 696 edp->state = newstate; 697 return 0; 698 } 699 700 int 701 wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, 702 struct wsemul_inputstate *instate) 703 { 704 switch (instate->inchar) { 705 case '5': /* DEC supplemental graphic */ 706 /* XXX there are differences */ 707 edp->flags &= ~VTFL_UTF8; 708 edp->chartab_G[edp->designating] = edp->isolatin1tab; 709 break; 710 default: 711 #ifdef VT100_PRINTUNKNOWN 712 printf("ESC%c%% %x unknown\n", edp->designating + '(', 713 instate->inchar); 714 #endif 715 break; 716 } 717 718 edp->state = VT100_EMUL_STATE_NORMAL; 719 return 0; 720 } 721 722 int 723 wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, 724 struct wsemul_inputstate *instate) 725 { 726 u_int newstate = VT100_EMUL_STATE_NORMAL; 727 int nrc; 728 729 switch (instate->inchar) { 730 case '%': /* probably portuguese */ 731 newstate = VT100_EMUL_STATE_SCS96_PERCENT; 732 break; 733 case 'A': /* ISO-latin-1 supplemental */ 734 edp->flags &= ~VTFL_UTF8; 735 edp->chartab_G[edp->designating] = edp->isolatin1tab; 736 break; 737 case '4': /* dutch */ 738 nrc = 1; 739 goto setnrc; 740 case '5': case 'C': /* finnish */ 741 nrc = 2; 742 goto setnrc; 743 case 'R': /* french */ 744 nrc = 3; 745 goto setnrc; 746 case 'Q': /* french canadian */ 747 nrc = 4; 748 goto setnrc; 749 case 'K': /* german */ 750 nrc = 5; 751 goto setnrc; 752 case 'Y': /* italian */ 753 nrc = 6; 754 goto setnrc; 755 case 'E': case '6': /* norwegian / danish */ 756 nrc = 7; 757 goto setnrc; 758 case 'Z': /* spanish */ 759 nrc = 9; 760 goto setnrc; 761 case '7': case 'H': /* swedish */ 762 nrc = 10; 763 goto setnrc; 764 case '=': /* swiss */ 765 nrc = 11; 766 setnrc: 767 if (vt100_setnrc(edp, nrc) == 0) /* what table ??? */ 768 break; 769 /* else FALLTHROUGH */ 770 default: 771 #ifdef VT100_PRINTUNKNOWN 772 printf("ESC%c %x unknown\n", edp->designating + '-' - 1, 773 instate->inchar); 774 #endif 775 break; 776 } 777 778 edp->state = newstate; 779 return 0; 780 } 781 782 int 783 wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, 784 struct wsemul_inputstate *instate) 785 { 786 switch (instate->inchar) { 787 case '6': /* portuguese */ 788 if (vt100_setnrc(edp, 8) == 0) 789 break; 790 /* else FALLTHROUGH */ 791 default: 792 #ifdef VT100_PRINTUNKNOWN 793 printf("ESC%c%% %x unknown\n", edp->designating + '-' - 1, 794 instate->inchar); 795 #endif 796 break; 797 } 798 799 edp->state = VT100_EMUL_STATE_NORMAL; 800 return 0; 801 } 802 803 int 804 wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, 805 struct wsemul_inputstate *instate) 806 { 807 switch (instate->inchar) { 808 case 'F': /* 7-bit controls */ 809 case 'G': /* 8-bit controls */ 810 #ifdef VT100_PRINTNOTIMPL 811 printf("ESC<SPC> %x ignored\n", instate->inchar); 812 #endif 813 break; 814 default: 815 #ifdef VT100_PRINTUNKNOWN 816 printf("ESC<SPC> %x unknown\n", instate->inchar); 817 #endif 818 break; 819 } 820 821 edp->state = VT100_EMUL_STATE_NORMAL; 822 return 0; 823 } 824 825 int 826 wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, 827 struct wsemul_inputstate *instate) 828 { 829 if (edp->dcstype && edp->dcspos < DCS_MAXLEN) { 830 if (instate->inchar & ~0xff) { 831 #ifdef VT100_PRINTUNKNOWN 832 printf("unknown char %x in DCS\n", instate->inchar); 833 #endif 834 } else 835 edp->dcsarg[edp->dcspos++] = (char)instate->inchar; 836 } 837 838 edp->state = VT100_EMUL_STATE_STRING; 839 return 0; 840 } 841 842 int 843 wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, 844 struct wsemul_inputstate *instate) 845 { 846 if (instate->inchar == '\\') { /* ST complete */ 847 wsemul_vt100_handle_dcs(edp); 848 edp->state = VT100_EMUL_STATE_NORMAL; 849 } else 850 edp->state = VT100_EMUL_STATE_STRING; 851 852 return 0; 853 } 854 855 int 856 wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, 857 struct wsemul_inputstate *instate) 858 { 859 u_int newstate = VT100_EMUL_STATE_DCS; 860 861 switch (instate->inchar) { 862 case '0': case '1': case '2': case '3': case '4': 863 case '5': case '6': case '7': case '8': case '9': 864 /* argument digit */ 865 if (edp->nargs > VT100_EMUL_NARGS - 1) 866 break; 867 edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) + 868 (instate->inchar - '0'); 869 break; 870 case ';': /* argument terminator */ 871 edp->nargs++; 872 break; 873 default: 874 edp->nargs++; 875 if (edp->nargs > VT100_EMUL_NARGS) { 876 #ifdef VT100_DEBUG 877 printf("vt100: too many arguments\n"); 878 #endif 879 edp->nargs = VT100_EMUL_NARGS; 880 } 881 newstate = VT100_EMUL_STATE_STRING; 882 switch (instate->inchar) { 883 case '$': 884 newstate = VT100_EMUL_STATE_DCS_DOLLAR; 885 break; 886 case '{': /* DECDLD soft charset */ /* } */ 887 case '!': /* DECRQUPSS user preferred supplemental set */ 888 /* 'u' must follow - need another state */ 889 case '|': /* DECUDK program F6..F20 */ 890 #ifdef VT100_PRINTNOTIMPL 891 printf("DCS%c ignored\n", (char)instate->inchar); 892 #endif 893 break; 894 default: 895 #ifdef VT100_PRINTUNKNOWN 896 printf("DCS %x (%d, %d) unknown\n", instate->inchar, 897 ARG(0), ARG(1)); 898 #endif 899 break; 900 } 901 } 902 903 edp->state = newstate; 904 return 0; 905 } 906 907 int 908 wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, 909 struct wsemul_inputstate *instate) 910 { 911 switch (instate->inchar) { 912 case 'p': /* DECRSTS terminal state restore */ 913 case 'q': /* DECRQSS control function request */ 914 #ifdef VT100_PRINTNOTIMPL 915 printf("DCS$%c ignored\n", (char)instate->inchar); 916 #endif 917 break; 918 case 't': /* DECRSPS restore presentation state */ 919 switch (ARG(0)) { 920 case 0: /* error */ 921 break; 922 case 1: /* cursor information restore */ 923 #ifdef VT100_PRINTNOTIMPL 924 printf("DCS1$t ignored\n"); 925 #endif 926 break; 927 case 2: /* tab stop restore */ 928 edp->dcspos = 0; 929 edp->dcstype = DCSTYPE_TABRESTORE; 930 break; 931 default: 932 #ifdef VT100_PRINTUNKNOWN 933 printf("DCS%d$t unknown\n", ARG(0)); 934 #endif 935 break; 936 } 937 break; 938 default: 939 #ifdef VT100_PRINTUNKNOWN 940 printf("DCS$ %x (%d, %d) unknown\n", 941 instate->inchar, ARG(0), ARG(1)); 942 #endif 943 break; 944 } 945 946 edp->state = VT100_EMUL_STATE_STRING; 947 return 0; 948 } 949 950 int 951 wsemul_vt100_output_esc_percent(struct wsemul_vt100_emuldata *edp, 952 struct wsemul_inputstate *instate) 953 { 954 switch (instate->inchar) { 955 #ifdef HAVE_UTF8_SUPPORT 956 case 'G': 957 edp->flags |= VTFL_UTF8; 958 edp->kstate.mbleft = edp->instate.mbleft = 0; 959 break; 960 case '@': 961 edp->flags &= ~VTFL_UTF8; 962 break; 963 #endif 964 default: 965 #ifdef VT100_PRINTUNKNOWN 966 printf("ESC% %x unknown\n", instate->inchar); 967 #endif 968 break; 969 } 970 edp->state = VT100_EMUL_STATE_NORMAL; 971 return 0; 972 } 973 974 int 975 wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, 976 struct wsemul_inputstate *instate) 977 { 978 int i; 979 int rc = 0; 980 981 switch (instate->inchar) { 982 case '5': /* DECSWL single width, single height */ 983 if (edp->dblwid != NULL && edp->dw != 0) { 984 for (i = 0; i < edp->ncols / 2; i++) { 985 WSEMULOP(rc, edp, &edp->abortstate, copycols, 986 (edp->emulcookie, edp->crow, 2 * i, i, 1)); 987 if (rc != 0) 988 return rc; 989 } 990 WSEMULOP(rc, edp, &edp->abortstate, erasecols, 991 (edp->emulcookie, edp->crow, i, edp->ncols - i, 992 edp->bkgdattr)); 993 if (rc != 0) 994 return rc; 995 edp->dblwid[edp->crow] = 0; 996 edp->dw = 0; 997 } 998 break; 999 case '6': /* DECDWL double width, single height */ 1000 case '3': /* DECDHL double width, double height, top half */ 1001 case '4': /* DECDHL double width, double height, bottom half */ 1002 if (edp->dblwid != NULL && edp->dw == 0) { 1003 for (i = edp->ncols / 2 - 1; i >= 0; i--) { 1004 WSEMULOP(rc, edp, &edp->abortstate, copycols, 1005 (edp->emulcookie, edp->crow, i, 2 * i, 1)); 1006 if (rc != 0) 1007 return rc; 1008 } 1009 for (i = 0; i < edp->ncols / 2; i++) { 1010 WSEMULOP(rc, edp, &edp->abortstate, erasecols, 1011 (edp->emulcookie, edp->crow, 2 * i + 1, 1, 1012 edp->bkgdattr)); 1013 if (rc != 0) 1014 return rc; 1015 } 1016 edp->dblwid[edp->crow] = 1; 1017 edp->dw = 1; 1018 if (edp->ccol > (edp->ncols >> 1) - 1) 1019 edp->ccol = (edp->ncols >> 1) - 1; 1020 } 1021 break; 1022 case '8': { /* DECALN */ 1023 int i, j; 1024 for (i = 0; i < edp->nrows; i++) 1025 for (j = 0; j < edp->ncols; j++) { 1026 WSEMULOP(rc, edp, &edp->abortstate, putchar, 1027 (edp->emulcookie, i, j, 'E', edp->curattr)); 1028 if (rc != 0) 1029 return rc; 1030 } 1031 } 1032 edp->ccol = 0; 1033 edp->crow = 0; 1034 break; 1035 default: 1036 #ifdef VT100_PRINTUNKNOWN 1037 printf("ESC# %x unknown\n", instate->inchar); 1038 #endif 1039 break; 1040 } 1041 1042 if (COLS_LEFT != 0) 1043 edp->flags &= ~VTFL_LASTCHAR; 1044 1045 edp->state = VT100_EMUL_STATE_NORMAL; 1046 return 0; 1047 } 1048 1049 int 1050 wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, 1051 struct wsemul_inputstate *instate) 1052 { 1053 u_int newstate = VT100_EMUL_STATE_CSI; 1054 int oargs; 1055 int rc = 0; 1056 1057 switch (instate->inchar) { 1058 case '0': case '1': case '2': case '3': case '4': 1059 case '5': case '6': case '7': case '8': case '9': 1060 /* argument digit */ 1061 if (edp->nargs > VT100_EMUL_NARGS - 1) 1062 break; 1063 edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) + 1064 (instate->inchar - '0'); 1065 break; 1066 case ';': /* argument terminator */ 1067 edp->nargs++; 1068 break; 1069 case '?': /* DEC specific */ 1070 case '>': /* DA query */ 1071 edp->modif1 = (char)instate->inchar; 1072 break; 1073 case '!': 1074 case '"': 1075 case '$': 1076 case '&': 1077 edp->modif2 = (char)instate->inchar; 1078 break; 1079 default: /* end of escape sequence */ 1080 oargs = edp->nargs++; 1081 if (edp->nargs > VT100_EMUL_NARGS) { 1082 #ifdef VT100_DEBUG 1083 printf("vt100: too many arguments\n"); 1084 #endif 1085 edp->nargs = VT100_EMUL_NARGS; 1086 } 1087 rc = wsemul_vt100_handle_csi(edp, instate); 1088 if (rc != 0) { 1089 edp->nargs = oargs; 1090 return rc; 1091 } 1092 newstate = VT100_EMUL_STATE_NORMAL; 1093 break; 1094 } 1095 1096 if (COLS_LEFT != 0) 1097 edp->flags &= ~VTFL_LASTCHAR; 1098 1099 edp->state = newstate; 1100 return 0; 1101 } 1102 1103 u_int 1104 wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel) 1105 { 1106 struct wsemul_vt100_emuldata *edp = cookie; 1107 struct wsemul_inputstate *instate; 1108 u_int processed = 0; 1109 #ifdef HAVE_JUMP_SCROLL 1110 int lines; 1111 #endif 1112 int rc = 0; 1113 1114 #ifdef DIAGNOSTIC 1115 if (kernel && !edp->console) 1116 panic("wsemul_vt100_output: kernel output, not console"); 1117 #endif 1118 1119 instate = kernel ? &edp->kstate : &edp->instate; 1120 1121 switch (edp->abortstate.state) { 1122 case ABORT_FAILED_CURSOR: 1123 /* 1124 * If we could not display the cursor back, we pretended not 1125 * having been able to display the last character. But this 1126 * is a lie, so compensate here. 1127 */ 1128 data++, count--; 1129 processed++; 1130 wsemul_reset_abortstate(&edp->abortstate); 1131 break; 1132 case ABORT_OK: 1133 /* remove cursor image if visible */ 1134 if (edp->flags & VTFL_CURSORON) { 1135 rc = (*edp->emulops->cursor) 1136 (edp->emulcookie, 0, edp->crow, 1137 edp->ccol << edp->dw); 1138 if (rc != 0) 1139 return 0; 1140 } 1141 break; 1142 default: 1143 break; 1144 } 1145 1146 for (;;) { 1147 #ifdef HAVE_JUMP_SCROLL 1148 switch (edp->abortstate.state) { 1149 case ABORT_FAILED_JUMP_SCROLL: 1150 /* 1151 * If we failed a previous jump scroll attempt, we 1152 * need to try to resume it with the same distance. 1153 * We can not recompute it since there might be more 1154 * bytes in the tty ring, causing a different result. 1155 */ 1156 lines = edp->abortstate.lines; 1157 break; 1158 case ABORT_OK: 1159 /* 1160 * If we are at the bottom of the scrolling area, count 1161 * newlines until an escape sequence appears. 1162 */ 1163 if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) && 1164 ROWS_BELOW == 0) 1165 lines = wsemul_vt100_jump_scroll(edp, data, 1166 count, kernel); 1167 else 1168 lines = 0; 1169 break; 1170 default: 1171 /* 1172 * If we are recovering a non-scrolling failure, 1173 * do not try to scroll yet. 1174 */ 1175 lines = 0; 1176 break; 1177 } 1178 1179 if (lines > 1) { 1180 wsemul_resume_abort(&edp->abortstate); 1181 rc = wsemul_vt100_scrollup(edp, lines); 1182 if (rc != 0) { 1183 wsemul_abort_jump_scroll(&edp->abortstate, 1184 lines); 1185 return processed; 1186 } 1187 wsemul_reset_abortstate(&edp->abortstate); 1188 edp->crow -= lines; 1189 } 1190 #endif 1191 1192 wsemul_resume_abort(&edp->abortstate); 1193 1194 if (wsemul_getchar(&data, &count, instate, 1195 #ifdef HAVE_UTF8_SUPPORT 1196 (edp->state == VT100_EMUL_STATE_NORMAL && !kernel) ? 1197 edp->flags & VTFL_UTF8 : 0 1198 #else 1199 0 1200 #endif 1201 ) != 0) 1202 break; 1203 1204 if (!(instate->inchar & ~0xff) && 1205 (instate->inchar & 0x7f) < 0x20) { 1206 rc = wsemul_vt100_output_c0c1(edp, instate, kernel); 1207 if (rc != 0) 1208 break; 1209 processed++; 1210 continue; 1211 } 1212 1213 if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) { 1214 rc = wsemul_vt100_output_normal(edp, instate, kernel); 1215 if (rc != 0) 1216 break; 1217 processed++; 1218 continue; 1219 } 1220 #ifdef DIAGNOSTIC 1221 if (edp->state > nitems(vt100_output)) 1222 panic("wsemul_vt100: invalid state %d", edp->state); 1223 #endif 1224 rc = vt100_output[edp->state - 1](edp, instate); 1225 if (rc != 0) 1226 break; 1227 processed++; 1228 } 1229 1230 if (rc != 0) 1231 wsemul_abort_other(&edp->abortstate); 1232 else { 1233 /* put cursor image back if visible */ 1234 if (edp->flags & VTFL_CURSORON) { 1235 rc = (*edp->emulops->cursor) 1236 (edp->emulcookie, 1, edp->crow, 1237 edp->ccol << edp->dw); 1238 if (rc != 0) { 1239 /* 1240 * Fail the last character output, remembering 1241 * that only the cursor operation really needs 1242 * to be done. 1243 */ 1244 wsemul_abort_cursor(&edp->abortstate); 1245 processed--; 1246 } 1247 } 1248 } 1249 1250 if (rc == 0) 1251 wsemul_reset_abortstate(&edp->abortstate); 1252 1253 return processed; 1254 } 1255 1256 #ifdef HAVE_JUMP_SCROLL 1257 int 1258 wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *edp, const u_char *data, 1259 u_int count, int kernel) 1260 { 1261 struct wsemul_inputstate tmpstate; 1262 u_int pos, lines; 1263 1264 lines = 0; 1265 pos = edp->ccol; 1266 tmpstate = kernel ? edp->kstate : edp->instate; /* structure copy */ 1267 1268 while (wsemul_getchar(&data, &count, &tmpstate, 1269 #ifdef HAVE_UTF8_SUPPORT 1270 kernel ? 0 : edp->flags & VTFL_UTF8 1271 #else 1272 0 1273 #endif 1274 ) == 0) { 1275 /* 1276 * Only char causing a transition from 1277 * VT100_EMUL_STATE_NORMAL to another state, for now. 1278 * Revisit this if this changes... 1279 */ 1280 if (tmpstate.inchar == ASCII_ESC) 1281 break; 1282 1283 if (ISSET(edp->flags, VTFL_DECAWM)) 1284 switch (tmpstate.inchar) { 1285 case ASCII_BS: 1286 if (pos > 0) 1287 pos--; 1288 break; 1289 case ASCII_CR: 1290 pos = 0; 1291 break; 1292 case ASCII_HT: 1293 if (edp->tabs) { 1294 pos++; 1295 while (pos < NCOLS - 1 && 1296 edp->tabs[pos] == 0) 1297 pos++; 1298 } else { 1299 pos = (pos + 7) & ~7; 1300 if (pos >= NCOLS) 1301 pos = NCOLS - 1; 1302 } 1303 break; 1304 default: 1305 if (!(tmpstate.inchar & ~0xff) && 1306 (tmpstate.inchar & 0x7f) < 0x20) 1307 break; 1308 if (pos++ >= NCOLS) { 1309 pos = 0; 1310 tmpstate.inchar = ASCII_LF; 1311 } 1312 break; 1313 } 1314 1315 if (tmpstate.inchar == ASCII_LF || 1316 tmpstate.inchar == ASCII_VT || 1317 tmpstate.inchar == ASCII_FF) { 1318 if (++lines >= edp->scrreg_nrows - 1) 1319 break; 1320 } 1321 } 1322 1323 return lines; 1324 } 1325 #endif 1326