1 /* $OpenBSD: wsemul_vt100.c,v 1.39 2020/05/25 09:55:49 jsg 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, uint32_t); 61 void *wsemul_vt100_attach(int, const struct wsscreen_descr *, 62 void *, int, int, void *, uint32_t); 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, uint32_t); 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 uint32_t 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, uint32_t 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->pack_attr)(cookie, 181 WS_KERNEL_FG, WS_KERNEL_BG, 182 WS_KERNEL_COLATTR | WSATTR_WSCOLORS, &edp->kernattr); 183 else 184 res = (*edp->emulops->pack_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, uint32_t 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, sizeof *edp); 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 case ASCII_LF: 476 case ASCII_VT: 477 case ASCII_FF: 478 rc = wsemul_vt100_nextline(edp); 479 break; 480 } 481 482 if (COLS_LEFT != 0) 483 edp->flags &= ~VTFL_LASTCHAR; 484 485 return rc; 486 } 487 488 int 489 wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, 490 struct wsemul_inputstate *instate) 491 { 492 u_int newstate = VT100_EMUL_STATE_NORMAL; 493 int rc = 0; 494 int i; 495 496 switch (instate->inchar) { 497 case '[': /* CSI */ 498 edp->nargs = 0; 499 memset(edp->args, 0, sizeof (edp->args)); 500 edp->modif1 = edp->modif2 = '\0'; 501 newstate = VT100_EMUL_STATE_CSI; 502 break; 503 case '7': /* DECSC */ 504 edp->flags |= VTFL_SAVEDCURS; 505 edp->savedcursor_row = edp->crow; 506 edp->savedcursor_col = edp->ccol; 507 edp->savedattr = edp->curattr; 508 edp->savedbkgdattr = edp->bkgdattr; 509 edp->savedattrflags = edp->attrflags; 510 edp->savedfgcol = edp->fgcol; 511 edp->savedbgcol = edp->bgcol; 512 for (i = 0; i < 4; i++) 513 edp->savedchartab_G[i] = edp->chartab_G[i]; 514 edp->savedchartab0 = edp->chartab0; 515 edp->savedchartab1 = edp->chartab1; 516 break; 517 case '8': /* DECRC */ 518 if ((edp->flags & VTFL_SAVEDCURS) == 0) 519 break; 520 edp->crow = edp->savedcursor_row; 521 edp->ccol = edp->savedcursor_col; 522 edp->curattr = edp->savedattr; 523 edp->bkgdattr = edp->savedbkgdattr; 524 edp->attrflags = edp->savedattrflags; 525 edp->fgcol = edp->savedfgcol; 526 edp->bgcol = edp->savedbgcol; 527 for (i = 0; i < 4; i++) 528 edp->chartab_G[i] = edp->savedchartab_G[i]; 529 edp->chartab0 = edp->savedchartab0; 530 edp->chartab1 = edp->savedchartab1; 531 break; 532 case '=': /* DECKPAM application mode */ 533 edp->flags |= VTFL_APPLKEYPAD; 534 break; 535 case '>': /* DECKPNM numeric mode */ 536 edp->flags &= ~VTFL_APPLKEYPAD; 537 break; 538 case 'E': /* NEL */ 539 edp->ccol = 0; 540 /* FALLTHROUGH */ 541 case 'D': /* IND */ 542 rc = wsemul_vt100_nextline(edp); 543 break; 544 case 'H': /* HTS */ 545 if (edp->tabs != NULL) 546 edp->tabs[edp->ccol] = 1; 547 break; 548 case '~': /* LS1R */ 549 edp->flags &= ~VTFL_UTF8; 550 edp->chartab1 = 1; 551 break; 552 case 'n': /* LS2 */ 553 edp->flags &= ~VTFL_UTF8; 554 edp->chartab0 = 2; 555 break; 556 case '}': /* LS2R */ 557 edp->flags &= ~VTFL_UTF8; 558 edp->chartab1 = 2; 559 break; 560 case 'o': /* LS3 */ 561 edp->flags &= ~VTFL_UTF8; 562 edp->chartab0 = 3; 563 break; 564 case '|': /* LS3R */ 565 edp->flags &= ~VTFL_UTF8; 566 edp->chartab1 = 3; 567 break; 568 case 'N': /* SS2 */ 569 edp->flags &= ~VTFL_UTF8; 570 edp->sschartab = 2; 571 break; 572 case 'O': /* SS3 */ 573 edp->flags &= ~VTFL_UTF8; 574 edp->sschartab = 3; 575 break; 576 case 'M': /* RI */ 577 if (ROWS_ABOVE > 0) { 578 edp->crow--; 579 CHECK_DW; 580 break; 581 } 582 rc = wsemul_vt100_scrolldown(edp, 1); 583 break; 584 case 'P': /* DCS */ 585 edp->nargs = 0; 586 memset(edp->args, 0, sizeof (edp->args)); 587 newstate = VT100_EMUL_STATE_DCS; 588 break; 589 case 'c': /* RIS */ 590 wsemul_vt100_reset(edp); 591 rc = wsemul_vt100_ed(edp, 2); 592 if (rc != 0) 593 break; 594 edp->ccol = edp->crow = 0; 595 break; 596 case '(': case ')': case '*': case '+': /* SCS */ 597 edp->designating = instate->inchar - '('; 598 newstate = VT100_EMUL_STATE_SCS94; 599 break; 600 case '-': case '.': case '/': /* SCS */ 601 edp->designating = instate->inchar - '-' + 1; 602 newstate = VT100_EMUL_STATE_SCS96; 603 break; 604 case '#': 605 newstate = VT100_EMUL_STATE_ESC_HASH; 606 break; 607 case ' ': /* 7/8 bit */ 608 newstate = VT100_EMUL_STATE_ESC_SPC; 609 break; 610 case ']': /* OSC operating system command */ 611 case '^': /* PM privacy message */ 612 case '_': /* APC application program command */ 613 /* ignored */ 614 newstate = VT100_EMUL_STATE_STRING; 615 break; 616 case '<': /* exit VT52 mode - ignored */ 617 break; 618 case '%': /* UTF-8 encoding sequences */ 619 newstate = VT100_EMUL_STATE_ESC_PERCENT; 620 break; 621 default: 622 #ifdef VT100_PRINTUNKNOWN 623 printf("ESC %x unknown\n", instate->inchar); 624 #endif 625 break; 626 } 627 628 if (COLS_LEFT != 0) 629 edp->flags &= ~VTFL_LASTCHAR; 630 631 if (rc != 0) 632 return rc; 633 634 edp->state = newstate; 635 return 0; 636 } 637 638 int 639 wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, 640 struct wsemul_inputstate *instate) 641 { 642 u_int newstate = VT100_EMUL_STATE_NORMAL; 643 644 switch (instate->inchar) { 645 case '%': /* probably DEC supplemental graphic */ 646 newstate = VT100_EMUL_STATE_SCS94_PERCENT; 647 break; 648 case 'A': /* british / national */ 649 edp->flags &= ~VTFL_UTF8; 650 edp->chartab_G[edp->designating] = edp->nrctab; 651 break; 652 case 'B': /* ASCII */ 653 edp->flags &= ~VTFL_UTF8; 654 edp->chartab_G[edp->designating] = 0; 655 break; 656 case '<': /* user preferred supplemental */ 657 /* XXX not really "user" preferred */ 658 edp->flags &= ~VTFL_UTF8; 659 edp->chartab_G[edp->designating] = edp->isolatin1tab; 660 break; 661 case '0': /* DEC special graphic */ 662 edp->flags &= ~VTFL_UTF8; 663 edp->chartab_G[edp->designating] = edp->decgraphtab; 664 break; 665 case '>': /* DEC tech */ 666 edp->flags &= ~VTFL_UTF8; 667 edp->chartab_G[edp->designating] = edp->dectechtab; 668 break; 669 default: 670 #ifdef VT100_PRINTUNKNOWN 671 printf("ESC%c %x unknown\n", edp->designating + '(', 672 instate->inchar); 673 #endif 674 break; 675 } 676 677 edp->state = newstate; 678 return 0; 679 } 680 681 int 682 wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, 683 struct wsemul_inputstate *instate) 684 { 685 switch (instate->inchar) { 686 case '5': /* DEC supplemental graphic */ 687 /* XXX there are differences */ 688 edp->flags &= ~VTFL_UTF8; 689 edp->chartab_G[edp->designating] = edp->isolatin1tab; 690 break; 691 default: 692 #ifdef VT100_PRINTUNKNOWN 693 printf("ESC%c%% %x unknown\n", edp->designating + '(', 694 instate->inchar); 695 #endif 696 break; 697 } 698 699 edp->state = VT100_EMUL_STATE_NORMAL; 700 return 0; 701 } 702 703 int 704 wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, 705 struct wsemul_inputstate *instate) 706 { 707 u_int newstate = VT100_EMUL_STATE_NORMAL; 708 int nrc; 709 710 switch (instate->inchar) { 711 case '%': /* probably portuguese */ 712 newstate = VT100_EMUL_STATE_SCS96_PERCENT; 713 break; 714 case 'A': /* ISO-latin-1 supplemental */ 715 edp->flags &= ~VTFL_UTF8; 716 edp->chartab_G[edp->designating] = edp->isolatin1tab; 717 break; 718 case '4': /* dutch */ 719 nrc = 1; 720 goto setnrc; 721 case '5': case 'C': /* finnish */ 722 nrc = 2; 723 goto setnrc; 724 case 'R': /* french */ 725 nrc = 3; 726 goto setnrc; 727 case 'Q': /* french canadian */ 728 nrc = 4; 729 goto setnrc; 730 case 'K': /* german */ 731 nrc = 5; 732 goto setnrc; 733 case 'Y': /* italian */ 734 nrc = 6; 735 goto setnrc; 736 case 'E': case '6': /* norwegian / danish */ 737 nrc = 7; 738 goto setnrc; 739 case 'Z': /* spanish */ 740 nrc = 9; 741 goto setnrc; 742 case '7': case 'H': /* swedish */ 743 nrc = 10; 744 goto setnrc; 745 case '=': /* swiss */ 746 nrc = 11; 747 setnrc: 748 if (vt100_setnrc(edp, nrc) == 0) /* what table ??? */ 749 break; 750 /* else FALLTHROUGH */ 751 default: 752 #ifdef VT100_PRINTUNKNOWN 753 printf("ESC%c %x unknown\n", edp->designating + '-' - 1, 754 instate->inchar); 755 #endif 756 break; 757 } 758 759 edp->state = newstate; 760 return 0; 761 } 762 763 int 764 wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, 765 struct wsemul_inputstate *instate) 766 { 767 switch (instate->inchar) { 768 case '6': /* portuguese */ 769 if (vt100_setnrc(edp, 8) == 0) 770 break; 771 /* else FALLTHROUGH */ 772 default: 773 #ifdef VT100_PRINTUNKNOWN 774 printf("ESC%c%% %x unknown\n", edp->designating + '-' - 1, 775 instate->inchar); 776 #endif 777 break; 778 } 779 780 edp->state = VT100_EMUL_STATE_NORMAL; 781 return 0; 782 } 783 784 int 785 wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, 786 struct wsemul_inputstate *instate) 787 { 788 switch (instate->inchar) { 789 case 'F': /* 7-bit controls */ 790 case 'G': /* 8-bit controls */ 791 #ifdef VT100_PRINTNOTIMPL 792 printf("ESC<SPC> %x ignored\n", instate->inchar); 793 #endif 794 break; 795 default: 796 #ifdef VT100_PRINTUNKNOWN 797 printf("ESC<SPC> %x unknown\n", instate->inchar); 798 #endif 799 break; 800 } 801 802 edp->state = VT100_EMUL_STATE_NORMAL; 803 return 0; 804 } 805 806 int 807 wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, 808 struct wsemul_inputstate *instate) 809 { 810 if (edp->dcstype && edp->dcspos < DCS_MAXLEN) { 811 if (instate->inchar & ~0xff) { 812 #ifdef VT100_PRINTUNKNOWN 813 printf("unknown char %x in DCS\n", instate->inchar); 814 #endif 815 } else 816 edp->dcsarg[edp->dcspos++] = (char)instate->inchar; 817 } 818 819 edp->state = VT100_EMUL_STATE_STRING; 820 return 0; 821 } 822 823 int 824 wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, 825 struct wsemul_inputstate *instate) 826 { 827 if (instate->inchar == '\\') { /* ST complete */ 828 wsemul_vt100_handle_dcs(edp); 829 edp->state = VT100_EMUL_STATE_NORMAL; 830 } else 831 edp->state = VT100_EMUL_STATE_STRING; 832 833 return 0; 834 } 835 836 int 837 wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, 838 struct wsemul_inputstate *instate) 839 { 840 u_int newstate = VT100_EMUL_STATE_DCS; 841 842 switch (instate->inchar) { 843 case '0': case '1': case '2': case '3': case '4': 844 case '5': case '6': case '7': case '8': case '9': 845 /* argument digit */ 846 if (edp->nargs > VT100_EMUL_NARGS - 1) 847 break; 848 edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) + 849 (instate->inchar - '0'); 850 break; 851 case ';': /* argument terminator */ 852 edp->nargs++; 853 break; 854 default: 855 edp->nargs++; 856 if (edp->nargs > VT100_EMUL_NARGS) { 857 #ifdef VT100_DEBUG 858 printf("vt100: too many arguments\n"); 859 #endif 860 edp->nargs = VT100_EMUL_NARGS; 861 } 862 newstate = VT100_EMUL_STATE_STRING; 863 switch (instate->inchar) { 864 case '$': 865 newstate = VT100_EMUL_STATE_DCS_DOLLAR; 866 break; 867 case '{': /* DECDLD soft charset */ /* } */ 868 case '!': /* DECRQUPSS user preferred supplemental set */ 869 /* 'u' must follow - need another state */ 870 case '|': /* DECUDK program F6..F20 */ 871 #ifdef VT100_PRINTNOTIMPL 872 printf("DCS%c ignored\n", (char)instate->inchar); 873 #endif 874 break; 875 default: 876 #ifdef VT100_PRINTUNKNOWN 877 printf("DCS %x (%d, %d) unknown\n", instate->inchar, 878 ARG(0), ARG(1)); 879 #endif 880 break; 881 } 882 } 883 884 edp->state = newstate; 885 return 0; 886 } 887 888 int 889 wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, 890 struct wsemul_inputstate *instate) 891 { 892 switch (instate->inchar) { 893 case 'p': /* DECRSTS terminal state restore */ 894 case 'q': /* DECRQSS control function request */ 895 #ifdef VT100_PRINTNOTIMPL 896 printf("DCS$%c ignored\n", (char)instate->inchar); 897 #endif 898 break; 899 case 't': /* DECRSPS restore presentation state */ 900 switch (ARG(0)) { 901 case 0: /* error */ 902 break; 903 case 1: /* cursor information restore */ 904 #ifdef VT100_PRINTNOTIMPL 905 printf("DCS1$t ignored\n"); 906 #endif 907 break; 908 case 2: /* tab stop restore */ 909 edp->dcspos = 0; 910 edp->dcstype = DCSTYPE_TABRESTORE; 911 break; 912 default: 913 #ifdef VT100_PRINTUNKNOWN 914 printf("DCS%d$t unknown\n", ARG(0)); 915 #endif 916 break; 917 } 918 break; 919 default: 920 #ifdef VT100_PRINTUNKNOWN 921 printf("DCS$ %x (%d, %d) unknown\n", 922 instate->inchar, ARG(0), ARG(1)); 923 #endif 924 break; 925 } 926 927 edp->state = VT100_EMUL_STATE_STRING; 928 return 0; 929 } 930 931 int 932 wsemul_vt100_output_esc_percent(struct wsemul_vt100_emuldata *edp, 933 struct wsemul_inputstate *instate) 934 { 935 switch (instate->inchar) { 936 #ifdef HAVE_UTF8_SUPPORT 937 case 'G': 938 edp->flags |= VTFL_UTF8; 939 edp->kstate.mbleft = edp->instate.mbleft = 0; 940 break; 941 case '@': 942 edp->flags &= ~VTFL_UTF8; 943 break; 944 #endif 945 default: 946 #ifdef VT100_PRINTUNKNOWN 947 printf("ESC% %x unknown\n", instate->inchar); 948 #endif 949 break; 950 } 951 edp->state = VT100_EMUL_STATE_NORMAL; 952 return 0; 953 } 954 955 int 956 wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, 957 struct wsemul_inputstate *instate) 958 { 959 int i; 960 int rc = 0; 961 962 switch (instate->inchar) { 963 case '5': /* DECSWL single width, single height */ 964 if (edp->dblwid != NULL && edp->dw != 0) { 965 for (i = 0; i < edp->ncols / 2; i++) { 966 WSEMULOP(rc, edp, &edp->abortstate, copycols, 967 (edp->emulcookie, edp->crow, 2 * i, i, 1)); 968 if (rc != 0) 969 return rc; 970 } 971 WSEMULOP(rc, edp, &edp->abortstate, erasecols, 972 (edp->emulcookie, edp->crow, i, edp->ncols - i, 973 edp->bkgdattr)); 974 if (rc != 0) 975 return rc; 976 edp->dblwid[edp->crow] = 0; 977 edp->dw = 0; 978 } 979 break; 980 case '6': /* DECDWL double width, single height */ 981 case '3': /* DECDHL double width, double height, top half */ 982 case '4': /* DECDHL double width, double height, bottom half */ 983 if (edp->dblwid != NULL && edp->dw == 0) { 984 for (i = edp->ncols / 2 - 1; i >= 0; i--) { 985 WSEMULOP(rc, edp, &edp->abortstate, copycols, 986 (edp->emulcookie, edp->crow, i, 2 * i, 1)); 987 if (rc != 0) 988 return rc; 989 } 990 for (i = 0; i < edp->ncols / 2; i++) { 991 WSEMULOP(rc, edp, &edp->abortstate, erasecols, 992 (edp->emulcookie, edp->crow, 2 * i + 1, 1, 993 edp->bkgdattr)); 994 if (rc != 0) 995 return rc; 996 } 997 edp->dblwid[edp->crow] = 1; 998 edp->dw = 1; 999 if (edp->ccol > (edp->ncols >> 1) - 1) 1000 edp->ccol = (edp->ncols >> 1) - 1; 1001 } 1002 break; 1003 case '8': { /* DECALN */ 1004 int i, j; 1005 for (i = 0; i < edp->nrows; i++) 1006 for (j = 0; j < edp->ncols; j++) { 1007 WSEMULOP(rc, edp, &edp->abortstate, putchar, 1008 (edp->emulcookie, i, j, 'E', edp->curattr)); 1009 if (rc != 0) 1010 return rc; 1011 } 1012 } 1013 edp->ccol = 0; 1014 edp->crow = 0; 1015 break; 1016 default: 1017 #ifdef VT100_PRINTUNKNOWN 1018 printf("ESC# %x unknown\n", instate->inchar); 1019 #endif 1020 break; 1021 } 1022 1023 if (COLS_LEFT != 0) 1024 edp->flags &= ~VTFL_LASTCHAR; 1025 1026 edp->state = VT100_EMUL_STATE_NORMAL; 1027 return 0; 1028 } 1029 1030 int 1031 wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, 1032 struct wsemul_inputstate *instate) 1033 { 1034 u_int newstate = VT100_EMUL_STATE_CSI; 1035 int oargs; 1036 int rc = 0; 1037 1038 switch (instate->inchar) { 1039 case '0': case '1': case '2': case '3': case '4': 1040 case '5': case '6': case '7': case '8': case '9': 1041 /* argument digit */ 1042 if (edp->nargs > VT100_EMUL_NARGS - 1) 1043 break; 1044 edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) + 1045 (instate->inchar - '0'); 1046 break; 1047 case ';': /* argument terminator */ 1048 edp->nargs++; 1049 break; 1050 case '?': /* DEC specific */ 1051 case '>': /* DA query */ 1052 edp->modif1 = (char)instate->inchar; 1053 break; 1054 case '!': 1055 case '"': 1056 case '$': 1057 case '&': 1058 edp->modif2 = (char)instate->inchar; 1059 break; 1060 default: /* end of escape sequence */ 1061 oargs = edp->nargs++; 1062 if (edp->nargs > VT100_EMUL_NARGS) { 1063 #ifdef VT100_DEBUG 1064 printf("vt100: too many arguments\n"); 1065 #endif 1066 edp->nargs = VT100_EMUL_NARGS; 1067 } 1068 rc = wsemul_vt100_handle_csi(edp, instate); 1069 if (rc != 0) { 1070 edp->nargs = oargs; 1071 return rc; 1072 } 1073 newstate = VT100_EMUL_STATE_NORMAL; 1074 break; 1075 } 1076 1077 if (COLS_LEFT != 0) 1078 edp->flags &= ~VTFL_LASTCHAR; 1079 1080 edp->state = newstate; 1081 return 0; 1082 } 1083 1084 u_int 1085 wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel) 1086 { 1087 struct wsemul_vt100_emuldata *edp = cookie; 1088 struct wsemul_inputstate *instate; 1089 u_int processed = 0; 1090 #ifdef HAVE_JUMP_SCROLL 1091 int lines; 1092 #endif 1093 int rc = 0; 1094 1095 #ifdef DIAGNOSTIC 1096 if (kernel && !edp->console) 1097 panic("wsemul_vt100_output: kernel output, not console"); 1098 #endif 1099 1100 instate = kernel ? &edp->kstate : &edp->instate; 1101 1102 switch (edp->abortstate.state) { 1103 case ABORT_FAILED_CURSOR: 1104 /* 1105 * If we could not display the cursor back, we pretended not 1106 * having been able to display the last character. But this 1107 * is a lie, so compensate here. 1108 */ 1109 data++, count--; 1110 processed++; 1111 wsemul_reset_abortstate(&edp->abortstate); 1112 break; 1113 case ABORT_OK: 1114 /* remove cursor image if visible */ 1115 if (edp->flags & VTFL_CURSORON) { 1116 rc = (*edp->emulops->cursor) 1117 (edp->emulcookie, 0, edp->crow, 1118 edp->ccol << edp->dw); 1119 if (rc != 0) 1120 return 0; 1121 } 1122 break; 1123 default: 1124 break; 1125 } 1126 1127 for (;;) { 1128 #ifdef HAVE_JUMP_SCROLL 1129 switch (edp->abortstate.state) { 1130 case ABORT_FAILED_JUMP_SCROLL: 1131 /* 1132 * If we failed a previous jump scroll attempt, we 1133 * need to try to resume it with the same distance. 1134 * We can not recompute it since there might be more 1135 * bytes in the tty ring, causing a different result. 1136 */ 1137 lines = edp->abortstate.lines; 1138 break; 1139 case ABORT_OK: 1140 /* 1141 * If we are at the bottom of the scrolling area, count 1142 * newlines until an escape sequence appears. 1143 */ 1144 if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) && 1145 ROWS_BELOW == 0) 1146 lines = wsemul_vt100_jump_scroll(edp, data, 1147 count, kernel); 1148 else 1149 lines = 0; 1150 break; 1151 default: 1152 /* 1153 * If we are recovering a non-scrolling failure, 1154 * do not try to scroll yet. 1155 */ 1156 lines = 0; 1157 break; 1158 } 1159 1160 if (lines > 1) { 1161 wsemul_resume_abort(&edp->abortstate); 1162 rc = wsemul_vt100_scrollup(edp, lines); 1163 if (rc != 0) { 1164 wsemul_abort_jump_scroll(&edp->abortstate, 1165 lines); 1166 return processed; 1167 } 1168 wsemul_reset_abortstate(&edp->abortstate); 1169 edp->crow -= lines; 1170 } 1171 #endif 1172 1173 wsemul_resume_abort(&edp->abortstate); 1174 1175 if (wsemul_getchar(&data, &count, instate, 1176 #ifdef HAVE_UTF8_SUPPORT 1177 (edp->state == VT100_EMUL_STATE_NORMAL && !kernel) ? 1178 edp->flags & VTFL_UTF8 : 0 1179 #else 1180 0 1181 #endif 1182 ) != 0) 1183 break; 1184 1185 if (!(instate->inchar & ~0xff) && 1186 (instate->inchar & 0x7f) < 0x20) { 1187 rc = wsemul_vt100_output_c0c1(edp, instate, kernel); 1188 if (rc != 0) 1189 break; 1190 processed++; 1191 continue; 1192 } 1193 1194 if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) { 1195 rc = wsemul_vt100_output_normal(edp, instate, kernel); 1196 if (rc != 0) 1197 break; 1198 processed++; 1199 continue; 1200 } 1201 #ifdef DIAGNOSTIC 1202 if (edp->state > nitems(vt100_output)) 1203 panic("wsemul_vt100: invalid state %d", edp->state); 1204 #endif 1205 rc = vt100_output[edp->state - 1](edp, instate); 1206 if (rc != 0) 1207 break; 1208 processed++; 1209 } 1210 1211 if (rc != 0) 1212 wsemul_abort_other(&edp->abortstate); 1213 else { 1214 /* put cursor image back if visible */ 1215 if (edp->flags & VTFL_CURSORON) { 1216 rc = (*edp->emulops->cursor) 1217 (edp->emulcookie, 1, edp->crow, 1218 edp->ccol << edp->dw); 1219 if (rc != 0) { 1220 /* 1221 * Fail the last character output, remembering 1222 * that only the cursor operation really needs 1223 * to be done. 1224 */ 1225 wsemul_abort_cursor(&edp->abortstate); 1226 processed--; 1227 } 1228 } 1229 } 1230 1231 if (rc == 0) 1232 wsemul_reset_abortstate(&edp->abortstate); 1233 1234 return processed; 1235 } 1236 1237 #ifdef HAVE_JUMP_SCROLL 1238 int 1239 wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *edp, const u_char *data, 1240 u_int count, int kernel) 1241 { 1242 struct wsemul_inputstate tmpstate; 1243 u_int pos, lines; 1244 1245 lines = 0; 1246 pos = edp->ccol; 1247 tmpstate = kernel ? edp->kstate : edp->instate; /* structure copy */ 1248 1249 while (wsemul_getchar(&data, &count, &tmpstate, 1250 #ifdef HAVE_UTF8_SUPPORT 1251 kernel ? 0 : edp->flags & VTFL_UTF8 1252 #else 1253 0 1254 #endif 1255 ) == 0) { 1256 /* 1257 * Only char causing a transition from 1258 * VT100_EMUL_STATE_NORMAL to another state, for now. 1259 * Revisit this if this changes... 1260 */ 1261 if (tmpstate.inchar == ASCII_ESC) 1262 break; 1263 1264 if (ISSET(edp->flags, VTFL_DECAWM)) 1265 switch (tmpstate.inchar) { 1266 case ASCII_BS: 1267 if (pos > 0) 1268 pos--; 1269 break; 1270 case ASCII_CR: 1271 pos = 0; 1272 break; 1273 case ASCII_HT: 1274 if (edp->tabs) { 1275 pos++; 1276 while (pos < NCOLS - 1 && 1277 edp->tabs[pos] == 0) 1278 pos++; 1279 } else { 1280 pos = (pos + 7) & ~7; 1281 if (pos >= NCOLS) 1282 pos = NCOLS - 1; 1283 } 1284 break; 1285 default: 1286 if (!(tmpstate.inchar & ~0xff) && 1287 (tmpstate.inchar & 0x7f) < 0x20) 1288 break; 1289 if (pos++ >= NCOLS) { 1290 pos = 0; 1291 tmpstate.inchar = ASCII_LF; 1292 } 1293 break; 1294 } 1295 1296 if (tmpstate.inchar == ASCII_LF || 1297 tmpstate.inchar == ASCII_VT || 1298 tmpstate.inchar == ASCII_FF) { 1299 if (++lines >= edp->scrreg_nrows - 1) 1300 break; 1301 } 1302 } 1303 1304 return lines; 1305 } 1306 #endif 1307