1 /* 2 * Copyright 2001-2010, Haiku, Inc. 3 * Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net> 4 * Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 5 * Distributed under the terms of the MIT license. 6 */ 7 8 9 //! Escape sequence parse and character encoding. 10 11 12 #include "TermParse.h" 13 14 #include <ctype.h> 15 #include <errno.h> 16 #include <stdio.h> 17 #include <signal.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include <Autolock.h> 22 #include <Beep.h> 23 #include <Catalog.h> 24 #include <Locale.h> 25 #include <Message.h> 26 #include <UTF8.h> 27 28 #include "TermConst.h" 29 #include "TerminalBuffer.h" 30 #include "VTparse.h" 31 32 33 extern int gUTF8GroundTable[]; /* UTF8 Ground table */ 34 extern int gCS96GroundTable[]; /* CS96 Ground table */ 35 extern int gISO8859GroundTable[]; /* ISO8859 & EUC Ground table */ 36 extern int gWinCPGroundTable[]; /* Windows cp1252, cp1251, koi-8r */ 37 extern int gSJISGroundTable[]; /* Shift-JIS Ground table */ 38 39 extern int gEscTable[]; /* ESC */ 40 extern int gCsiTable[]; /* ESC [ */ 41 extern int gDecTable[]; /* ESC [ ? */ 42 extern int gScrTable[]; /* ESC # */ 43 extern int gIgnoreTable[]; /* ignore table */ 44 extern int gIesTable[]; /* ignore ESC table */ 45 extern int gEscIgnoreTable[]; /* ESC ignore table */ 46 extern int gMbcsTable[]; /* ESC $ */ 47 48 extern int gLineDrawTable[]; /* ESC ( 0 */ 49 50 51 #define DEFAULT -1 52 #define NPARAM 10 // Max parameters 53 54 55 //! Get char from pty reader buffer. 56 inline uchar 57 TermParse::_NextParseChar() 58 { 59 if (fParserBufferOffset >= fParserBufferSize) { 60 // parser buffer empty 61 status_t error = _ReadParserBuffer(); 62 if (error != B_OK) 63 throw error; 64 } 65 66 return fParserBuffer[fParserBufferOffset++]; 67 } 68 69 70 TermParse::TermParse(int fd) 71 : 72 fFd(fd), 73 fAttr(FORECOLORED(7)), 74 fSavedAttr(FORECOLORED(7)), 75 fParseThread(-1), 76 fReaderThread(-1), 77 fReaderSem(-1), 78 fReaderLocker(-1), 79 fBufferPosition(0), 80 fReadBufferSize(0), 81 fParserBufferSize(0), 82 fParserBufferOffset(0), 83 fBuffer(NULL), 84 fQuitting(true) 85 { 86 memset(fReadBuffer, 0, READ_BUF_SIZE); 87 memset(fParserBuffer, 0, ESC_PARSER_BUFFER_SIZE); 88 } 89 90 91 TermParse::~TermParse() 92 { 93 StopThreads(); 94 } 95 96 97 status_t 98 TermParse::StartThreads(TerminalBuffer *buffer) 99 { 100 if (fBuffer != NULL) 101 return B_ERROR; 102 103 fQuitting = false; 104 fBuffer = buffer; 105 106 status_t status = _InitPtyReader(); 107 if (status < B_OK) { 108 fBuffer = NULL; 109 return status; 110 } 111 112 status = _InitTermParse(); 113 if (status < B_OK) { 114 _StopPtyReader(); 115 fBuffer = NULL; 116 return status; 117 } 118 119 return B_OK; 120 } 121 122 123 status_t 124 TermParse::StopThreads() 125 { 126 if (fBuffer == NULL) 127 return B_ERROR; 128 129 fQuitting = true; 130 131 _StopPtyReader(); 132 _StopTermParse(); 133 134 fBuffer = NULL; 135 136 return B_OK; 137 } 138 139 140 //! Initialize and spawn EscParse thread. 141 status_t 142 TermParse::_InitTermParse() 143 { 144 if (fParseThread >= 0) 145 return B_ERROR; // we might want to return B_OK instead ? 146 147 fParseThread = spawn_thread(_escparse_thread, "EscParse", 148 B_DISPLAY_PRIORITY, this); 149 150 if (fParseThread < 0) 151 return fParseThread; 152 153 resume_thread(fParseThread); 154 155 return B_OK; 156 } 157 158 159 //! Initialize and spawn PtyReader thread. 160 status_t 161 TermParse::_InitPtyReader() 162 { 163 if (fReaderThread >= 0) 164 return B_ERROR; // same as above 165 166 fReaderSem = create_sem(0, "pty_reader_sem"); 167 if (fReaderSem < 0) 168 return fReaderSem; 169 170 fReaderLocker = create_sem(0, "pty_locker_sem"); 171 if (fReaderLocker < 0) { 172 delete_sem(fReaderSem); 173 fReaderSem = -1; 174 return fReaderLocker; 175 } 176 177 fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader", 178 B_NORMAL_PRIORITY, this); 179 if (fReaderThread < 0) { 180 delete_sem(fReaderSem); 181 fReaderSem = -1; 182 delete_sem(fReaderLocker); 183 fReaderLocker = -1; 184 return fReaderThread; 185 } 186 187 resume_thread(fReaderThread); 188 189 return B_OK; 190 } 191 192 193 void 194 TermParse::_StopTermParse() 195 { 196 if (fParseThread >= 0) { 197 status_t dummy; 198 wait_for_thread(fParseThread, &dummy); 199 fParseThread = -1; 200 } 201 } 202 203 204 void 205 TermParse::_StopPtyReader() 206 { 207 if (fReaderSem >= 0) { 208 delete_sem(fReaderSem); 209 fReaderSem = -1; 210 } 211 if (fReaderLocker >= 0) { 212 delete_sem(fReaderLocker); 213 fReaderLocker = -1; 214 } 215 216 if (fReaderThread >= 0) { 217 suspend_thread(fReaderThread); 218 219 status_t status; 220 wait_for_thread(fReaderThread, &status); 221 222 fReaderThread = -1; 223 } 224 } 225 226 227 //! Get data from pty device. 228 int32 229 TermParse::PtyReader() 230 { 231 int32 bufferSize = 0; 232 int32 readPos = 0; 233 while (!fQuitting) { 234 // If Pty Buffer nearly full, snooze this thread, and continue. 235 while (READ_BUF_SIZE - bufferSize < MIN_PTY_BUFFER_SPACE) { 236 status_t status; 237 do { 238 status = acquire_sem(fReaderLocker); 239 } while (status == B_INTERRUPTED); 240 if (status < B_OK) 241 return status; 242 243 bufferSize = fReadBufferSize; 244 } 245 246 // Read PTY 247 uchar buf[READ_BUF_SIZE]; 248 ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize); 249 if (nread <= 0) { 250 fBuffer->NotifyQuit(errno); 251 return B_OK; 252 } 253 254 // Copy read string to PtyBuffer. 255 256 int32 left = READ_BUF_SIZE - readPos; 257 258 if (nread >= left) { 259 memcpy(fReadBuffer + readPos, buf, left); 260 memcpy(fReadBuffer, buf + left, nread - left); 261 } else 262 memcpy(fReadBuffer + readPos, buf, nread); 263 264 bufferSize = atomic_add(&fReadBufferSize, nread); 265 if (bufferSize == 0) 266 release_sem(fReaderSem); 267 268 bufferSize += nread; 269 readPos = (readPos + nread) % READ_BUF_SIZE; 270 } 271 272 return B_OK; 273 } 274 275 276 void 277 TermParse::DumpState(int *groundtable, int *parsestate, uchar c) 278 { 279 static const struct { 280 int *p; 281 const char *name; 282 } tables[] = { 283 #define T(t) \ 284 { t, #t } 285 T(gUTF8GroundTable), 286 T(gCS96GroundTable), 287 T(gISO8859GroundTable), 288 T(gWinCPGroundTable), 289 T(gSJISGroundTable), 290 T(gEscTable), 291 T(gCsiTable), 292 T(gDecTable), 293 T(gScrTable), 294 T(gIgnoreTable), 295 T(gIesTable), 296 T(gEscIgnoreTable), 297 T(gMbcsTable), 298 { NULL, NULL } 299 }; 300 int i; 301 fprintf(stderr, "groundtable: "); 302 for (i = 0; tables[i].p; i++) { 303 if (tables[i].p == groundtable) 304 fprintf(stderr, "%s\t", tables[i].name); 305 } 306 fprintf(stderr, "parsestate: "); 307 for (i = 0; tables[i].p; i++) { 308 if (tables[i].p == parsestate) 309 fprintf(stderr, "%s\t", tables[i].name); 310 } 311 fprintf(stderr, "char: 0x%02x (%d)\n", c, c); 312 } 313 314 315 int * 316 TermParse::_GuessGroundTable(int encoding) 317 { 318 switch (encoding) { 319 case B_ISO1_CONVERSION: 320 case B_ISO2_CONVERSION: 321 case B_ISO3_CONVERSION: 322 case B_ISO4_CONVERSION: 323 case B_ISO5_CONVERSION: 324 case B_ISO6_CONVERSION: 325 case B_ISO7_CONVERSION: 326 case B_ISO8_CONVERSION: 327 case B_ISO9_CONVERSION: 328 case B_ISO10_CONVERSION: 329 case B_ISO13_CONVERSION: 330 case B_ISO14_CONVERSION: 331 case B_ISO15_CONVERSION: 332 case B_EUC_CONVERSION: 333 case B_EUC_KR_CONVERSION: 334 case B_JIS_CONVERSION: 335 case B_GBK_CONVERSION: 336 case B_BIG5_CONVERSION: 337 return gISO8859GroundTable; 338 339 case B_KOI8R_CONVERSION: 340 case B_MS_WINDOWS_1251_CONVERSION: 341 case B_MS_WINDOWS_CONVERSION: 342 case B_MAC_ROMAN_CONVERSION: 343 case B_MS_DOS_866_CONVERSION: 344 case B_MS_DOS_CONVERSION: 345 return gWinCPGroundTable; 346 347 case B_SJIS_CONVERSION: 348 return gSJISGroundTable; 349 350 case M_UTF8: 351 default: 352 break; 353 } 354 355 return gUTF8GroundTable; 356 } 357 358 359 int32 360 TermParse::EscParse() 361 { 362 int top; 363 int bottom; 364 // int cs96 = 0; 365 uchar curess = 0; 366 367 char cbuf[4] = { 0 }; 368 char dstbuf[4] = { 0 }; 369 char *ptr; 370 371 int currentEncoding = -1; 372 373 int param[NPARAM]; 374 int nparam = 1; 375 376 int row; 377 int column; 378 379 /* default encoding system is UTF8 */ 380 int *groundtable = gUTF8GroundTable; 381 int *parsestate = gUTF8GroundTable; 382 383 /* Handle switch between G0 and G1 character sets */ 384 int *alternateParseTable = gUTF8GroundTable; 385 bool shifted_in = false; 386 387 int32 srcLen = sizeof(cbuf); 388 int32 dstLen = sizeof(dstbuf); 389 int32 dummyState = 0; 390 391 int width = 1; 392 BAutolock locker(fBuffer); 393 394 fAttr = fSavedAttr = FORECOLORED(7); 395 396 while (!fQuitting) { 397 try { 398 uchar c = _NextParseChar(); 399 400 //DumpState(groundtable, parsestate, c); 401 402 if (currentEncoding != fBuffer->Encoding()) { 403 // Change coding, change parse table. 404 groundtable = _GuessGroundTable(fBuffer->Encoding()); 405 parsestate = groundtable; 406 currentEncoding = fBuffer->Encoding(); 407 } 408 409 //debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]); 410 switch (parsestate[c]) { 411 case CASE_PRINT: 412 fBuffer->InsertChar((char)c, fAttr); 413 break; 414 415 case CASE_PRINT_GR: 416 /* case iso8859 gr character, or euc */ 417 ptr = cbuf; 418 if (currentEncoding == B_EUC_CONVERSION 419 || currentEncoding == B_EUC_KR_CONVERSION 420 || currentEncoding == B_JIS_CONVERSION 421 || currentEncoding == B_GBK_CONVERSION 422 || currentEncoding == B_BIG5_CONVERSION) { 423 switch (parsestate[curess]) { 424 case CASE_SS2: /* JIS X 0201 */ 425 width = 1; 426 *ptr++ = curess; 427 *ptr++ = c; 428 *ptr = 0; 429 curess = 0; 430 break; 431 432 case CASE_SS3: /* JIS X 0212 */ 433 width = 1; 434 *ptr++ = curess; 435 *ptr++ = c; 436 c = _NextParseChar(); 437 *ptr++ = c; 438 *ptr = 0; 439 curess = 0; 440 break; 441 442 default: /* JIS X 0208 */ 443 width = 2; 444 *ptr++ = c; 445 c = _NextParseChar(); 446 *ptr++ = c; 447 *ptr = 0; 448 break; 449 } 450 } else { 451 /* ISO-8859-1...10 and MacRoman */ 452 *ptr++ = c; 453 *ptr = 0; 454 } 455 456 srcLen = strlen(cbuf); 457 dstLen = sizeof(dstbuf); 458 if (currentEncoding != B_JIS_CONVERSION) { 459 convert_to_utf8(currentEncoding, cbuf, &srcLen, 460 dstbuf, &dstLen, &dummyState, '?'); 461 } else { 462 convert_to_utf8(B_EUC_CONVERSION, cbuf, &srcLen, 463 dstbuf, &dstLen, &dummyState, '?'); 464 } 465 466 fBuffer->InsertChar(dstbuf, dstLen, width, fAttr); 467 break; 468 469 case CASE_PRINT_CS96: 470 cbuf[0] = c | 0x80; 471 c = _NextParseChar(); 472 cbuf[1] = c | 0x80; 473 cbuf[2] = 0; 474 srcLen = 2; 475 dstLen = sizeof(dstbuf); 476 convert_to_utf8(B_EUC_CONVERSION, cbuf, &srcLen, 477 dstbuf, &dstLen, &dummyState, '?'); 478 fBuffer->InsertChar(dstbuf, dstLen, fAttr); 479 break; 480 481 case CASE_PRINT_GRA: 482 /* "Special characters and line drawing" enabled by \E(0 */ 483 switch (c) { 484 case 'a': 485 fBuffer->InsertChar("\xE2\x96\x92",3,fAttr); 486 break; 487 case 'j': 488 fBuffer->InsertChar("\xE2\x94\x98",3,fAttr); 489 break; 490 case 'k': 491 fBuffer->InsertChar("\xE2\x94\x90",3,fAttr); 492 break; 493 case 'l': 494 fBuffer->InsertChar("\xE2\x94\x8C",3,fAttr); 495 break; 496 case 'm': 497 fBuffer->InsertChar("\xE2\x94\x94",3,fAttr); 498 break; 499 case 'n': 500 fBuffer->InsertChar("\xE2\x94\xBC",3,fAttr); 501 break; 502 case 'q': 503 fBuffer->InsertChar("\xE2\x94\x80",3,fAttr); 504 break; 505 case 't': 506 fBuffer->InsertChar("\xE2\x94\x9C",3,fAttr); 507 break; 508 case 'u': 509 fBuffer->InsertChar("\xE2\x94\xA4",3,fAttr); 510 break; 511 case 'v': 512 fBuffer->InsertChar("\xE2\x94\xB4",3,fAttr); 513 break; 514 case 'w': 515 fBuffer->InsertChar("\xE2\x94\xAC",3,fAttr); 516 break; 517 case 'x': 518 fBuffer->InsertChar("\xE2\x94\x82",3,fAttr); 519 break; 520 default: 521 fBuffer->InsertChar((char)c, fAttr); 522 } 523 break; 524 525 case CASE_LF: 526 fBuffer->InsertLF(); 527 break; 528 529 case CASE_CR: 530 fBuffer->InsertCR(fAttr); 531 break; 532 533 case CASE_SJIS_KANA: 534 cbuf[0] = c; 535 cbuf[1] = '\0'; 536 srcLen = 1; 537 dstLen = sizeof(dstbuf); 538 convert_to_utf8(currentEncoding, cbuf, &srcLen, 539 dstbuf, &dstLen, &dummyState, '?'); 540 fBuffer->InsertChar(dstbuf, dstLen, fAttr); 541 break; 542 543 case CASE_SJIS_INSTRING: 544 cbuf[0] = c; 545 c = _NextParseChar(); 546 cbuf[1] = c; 547 cbuf[2] = '\0'; 548 srcLen = 2; 549 dstLen = sizeof(dstbuf); 550 convert_to_utf8(currentEncoding, cbuf, &srcLen, 551 dstbuf, &dstLen, &dummyState, '?'); 552 fBuffer->InsertChar(dstbuf, dstLen, fAttr); 553 break; 554 555 case CASE_UTF8_2BYTE: 556 cbuf[0] = c; 557 c = _NextParseChar(); 558 if (groundtable[c] != CASE_UTF8_INSTRING) 559 break; 560 cbuf[1] = c; 561 cbuf[2] = '\0'; 562 563 fBuffer->InsertChar(cbuf, 2, fAttr); 564 break; 565 566 case CASE_UTF8_3BYTE: 567 cbuf[0] = c; 568 c = _NextParseChar(); 569 if (groundtable[c] != CASE_UTF8_INSTRING) 570 break; 571 cbuf[1] = c; 572 573 c = _NextParseChar(); 574 if (groundtable[c] != CASE_UTF8_INSTRING) 575 break; 576 cbuf[2] = c; 577 cbuf[3] = '\0'; 578 fBuffer->InsertChar(cbuf, 3, fAttr); 579 break; 580 581 case CASE_MBCS: 582 /* ESC $ */ 583 parsestate = gMbcsTable; 584 break; 585 586 case CASE_GSETS: 587 /* ESC $ ? */ 588 parsestate = gCS96GroundTable; 589 // cs96 = 1; 590 break; 591 592 case CASE_SCS_STATE: 593 { 594 char page = _NextParseChar(); 595 596 int* newTable = _GuessGroundTable(currentEncoding); 597 if (page == '0') 598 newTable = gLineDrawTable; 599 600 if (c == '(') { 601 if (shifted_in) 602 alternateParseTable = newTable; 603 else 604 groundtable = newTable; 605 } else if (c == ')') { 606 if (!shifted_in) 607 alternateParseTable = newTable; 608 else 609 groundtable = newTable; 610 } 611 612 parsestate = groundtable; 613 614 break; 615 } 616 617 case CASE_GROUND_STATE: 618 /* exit ignore mode */ 619 parsestate = groundtable; 620 break; 621 622 case CASE_BELL: 623 beep(); 624 break; 625 626 case CASE_BS: 627 fBuffer->MoveCursorLeft(1); 628 break; 629 630 case CASE_TAB: 631 fBuffer->InsertTab(fAttr); 632 break; 633 634 case CASE_ESC: 635 /* escape */ 636 parsestate = gEscTable; 637 break; 638 639 case CASE_IGNORE_STATE: 640 /* Ies: ignore anything else */ 641 parsestate = gIgnoreTable; 642 break; 643 644 case CASE_IGNORE_ESC: 645 /* Ign: escape */ 646 parsestate = gIesTable; 647 break; 648 649 case CASE_IGNORE: 650 /* Ignore character */ 651 break; 652 653 case CASE_SI: 654 /* shift in (to G1 charset) */ 655 if (shifted_in == false) { 656 int* tmp = alternateParseTable; 657 alternateParseTable = parsestate; 658 parsestate = tmp; 659 } 660 break; 661 662 case CASE_SO: 663 /* shift out (to G0 charset) */ 664 if (shifted_in == true) { 665 int* tmp = alternateParseTable; 666 alternateParseTable = parsestate; 667 parsestate = tmp; 668 } 669 break; 670 671 case CASE_SCR_STATE: // ESC # 672 /* enter scr state */ 673 parsestate = gScrTable; 674 break; 675 676 case CASE_ESC_IGNORE: 677 /* unknown escape sequence */ 678 parsestate = gEscIgnoreTable; 679 break; 680 681 case CASE_ESC_DIGIT: // ESC [ number 682 /* digit in csi or dec mode */ 683 if ((row = param[nparam - 1]) == DEFAULT) 684 row = 0; 685 param[nparam - 1] = 10 * row + (c - '0'); 686 break; 687 688 case CASE_ESC_SEMI: // ESC ; 689 /* semicolon in csi or dec mode */ 690 if (nparam < NPARAM) 691 param[nparam++] = DEFAULT; 692 break; 693 694 case CASE_DEC_STATE: 695 /* enter dec mode */ 696 parsestate = gDecTable; 697 break; 698 699 case CASE_ICH: // ESC [@ insert charactor 700 /* ICH */ 701 if ((row = param[0]) < 1) 702 row = 1; 703 fBuffer->InsertSpace(row); 704 parsestate = groundtable; 705 break; 706 707 case CASE_CUU: // ESC [A cursor up, up arrow key. 708 /* CUU */ 709 if ((row = param[0]) < 1) 710 row = 1; 711 fBuffer->MoveCursorUp(row); 712 parsestate = groundtable; 713 break; 714 715 case CASE_CUD: // ESC [B cursor down, down arrow key. 716 /* CUD */ 717 if ((row = param[0]) < 1) 718 row = 1; 719 fBuffer->MoveCursorDown(row); 720 parsestate = groundtable; 721 break; 722 723 case CASE_CUF: // ESC [C cursor forword 724 /* CUF */ 725 if ((row = param[0]) < 1) 726 row = 1; 727 fBuffer->MoveCursorRight(row); 728 parsestate = groundtable; 729 break; 730 731 case CASE_CUB: // ESC [D cursor backword 732 /* CUB */ 733 if ((row = param[0]) < 1) 734 row = 1; 735 fBuffer->MoveCursorLeft(row); 736 parsestate = groundtable; 737 break; 738 739 case CASE_CUP: // ESC [...H move cursor 740 /* CUP | HVP */ 741 if ((row = param[0]) < 1) 742 row = 1; 743 if (nparam < 2 || (column = param[1]) < 1) 744 column = 1; 745 746 fBuffer->SetCursor(column - 1, row - 1 ); 747 parsestate = groundtable; 748 break; 749 750 case CASE_ED: // ESC [ ...J clear screen 751 /* ED */ 752 switch (param[0]) { 753 case DEFAULT: 754 case 0: 755 fBuffer->EraseBelow(); 756 break; 757 758 case 1: 759 fBuffer->EraseAbove(); 760 break; 761 762 case 2: 763 fBuffer->EraseAll(); 764 break; 765 } 766 parsestate = groundtable; 767 break; 768 769 case CASE_EL: // ESC [ ...K delete line 770 /* EL */ 771 switch (param[0]) { 772 case DEFAULT: 773 case 0: 774 fBuffer->DeleteColumns(); 775 break; 776 777 case 1: 778 fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1); 779 break; 780 781 case 2: 782 fBuffer->DeleteColumnsFrom(0); 783 break; 784 } 785 parsestate = groundtable; 786 break; 787 788 case CASE_IL: 789 /* IL */ 790 if ((row = param[0]) < 1) 791 row = 1; 792 fBuffer->InsertLines(row); 793 parsestate = groundtable; 794 break; 795 796 case CASE_DL: 797 /* DL */ 798 if ((row = param[0]) < 1) 799 row = 1; 800 fBuffer->DeleteLines(row); 801 parsestate = groundtable; 802 break; 803 804 case CASE_DCH: 805 /* DCH */ 806 if ((row = param[0]) < 1) 807 row = 1; 808 fBuffer->DeleteChars(row); 809 parsestate = groundtable; 810 break; 811 812 case CASE_SET: 813 /* SET */ 814 if (param[0] == 4) 815 fBuffer->SetInsertMode(MODE_INSERT); 816 parsestate = groundtable; 817 break; 818 819 case CASE_RST: 820 /* RST */ 821 if (param[0] == 4) 822 fBuffer->SetInsertMode(MODE_OVER); 823 parsestate = groundtable; 824 break; 825 826 case CASE_SGR: 827 { 828 /* SGR */ 829 for (row = 0; row < nparam; ++row) { 830 switch (param[row]) { 831 case DEFAULT: 832 case 0: /* Reset attribute */ 833 fAttr = FORECOLORED(7); 834 break; 835 836 case 1: /* Bright / Bold */ 837 fAttr |= FORECOLORED(8); 838 fAttr |= FORESET; 839 break; 840 841 case 4: /* Underline */ 842 fAttr |= UNDERLINE; 843 break; 844 845 case 5: 846 fAttr |= BOLD; 847 break; 848 849 case 7: /* Inverse */ 850 fAttr |= INVERSE; 851 break; 852 853 case 2: /* Faint: decreased intensity */ 854 case 21: /* Bright/Bold: off or Underline: Double */ 855 case 22: /* Not Bold, not bright, not faint */ 856 fAttr &= ~(FORECOLORED(8) | BOLD); 857 fAttr |= FORESET; 858 break; 859 860 case 24: /* Not Underline */ 861 fAttr &= ~UNDERLINE; 862 break; 863 864 case 27: /* Not Inverse */ 865 fAttr &= ~INVERSE; 866 break; 867 868 case 30: 869 case 31: 870 case 32: 871 case 33: 872 case 34: 873 case 35: 874 case 36: 875 case 37: 876 fAttr &= ~FORECOLORED(7); 877 fAttr |= FORECOLORED(param[row] - 30); 878 fAttr |= FORESET; 879 break; 880 881 case 38: 882 { 883 if (nparam != 3 || param[1] != 5) 884 break; 885 fAttr &= ~FORECOLORED(255 - 8); 886 fAttr |= FORECOLORED(param[2]); 887 fAttr |= FORESET; 888 889 row = nparam; // force exit of the parsing 890 891 break; 892 } 893 894 case 39: 895 fAttr &= ~FORESET; 896 break; 897 898 case 40: 899 case 41: 900 case 42: 901 case 43: 902 case 44: 903 case 45: 904 case 46: 905 case 47: 906 fAttr &= ~BACKCOLOR; 907 fAttr |= BACKCOLORED(param[row] - 40); 908 fAttr |= BACKSET; 909 break; 910 911 case 48: 912 { 913 if (nparam != 3 || param[1] != 5) 914 break; 915 fAttr &= ~BACKCOLOR; 916 fAttr |= BACKCOLORED(param[2]); 917 fAttr |= BACKSET; 918 919 row = nparam; // force exit of the parsing 920 921 break; 922 } 923 924 case 49: 925 fAttr &= ~BACKSET; 926 break; 927 } 928 } 929 parsestate = groundtable; 930 break; 931 } 932 933 case CASE_CPR: 934 // Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp) 935 // 21-JUL-99 936 _DeviceStatusReport(param[0]); 937 parsestate = groundtable; 938 break; 939 940 case CASE_DA1: 941 // DA - report device attributes 942 if (param[0] < 1) { 943 // claim to be a VT102 944 write(fFd, "\033[?6c", 5); 945 } 946 parsestate = groundtable; 947 break; 948 949 case CASE_DECSTBM: 950 /* DECSTBM - set scrolling region */ 951 952 if ((top = param[0]) < 1) 953 top = 1; 954 955 if (nparam < 2) 956 bottom = fBuffer->Height(); 957 else 958 bottom = param[1]; 959 960 top--; 961 bottom--; 962 963 if (bottom > top) 964 fBuffer->SetScrollRegion(top, bottom); 965 966 parsestate = groundtable; 967 break; 968 969 case CASE_DECREQTPARM: 970 // DEXREQTPARM - request terminal parameters 971 _DecReqTermParms(param[0]); 972 parsestate = groundtable; 973 break; 974 975 case CASE_DECSET: 976 /* DECSET */ 977 for (int i = 0; i < nparam; i++) 978 _DecPrivateModeSet(param[i]); 979 parsestate = groundtable; 980 break; 981 982 case CASE_DECRST: 983 /* DECRST */ 984 for (int i = 0; i < nparam; i++) 985 _DecPrivateModeReset(param[i]); 986 parsestate = groundtable; 987 break; 988 989 case CASE_DECALN: 990 /* DECALN */ 991 fBuffer->FillScreen(UTF8Char('E'), 1, 0); 992 parsestate = groundtable; 993 break; 994 995 // case CASE_GSETS: 996 // screen->gsets[scstype] = GSET(c) | cs96; 997 // parsestate = groundtable; 998 // break; 999 1000 case CASE_DECSC: 1001 /* DECSC */ 1002 _DecSaveCursor(); 1003 parsestate = groundtable; 1004 break; 1005 1006 case CASE_DECRC: 1007 /* DECRC */ 1008 _DecRestoreCursor(); 1009 parsestate = groundtable; 1010 break; 1011 1012 case CASE_HTS: 1013 /* HTS */ 1014 fBuffer->SetTabStop(fBuffer->Cursor().x); 1015 parsestate = groundtable; 1016 break; 1017 1018 case CASE_TBC: 1019 /* TBC */ 1020 if (param[0] < 1) 1021 fBuffer->ClearTabStop(fBuffer->Cursor().x); 1022 else if (param[0] == 3) 1023 fBuffer->ClearAllTabStops(); 1024 parsestate = groundtable; 1025 break; 1026 1027 case CASE_RI: 1028 /* RI */ 1029 fBuffer->InsertRI(); 1030 parsestate = groundtable; 1031 break; 1032 1033 case CASE_SS2: 1034 /* SS2 */ 1035 curess = c; 1036 parsestate = groundtable; 1037 break; 1038 1039 case CASE_SS3: 1040 /* SS3 */ 1041 curess = c; 1042 parsestate = groundtable; 1043 break; 1044 1045 case CASE_CSI_STATE: 1046 /* enter csi state */ 1047 nparam = 1; 1048 param[0] = DEFAULT; 1049 parsestate = gCsiTable; 1050 break; 1051 1052 case CASE_OSC: 1053 { 1054 /* Operating System Command: ESC ] */ 1055 char string[512]; 1056 uint32 len = 0; 1057 uchar mode_char = _NextParseChar(); 1058 if (mode_char != '0' 1059 && mode_char != '1' 1060 && mode_char != '2') { 1061 parsestate = groundtable; 1062 break; 1063 } 1064 uchar currentChar = _NextParseChar(); 1065 while ((currentChar = _NextParseChar()) != 0x7) { 1066 if (!isprint(currentChar & 0x7f) 1067 || len+2 >= sizeof(string)) 1068 break; 1069 string[len++] = currentChar; 1070 } 1071 if (currentChar == 0x7) { 1072 string[len] = '\0'; 1073 switch (mode_char) { 1074 case '0': 1075 case '2': 1076 fBuffer->SetTitle(string); 1077 break; 1078 case '1': 1079 break; 1080 } 1081 } 1082 parsestate = groundtable; 1083 break; 1084 } 1085 1086 case CASE_RIS: // ESC c ... Reset terminal. 1087 break; 1088 1089 case CASE_LS2: 1090 /* LS2 */ 1091 // screen->curgl = 2; 1092 parsestate = groundtable; 1093 break; 1094 1095 case CASE_LS3: 1096 /* LS3 */ 1097 // screen->curgl = 3; 1098 parsestate = groundtable; 1099 break; 1100 1101 case CASE_LS3R: 1102 /* LS3R */ 1103 // screen->curgr = 3; 1104 parsestate = groundtable; 1105 break; 1106 1107 case CASE_LS2R: 1108 /* LS2R */ 1109 // screen->curgr = 2; 1110 parsestate = groundtable; 1111 break; 1112 1113 case CASE_LS1R: 1114 /* LS1R */ 1115 // screen->curgr = 1; 1116 parsestate = groundtable; 1117 break; 1118 1119 case CASE_VPA: // ESC [...d move cursor absolute vertical 1120 /* VPA (CV) */ 1121 if ((row = param[0]) < 1) 1122 row = 1; 1123 1124 // note beterm wants it 1-based unlike usual terminals 1125 fBuffer->SetCursorY(row - 1); 1126 parsestate = groundtable; 1127 break; 1128 1129 case CASE_HPA: // ESC [...G move cursor absolute horizontal 1130 /* HPA (CH) */ 1131 if ((column = param[0]) < 1) 1132 column = 1; 1133 1134 // note beterm wants it 1-based unlike usual terminals 1135 fBuffer->SetCursorX(column - 1); 1136 parsestate = groundtable; 1137 break; 1138 1139 case CASE_SU: // scroll screen up 1140 if ((row = param[0]) < 1) 1141 row = 1; 1142 fBuffer->ScrollBy(row); 1143 parsestate = groundtable; 1144 break; 1145 1146 case CASE_SD: // scroll screen down 1147 if ((row = param[0]) < 1) 1148 row = 1; 1149 fBuffer->ScrollBy(-row); 1150 parsestate = groundtable; 1151 break; 1152 1153 1154 case CASE_ECH: // erase characters 1155 if ((column = param[0]) < 1) 1156 column = 1; 1157 fBuffer->EraseChars(column); 1158 parsestate = groundtable; 1159 break; 1160 1161 default: 1162 break; 1163 } 1164 } catch (...) { 1165 break; 1166 } 1167 } 1168 1169 return B_OK; 1170 } 1171 1172 1173 /*static*/ int32 1174 TermParse::_ptyreader_thread(void *data) 1175 { 1176 return reinterpret_cast<TermParse *>(data)->PtyReader(); 1177 } 1178 1179 1180 /*static*/ int32 1181 TermParse::_escparse_thread(void *data) 1182 { 1183 return reinterpret_cast<TermParse *>(data)->EscParse(); 1184 } 1185 1186 1187 status_t 1188 TermParse::_ReadParserBuffer() 1189 { 1190 // We have to unlock the terminal buffer while waiting for data from the 1191 // PTY. We don't have to unlock when we don't need to wait, but we do it 1192 // anyway, so that TermView won't be starved when trying to synchronize. 1193 fBuffer->Unlock(); 1194 1195 // wait for new input from pty 1196 if (fReadBufferSize == 0) { 1197 status_t status = B_OK; 1198 while (fReadBufferSize == 0 && status == B_OK) { 1199 do { 1200 status = acquire_sem(fReaderSem); 1201 } while (status == B_INTERRUPTED); 1202 1203 // eat any sems that were released unconditionally 1204 int32 semCount; 1205 if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0) 1206 acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0); 1207 } 1208 1209 if (status < B_OK) { 1210 fBuffer->Lock(); 1211 return status; 1212 } 1213 } 1214 1215 int32 toRead = fReadBufferSize; 1216 if (toRead > ESC_PARSER_BUFFER_SIZE) 1217 toRead = ESC_PARSER_BUFFER_SIZE; 1218 1219 for (int32 i = 0; i < toRead; i++) { 1220 // TODO: This could be optimized using memcpy instead and 1221 // calculating space left as in the PtyReader(). 1222 fParserBuffer[i] = fReadBuffer[fBufferPosition]; 1223 fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE; 1224 } 1225 1226 int32 bufferSize = atomic_add(&fReadBufferSize, -toRead); 1227 1228 // If the pty reader thread waits and we have made enough space in the 1229 // buffer now, let it run again. 1230 if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE 1231 && bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) { 1232 release_sem(fReaderLocker); 1233 } 1234 1235 fParserBufferSize = toRead; 1236 fParserBufferOffset = 0; 1237 1238 fBuffer->Lock(); 1239 return B_OK; 1240 } 1241 1242 1243 void 1244 TermParse::_DeviceStatusReport(int n) 1245 { 1246 char sbuf[16] ; 1247 int len; 1248 1249 switch (n) { 1250 case 5: 1251 { 1252 // Device status report requested 1253 // reply with "no malfunction detected" 1254 const char* toWrite = "\033[0n"; 1255 write(fFd, toWrite, strlen(toWrite)); 1256 break ; 1257 } 1258 case 6: 1259 // Cursor position report requested 1260 len = sprintf(sbuf, "\033[%" B_PRId32 ";%" B_PRId32 "R", 1261 fBuffer->Cursor().y + 1, 1262 fBuffer->Cursor().x + 1); 1263 write(fFd, sbuf, len); 1264 break ; 1265 default: 1266 return; 1267 } 1268 } 1269 1270 1271 void 1272 TermParse::_DecReqTermParms(int value) 1273 { 1274 // Terminal parameters report: 1275 // type (2 or 3); 1276 // no parity (1); 1277 // 8 bits per character (1); 1278 // transmit speed 38400bps (128); 1279 // receive speed 38400bps (128); 1280 // bit rate multiplier 16 (1); 1281 // no flags (0) 1282 char parms[] = "\033[?;1;1;128;128;1;0x"; 1283 1284 if (value < 1) 1285 parms[2] = '2'; 1286 else if (value == 1) 1287 parms[2] = '3'; 1288 else 1289 return; 1290 1291 write(fFd, parms, strlen(parms)); 1292 } 1293 1294 1295 void 1296 TermParse::_DecPrivateModeSet(int value) 1297 { 1298 switch (value) { 1299 case 1: 1300 // Application Cursor Keys (whatever that means). 1301 // Not supported yet. 1302 break; 1303 case 5: 1304 // Reverse Video (inverses colors for the complete screen 1305 // -- when followed by normal video, that's shortly flashes the 1306 // screen). 1307 // Not supported yet. 1308 break; 1309 case 6: 1310 // Set Origin Mode. 1311 fBuffer->SetOriginMode(true); 1312 break; 1313 case 9: 1314 // Set Mouse X and Y on button press. 1315 fBuffer->ReportX10MouseEvent(true); 1316 break; 1317 case 12: 1318 // Start Blinking Cursor. 1319 // Not supported yet. 1320 break; 1321 case 25: 1322 // Show Cursor. 1323 // Not supported yet. 1324 break; 1325 case 47: 1326 // Use Alternate Screen Buffer. 1327 fBuffer->UseAlternateScreenBuffer(false); 1328 break; 1329 case 1000: 1330 // Send Mouse X & Y on button press and release. 1331 fBuffer->ReportNormalMouseEvent(true); 1332 break; 1333 case 1002: 1334 // Send Mouse X and Y on button press and release, and on motion 1335 // when the mouse enter a new cell 1336 fBuffer->ReportButtonMouseEvent(true); 1337 break; 1338 case 1003: 1339 // Use All Motion Mouse Tracking 1340 fBuffer->ReportAnyMouseEvent(true); 1341 break; 1342 case 1034: 1343 // TODO: Interprete "meta" key, sets eighth bit. 1344 // Not supported yet. 1345 break; 1346 case 1036: 1347 // TODO: Send ESC when Meta modifies a key 1348 // Not supported yet. 1349 break; 1350 case 1039: 1351 // TODO: Send ESC when Alt modifies a key 1352 // Not supported yet. 1353 break; 1354 case 1049: 1355 // Save cursor as in DECSC and use Alternate Screen Buffer, clearing 1356 // it first. 1357 _DecSaveCursor(); 1358 fBuffer->UseAlternateScreenBuffer(true); 1359 break; 1360 } 1361 } 1362 1363 1364 void 1365 TermParse::_DecPrivateModeReset(int value) 1366 { 1367 switch (value) { 1368 case 1: 1369 // Normal Cursor Keys (whatever that means). 1370 // Not supported yet. 1371 break; 1372 case 3: 1373 // 80 Column Mode. 1374 // Not supported yet. 1375 break; 1376 case 4: 1377 // Jump (Fast) Scroll. 1378 // Not supported yet. 1379 break; 1380 case 5: 1381 // Normal Video (Leaves Reverse Video, cf. there). 1382 // Not supported yet. 1383 break; 1384 case 6: 1385 // Reset Origin Mode. 1386 fBuffer->SetOriginMode(false); 1387 break; 1388 case 9: 1389 // Disable Mouse X and Y on button press. 1390 fBuffer->ReportX10MouseEvent(false); 1391 break; 1392 case 12: 1393 // Stop Blinking Cursor. 1394 // Not supported yet. 1395 break; 1396 case 25: 1397 // Hide Cursor 1398 // Not supported yet. 1399 break; 1400 case 47: 1401 // Use Normal Screen Buffer. 1402 fBuffer->UseNormalScreenBuffer(); 1403 break; 1404 case 1000: 1405 // Don't send Mouse X & Y on button press and release. 1406 fBuffer->ReportNormalMouseEvent(false); 1407 break; 1408 case 1002: 1409 // Don't send Mouse X and Y on button press and release, and on motion 1410 // when the mouse enter a new cell 1411 fBuffer->ReportButtonMouseEvent(false); 1412 break; 1413 case 1003: 1414 // Disable All Motion Mouse Tracking. 1415 fBuffer->ReportAnyMouseEvent(false); 1416 break; 1417 case 1034: 1418 // Don't interprete "meta" key. 1419 // Not supported yet. 1420 break; 1421 case 1036: 1422 // TODO: Don't send ESC when Meta modifies a key 1423 // Not supported yet. 1424 break; 1425 case 1039: 1426 // TODO: Don't send ESC when Alt modifies a key 1427 // Not supported yet. 1428 break; 1429 case 1049: 1430 // Use Normal Screen Buffer and restore cursor as in DECRC. 1431 fBuffer->UseNormalScreenBuffer(); 1432 _DecRestoreCursor(); 1433 break; 1434 } 1435 } 1436 1437 1438 void 1439 TermParse::_DecSaveCursor() 1440 { 1441 fBuffer->SaveCursor(); 1442 fBuffer->SaveOriginMode(); 1443 fSavedAttr = fAttr; 1444 } 1445 1446 1447 void 1448 TermParse::_DecRestoreCursor() 1449 { 1450 fBuffer->RestoreCursor(); 1451 fBuffer->RestoreOriginMode(); 1452 fAttr = fSavedAttr; 1453 } 1454