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