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; 366 int bottom; 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 376 int row; 377 int column; 378 379 // default encoding system is UTF8 380 int *groundtable = gUTF8GroundTable; 381 int *parsestate = gUTF8GroundTable; 382 383 // handle alternative character sets G0 - G4 384 const char** graphSets[4] = { NULL, NULL, NULL, NULL }; 385 int curGL = 0; 386 int curGR = 0; 387 388 BAutolock locker(fBuffer); 389 390 while (!fQuitting) { 391 try { 392 uchar c = _NextParseChar(); 393 394 //DumpState(groundtable, parsestate, c); 395 396 if (currentEncoding != fBuffer->Encoding()) { 397 // Change coding, change parse table. 398 groundtable = _GuessGroundTable(fBuffer->Encoding()); 399 parsestate = groundtable; 400 currentEncoding = fBuffer->Encoding(); 401 } 402 403 //debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]); 404 int32 srcLen = 0; 405 int32 dstLen = sizeof(dstbuf); 406 int32 dummyState = 0; 407 408 switch (parsestate[c]) { 409 case CASE_PRINT: 410 { 411 int curGS = c < 128 ? curGL : curGR; 412 const char** curGraphSet = graphSets[curGS]; 413 if (curGraphSet != NULL) { 414 int offset = c - (c < 128 ? 0x20 : 0xA0); 415 if (offset >= 0 && offset < 96 416 && curGraphSet[offset] != 0) { 417 fBuffer->InsertChar(curGraphSet[offset]); 418 break; 419 } 420 } 421 fBuffer->InsertChar((char)c); 422 break; 423 } 424 case CASE_PRINT_GR: 425 { 426 /* case iso8859 gr character, or euc */ 427 switch (currentEncoding) { 428 case B_EUC_CONVERSION: 429 case B_EUC_KR_CONVERSION: 430 case B_JIS_CONVERSION: 431 case B_BIG5_CONVERSION: 432 cbuf[srcLen++] = c; 433 c = _NextParseChar(); 434 cbuf[srcLen++] = c; 435 break; 436 437 case B_GBK_CONVERSION: 438 cbuf[srcLen++] = c; 439 do { 440 // GBK-compatible codepoints are 2-bytes long 441 c = _NextParseChar(); 442 cbuf[srcLen++] = c; 443 444 // GB18030 extends GBK with 4-byte codepoints 445 // using 2nd byte from range 0x30...0x39 446 if (srcLen == 2 && (c < 0x30 || c > 0x39)) 447 break; 448 } while (srcLen < 4); 449 break; 450 451 default: // ISO-8859-1...10 and MacRoman 452 cbuf[srcLen++] = c; 453 break; 454 } 455 456 if (srcLen > 0) { 457 int encoding = currentEncoding == B_JIS_CONVERSION 458 ? B_EUC_CONVERSION : currentEncoding; 459 460 convert_to_utf8(encoding, cbuf, &srcLen, 461 dstbuf, &dstLen, &dummyState, '?'); 462 463 fBuffer->InsertChar(UTF8Char(dstbuf, dstLen)); 464 } 465 break; 466 } 467 468 case CASE_LF: 469 fBuffer->InsertLF(); 470 break; 471 472 case CASE_CR: 473 fBuffer->InsertCR(); 474 break; 475 476 case CASE_SJIS_KANA: 477 cbuf[srcLen++] = c; 478 convert_to_utf8(currentEncoding, cbuf, &srcLen, 479 dstbuf, &dstLen, &dummyState, '?'); 480 fBuffer->InsertChar(UTF8Char(dstbuf, dstLen)); 481 break; 482 483 case CASE_SJIS_INSTRING: 484 cbuf[srcLen++] = c; 485 c = _NextParseChar(); 486 cbuf[srcLen++] = c; 487 488 convert_to_utf8(currentEncoding, cbuf, &srcLen, 489 dstbuf, &dstLen, &dummyState, '?'); 490 fBuffer->InsertChar(UTF8Char(dstbuf, dstLen)); 491 break; 492 493 case CASE_UTF8_2BYTE: 494 cbuf[srcLen++] = c; 495 c = _NextParseChar(); 496 if (groundtable[c] != CASE_UTF8_INSTRING) 497 break; 498 cbuf[srcLen++] = c; 499 500 fBuffer->InsertChar(UTF8Char(cbuf, srcLen)); 501 break; 502 503 case CASE_UTF8_3BYTE: 504 cbuf[srcLen++] = c; 505 506 do { 507 c = _NextParseChar(); 508 if (groundtable[c] != CASE_UTF8_INSTRING) { 509 srcLen = 0; 510 break; 511 } 512 cbuf[srcLen++] = c; 513 514 } while (srcLen != 3); 515 516 if (srcLen > 0) 517 fBuffer->InsertChar(UTF8Char(cbuf, srcLen)); 518 break; 519 520 case CASE_SCS_STATE: 521 { 522 int set = -1; 523 switch (c) { 524 case '(': 525 set = 0; 526 break; 527 case ')': 528 case '-': 529 set = 1; 530 break; 531 case '*': 532 case '.': 533 set = 2; 534 break; 535 case '+': 536 case '/': 537 set = 3; 538 break; 539 default: 540 break; 541 } 542 543 if (set > -1) { 544 char page = _NextParseChar(); 545 switch (page) { 546 case '0': 547 graphSets[set] = gLineDrawGraphSet; 548 break; 549 default: 550 graphSets[set] = NULL; 551 break; 552 } 553 } 554 555 parsestate = groundtable; 556 break; 557 } 558 559 case CASE_GROUND_STATE: 560 /* exit ignore mode */ 561 parsestate = groundtable; 562 break; 563 564 case CASE_BELL: 565 beep(); 566 break; 567 568 case CASE_BS: 569 fBuffer->MoveCursorLeft(1); 570 break; 571 572 case CASE_TAB: 573 fBuffer->InsertTab(); 574 break; 575 576 case CASE_ESC: 577 /* escape */ 578 parsestate = gEscTable; 579 break; 580 581 case CASE_IGNORE_STATE: 582 /* Ies: ignore anything else */ 583 parsestate = gIgnoreTable; 584 break; 585 586 case CASE_IGNORE_ESC: 587 /* Ign: escape */ 588 parsestate = gIesTable; 589 break; 590 591 case CASE_IGNORE: 592 /* Ignore character */ 593 break; 594 595 case CASE_LS1: 596 /* select G1 into GL */ 597 curGL = 1; 598 parsestate = groundtable; 599 break; 600 601 case CASE_LS0: 602 /* select G0 into GL */ 603 curGL = 0; 604 parsestate = groundtable; 605 break; 606 607 case CASE_SCR_STATE: // ESC # 608 /* enter scr state */ 609 parsestate = gScrTable; 610 break; 611 612 case CASE_ESC_IGNORE: 613 /* unknown escape sequence */ 614 parsestate = gEscIgnoreTable; 615 break; 616 617 case CASE_ESC_DIGIT: // ESC [ number 618 /* digit in csi or dec mode */ 619 if ((row = param[nparam - 1]) == DEFAULT) 620 row = 0; 621 param[nparam - 1] = 10 * row + (c - '0'); 622 break; 623 624 case CASE_ESC_SEMI: // ESC ; 625 /* semicolon in csi or dec mode */ 626 if (nparam < NPARAM) 627 param[nparam++] = DEFAULT; 628 break; 629 630 case CASE_CSI_SP: // ESC [N q 631 // part of change cursor style DECSCUSR 632 if (nparam < NPARAM) 633 param[nparam++] = ' '; 634 break; 635 636 case CASE_DEC_STATE: 637 /* enter dec mode */ 638 parsestate = gDecTable; 639 break; 640 641 case CASE_ICH: // ESC [@ insert charactor 642 /* ICH */ 643 if ((row = param[0]) < 1) 644 row = 1; 645 fBuffer->InsertSpace(row); 646 parsestate = groundtable; 647 break; 648 649 case CASE_CUU: // ESC [A cursor up, up arrow key. 650 /* CUU */ 651 if ((row = param[0]) < 1) 652 row = 1; 653 fBuffer->MoveCursorUp(row); 654 parsestate = groundtable; 655 break; 656 657 case CASE_CUD: // ESC [B cursor down, down arrow key. 658 /* CUD */ 659 if ((row = param[0]) < 1) 660 row = 1; 661 fBuffer->MoveCursorDown(row); 662 parsestate = groundtable; 663 break; 664 665 case CASE_CUF: // ESC [C cursor forword 666 /* CUF */ 667 if ((row = param[0]) < 1) 668 row = 1; 669 fBuffer->MoveCursorRight(row); 670 parsestate = groundtable; 671 break; 672 673 case CASE_CUB: // ESC [D cursor backword 674 /* CUB */ 675 if ((row = param[0]) < 1) 676 row = 1; 677 fBuffer->MoveCursorLeft(row); 678 parsestate = groundtable; 679 break; 680 681 case CASE_CUP: // ESC [...H move cursor 682 /* CUP | HVP */ 683 if ((row = param[0]) < 1) 684 row = 1; 685 if (nparam < 2 || (column = param[1]) < 1) 686 column = 1; 687 688 fBuffer->SetCursor(column - 1, row - 1 ); 689 parsestate = groundtable; 690 break; 691 692 case CASE_ED: // ESC [ ...J clear screen 693 /* ED */ 694 switch (param[0]) { 695 case DEFAULT: 696 case 0: 697 fBuffer->EraseBelow(); 698 break; 699 700 case 1: 701 fBuffer->EraseAbove(); 702 break; 703 704 case 2: 705 fBuffer->EraseAll(); 706 break; 707 } 708 parsestate = groundtable; 709 break; 710 711 case CASE_EL: // ESC [ ...K delete line 712 /* EL */ 713 switch (param[0]) { 714 case DEFAULT: 715 case 0: 716 fBuffer->DeleteColumns(); 717 break; 718 719 case 1: 720 fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1); 721 break; 722 723 case 2: 724 fBuffer->DeleteColumnsFrom(0); 725 break; 726 } 727 parsestate = groundtable; 728 break; 729 730 case CASE_IL: 731 /* IL */ 732 if ((row = param[0]) < 1) 733 row = 1; 734 fBuffer->InsertLines(row); 735 parsestate = groundtable; 736 break; 737 738 case CASE_DL: 739 /* DL */ 740 if ((row = param[0]) < 1) 741 row = 1; 742 fBuffer->DeleteLines(row); 743 parsestate = groundtable; 744 break; 745 746 case CASE_DCH: 747 /* DCH */ 748 if ((row = param[0]) < 1) 749 row = 1; 750 fBuffer->DeleteChars(row); 751 parsestate = groundtable; 752 break; 753 754 case CASE_SET: 755 /* SET */ 756 if (param[0] == 4) 757 fBuffer->SetInsertMode(MODE_INSERT); 758 parsestate = groundtable; 759 break; 760 761 case CASE_RST: 762 /* RST */ 763 if (param[0] == 4) 764 fBuffer->SetInsertMode(MODE_OVER); 765 parsestate = groundtable; 766 break; 767 768 case CASE_SGR: 769 { 770 /* SGR */ 771 uint32 attributes = fBuffer->GetAttributes(); 772 for (row = 0; row < nparam; ++row) { 773 switch (param[row]) { 774 case DEFAULT: 775 case 0: /* Reset attribute */ 776 attributes = 0; 777 break; 778 779 case 1: /* Bold */ 780 case 5: 781 attributes |= BOLD; 782 break; 783 784 case 4: /* Underline */ 785 attributes |= UNDERLINE; 786 break; 787 788 case 7: /* Inverse */ 789 attributes |= INVERSE; 790 break; 791 792 case 22: /* Not Bold */ 793 attributes &= ~BOLD; 794 break; 795 796 case 24: /* Not Underline */ 797 attributes &= ~UNDERLINE; 798 break; 799 800 case 27: /* Not Inverse */ 801 attributes &= ~INVERSE; 802 break; 803 804 case 90: 805 case 91: 806 case 92: 807 case 93: 808 case 94: 809 case 95: 810 case 96: 811 case 97: 812 param[row] -= 60; 813 case 30: 814 case 31: 815 case 32: 816 case 33: 817 case 34: 818 case 35: 819 case 36: 820 case 37: 821 attributes &= ~FORECOLOR; 822 attributes |= FORECOLORED(param[row] - 30); 823 attributes |= FORESET; 824 break; 825 826 case 38: 827 { 828 int color = -1; 829 if (nparam == 3 && param[1] == 5) 830 color = param[2]; 831 else if (nparam == 5 && param[1] == 2) 832 color = fBuffer->GuessPaletteColor( 833 param[2], param[3], param[4]); 834 835 if (color >= 0) { 836 attributes &= ~FORECOLOR; 837 attributes |= FORECOLORED(color); 838 attributes |= FORESET; 839 } 840 841 row = nparam; // force exit of the parsing 842 break; 843 } 844 845 case 39: 846 attributes &= ~FORESET; 847 break; 848 849 case 100: 850 case 101: 851 case 102: 852 case 103: 853 case 104: 854 case 105: 855 case 106: 856 case 107: 857 param[row] -= 60; 858 case 40: 859 case 41: 860 case 42: 861 case 43: 862 case 44: 863 case 45: 864 case 46: 865 case 47: 866 attributes &= ~BACKCOLOR; 867 attributes |= BACKCOLORED(param[row] - 40); 868 attributes |= BACKSET; 869 break; 870 871 case 48: 872 { 873 int color = -1; 874 if (nparam == 3 && param[1] == 5) 875 color = param[2]; 876 else if (nparam == 5 && param[1] == 2) 877 color = fBuffer->GuessPaletteColor( 878 param[2], param[3], param[4]); 879 880 if (color >= 0) { 881 attributes &= ~BACKCOLOR; 882 attributes |= BACKCOLORED(color); 883 attributes |= BACKSET; 884 } 885 886 row = nparam; // force exit of the parsing 887 break; 888 } 889 890 case 49: 891 attributes &= ~BACKSET; 892 break; 893 } 894 } 895 fBuffer->SetAttributes(attributes); 896 parsestate = groundtable; 897 break; 898 } 899 900 case CASE_CPR: 901 // Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp) 902 // 21-JUL-99 903 _DeviceStatusReport(param[0]); 904 parsestate = groundtable; 905 break; 906 907 case CASE_DA1: 908 // DA - report device attributes 909 if (param[0] < 1) { 910 // claim to be a VT102 911 write(fFd, "\033[?6c", 5); 912 } 913 parsestate = groundtable; 914 break; 915 916 case CASE_DECSTBM: 917 /* DECSTBM - set scrolling region */ 918 919 if ((top = param[0]) < 1) 920 top = 1; 921 922 if (nparam < 2) 923 bottom = fBuffer->Height(); 924 else 925 bottom = param[1]; 926 927 top--; 928 bottom--; 929 930 if (bottom > top) 931 fBuffer->SetScrollRegion(top, bottom); 932 933 parsestate = groundtable; 934 break; 935 936 case CASE_DECSCUSR_ETC: 937 // DECSCUSR - set cursor style VT520 938 if (nparam == 2 && param[1] == ' ') { 939 bool blinking = (param[0] & 0x01) != 0; 940 int style = -1; 941 switch (param[0]) { 942 case 0: 943 blinking = true; 944 case 1: 945 case 2: 946 style = BLOCK_CURSOR; 947 break; 948 case 3: 949 case 4: 950 style = UNDERLINE_CURSOR; 951 break; 952 case 5: 953 case 6: 954 style = IBEAM_CURSOR; 955 break; 956 } 957 958 if (style != -1) 959 fBuffer->SetCursorStyle(style, blinking); 960 } 961 parsestate = groundtable; 962 break; 963 964 case CASE_DECREQTPARM: 965 // DEXREQTPARM - request terminal parameters 966 _DecReqTermParms(param[0]); 967 parsestate = groundtable; 968 break; 969 970 case CASE_DECSET: 971 /* DECSET */ 972 for (int i = 0; i < nparam; i++) 973 _DecPrivateModeSet(param[i]); 974 parsestate = groundtable; 975 break; 976 977 case CASE_DECRST: 978 /* DECRST */ 979 for (int i = 0; i < nparam; i++) 980 _DecPrivateModeReset(param[i]); 981 parsestate = groundtable; 982 break; 983 984 case CASE_DECALN: 985 /* DECALN */ 986 fBuffer->FillScreen(UTF8Char('E'), 0); 987 parsestate = groundtable; 988 break; 989 990 // case CASE_GSETS: 991 // screen->gsets[scstype] = GSET(c) | cs96; 992 // parsestate = groundtable; 993 // break; 994 995 case CASE_DECSC: 996 /* DECSC */ 997 fBuffer->SaveCursor(); 998 parsestate = groundtable; 999 break; 1000 1001 case CASE_DECRC: 1002 /* DECRC */ 1003 fBuffer->RestoreCursor(); 1004 parsestate = groundtable; 1005 break; 1006 1007 case CASE_HTS: 1008 /* HTS */ 1009 fBuffer->SetTabStop(fBuffer->Cursor().x); 1010 parsestate = groundtable; 1011 break; 1012 1013 case CASE_TBC: 1014 /* TBC */ 1015 if (param[0] < 1) 1016 fBuffer->ClearTabStop(fBuffer->Cursor().x); 1017 else if (param[0] == 3) 1018 fBuffer->ClearAllTabStops(); 1019 parsestate = groundtable; 1020 break; 1021 1022 case CASE_RI: 1023 /* RI */ 1024 fBuffer->InsertRI(); 1025 parsestate = groundtable; 1026 break; 1027 1028 case CASE_SS2: 1029 /* SS2 */ 1030 parsestate = groundtable; 1031 break; 1032 1033 case CASE_SS3: 1034 /* SS3 */ 1035 parsestate = groundtable; 1036 break; 1037 1038 case CASE_CSI_STATE: 1039 /* enter csi state */ 1040 nparam = 1; 1041 param[0] = DEFAULT; 1042 parsestate = gCsiTable; 1043 break; 1044 1045 case CASE_OSC: 1046 { 1047 /* Operating System Command: ESC ] */ 1048 uchar params[512]; 1049 // fill the buffer until BEL, ST or something else. 1050 bool isParsed = false; 1051 int32 skipCount = 0; // take care about UTF-8 characters 1052 for (uint i = 0; !isParsed && i < sizeof(params); i++) { 1053 params[i] = _NextParseChar(); 1054 1055 if (skipCount > 0) { 1056 skipCount--; 1057 continue; 1058 } 1059 1060 skipCount = UTF8Char::ByteCount(params[i]) - 1; 1061 if (skipCount > 0) 1062 continue; 1063 1064 switch (params[i]) { 1065 // BEL 1066 case 0x07: 1067 isParsed = true; 1068 break; 1069 // 8-bit ST 1070 case 0x9c: 1071 isParsed = true; 1072 break; 1073 // 7-bit ST is "ESC \" 1074 case '\\': 1075 // hm... Was \x1b replaced by 0 during parsing? 1076 if (i > 0 && params[i - 1] == 0) { 1077 isParsed = true; 1078 break; 1079 } 1080 default: 1081 if (!isprint(params[i] & 0x7f)) 1082 break; 1083 continue; 1084 } 1085 params[i] = '\0'; 1086 } 1087 1088 // watchdog for the 'end of buffer' case 1089 params[sizeof(params) - 1] = '\0'; 1090 1091 if (isParsed) 1092 _ProcessOperatingSystemControls(params); 1093 1094 parsestate = groundtable; 1095 break; 1096 } 1097 1098 case CASE_RIS: // ESC c ... Reset terminal. 1099 break; 1100 1101 case CASE_LS2: 1102 /* select G2 into GL */ 1103 curGL = 2; 1104 parsestate = groundtable; 1105 break; 1106 1107 case CASE_LS3: 1108 /* select G3 into GL */ 1109 curGL = 3; 1110 parsestate = groundtable; 1111 break; 1112 1113 case CASE_LS3R: 1114 /* select G3 into GR */ 1115 curGR = 3; 1116 parsestate = groundtable; 1117 break; 1118 1119 case CASE_LS2R: 1120 /* select G2 into GR */ 1121 curGR = 2; 1122 parsestate = groundtable; 1123 break; 1124 1125 case CASE_LS1R: 1126 /* select G1 into GR */ 1127 curGR = 1; 1128 parsestate = groundtable; 1129 break; 1130 1131 case CASE_VPA: // ESC [...d move cursor absolute vertical 1132 /* VPA (CV) */ 1133 if ((row = param[0]) < 1) 1134 row = 1; 1135 1136 // note beterm wants it 1-based unlike usual terminals 1137 fBuffer->SetCursorY(row - 1); 1138 parsestate = groundtable; 1139 break; 1140 1141 case CASE_HPA: // ESC [...G move cursor absolute horizontal 1142 /* HPA (CH) */ 1143 if ((column = param[0]) < 1) 1144 column = 1; 1145 1146 // note beterm wants it 1-based unlike usual terminals 1147 fBuffer->SetCursorX(column - 1); 1148 parsestate = groundtable; 1149 break; 1150 1151 case CASE_SU: // scroll screen up 1152 if ((row = param[0]) < 1) 1153 row = 1; 1154 fBuffer->ScrollBy(row); 1155 parsestate = groundtable; 1156 break; 1157 1158 case CASE_SD: // scroll screen down 1159 if ((row = param[0]) < 1) 1160 row = 1; 1161 fBuffer->ScrollBy(-row); 1162 parsestate = groundtable; 1163 break; 1164 1165 1166 case CASE_ECH: // erase characters 1167 if ((column = param[0]) < 1) 1168 column = 1; 1169 fBuffer->EraseChars(column); 1170 parsestate = groundtable; 1171 break; 1172 1173 default: 1174 break; 1175 } 1176 } catch (...) { 1177 break; 1178 } 1179 } 1180 1181 return B_OK; 1182 } 1183 1184 1185 /*static*/ int32 1186 TermParse::_ptyreader_thread(void *data) 1187 { 1188 return reinterpret_cast<TermParse *>(data)->PtyReader(); 1189 } 1190 1191 1192 /*static*/ int32 1193 TermParse::_escparse_thread(void *data) 1194 { 1195 return reinterpret_cast<TermParse *>(data)->EscParse(); 1196 } 1197 1198 1199 status_t 1200 TermParse::_ReadParserBuffer() 1201 { 1202 // We have to unlock the terminal buffer while waiting for data from the 1203 // PTY. We don't have to unlock when we don't need to wait, but we do it 1204 // anyway, so that TermView won't be starved when trying to synchronize. 1205 fBuffer->Unlock(); 1206 1207 // wait for new input from pty 1208 if (atomic_get(&fReadBufferSize) == 0) { 1209 status_t status = B_OK; 1210 while (atomic_get(&fReadBufferSize) == 0 && status == B_OK) { 1211 do { 1212 status = acquire_sem(fReaderSem); 1213 } while (status == B_INTERRUPTED); 1214 1215 // eat any sems that were released unconditionally 1216 int32 semCount; 1217 if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0) 1218 acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0); 1219 } 1220 1221 if (status < B_OK) { 1222 fBuffer->Lock(); 1223 return status; 1224 } 1225 } 1226 1227 int32 toRead = atomic_get(&fReadBufferSize); 1228 if (toRead > ESC_PARSER_BUFFER_SIZE) 1229 toRead = ESC_PARSER_BUFFER_SIZE; 1230 1231 for (int32 i = 0; i < toRead; i++) { 1232 // TODO: This could be optimized using memcpy instead and 1233 // calculating space left as in the PtyReader(). 1234 fParserBuffer[i] = fReadBuffer[fBufferPosition]; 1235 fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE; 1236 } 1237 1238 int32 bufferSize = atomic_add(&fReadBufferSize, -toRead); 1239 1240 // If the pty reader thread waits and we have made enough space in the 1241 // buffer now, let it run again. 1242 if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE 1243 && bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) { 1244 release_sem(fReaderLocker); 1245 } 1246 1247 fParserBufferSize = toRead; 1248 fParserBufferOffset = 0; 1249 1250 fBuffer->Lock(); 1251 return B_OK; 1252 } 1253 1254 1255 void 1256 TermParse::_DeviceStatusReport(int n) 1257 { 1258 char sbuf[16] ; 1259 int len; 1260 1261 switch (n) { 1262 case 5: 1263 { 1264 // Device status report requested 1265 // reply with "no malfunction detected" 1266 const char* toWrite = "\033[0n"; 1267 write(fFd, toWrite, strlen(toWrite)); 1268 break ; 1269 } 1270 case 6: 1271 // Cursor position report requested 1272 len = sprintf(sbuf, "\033[%" B_PRId32 ";%" B_PRId32 "R", 1273 fBuffer->Cursor().y + 1, 1274 fBuffer->Cursor().x + 1); 1275 write(fFd, sbuf, len); 1276 break ; 1277 default: 1278 return; 1279 } 1280 } 1281 1282 1283 void 1284 TermParse::_DecReqTermParms(int value) 1285 { 1286 // Terminal parameters report: 1287 // type (2 or 3); 1288 // no parity (1); 1289 // 8 bits per character (1); 1290 // transmit speed 38400bps (128); 1291 // receive speed 38400bps (128); 1292 // bit rate multiplier 16 (1); 1293 // no flags (0) 1294 char parms[] = "\033[?;1;1;128;128;1;0x"; 1295 1296 if (value < 1) 1297 parms[2] = '2'; 1298 else if (value == 1) 1299 parms[2] = '3'; 1300 else 1301 return; 1302 1303 write(fFd, parms, strlen(parms)); 1304 } 1305 1306 1307 void 1308 TermParse::_DecPrivateModeSet(int value) 1309 { 1310 switch (value) { 1311 case 1: 1312 // Application Cursor Keys (whatever that means). 1313 // Not supported yet. 1314 break; 1315 case 5: 1316 // Reverse Video (inverses colors for the complete screen 1317 // -- when followed by normal video, that's shortly flashes the 1318 // screen). 1319 // Not supported yet. 1320 break; 1321 case 6: 1322 // Set Origin Mode. 1323 fBuffer->SetOriginMode(true); 1324 break; 1325 case 9: 1326 // Set Mouse X and Y on button press. 1327 fBuffer->ReportX10MouseEvent(true); 1328 break; 1329 case 12: 1330 // Start Blinking Cursor. 1331 fBuffer->SetCursorBlinking(true); 1332 break; 1333 case 25: 1334 // Show Cursor. 1335 fBuffer->SetCursorHidden(false); 1336 break; 1337 case 47: 1338 // Use Alternate Screen Buffer. 1339 fBuffer->UseAlternateScreenBuffer(false); 1340 break; 1341 case 1000: 1342 // Send Mouse X & Y on button press and release. 1343 fBuffer->ReportNormalMouseEvent(true); 1344 break; 1345 case 1002: 1346 // Send Mouse X and Y on button press and release, and on motion 1347 // when the mouse enter a new cell 1348 fBuffer->ReportButtonMouseEvent(true); 1349 break; 1350 case 1003: 1351 // Use All Motion Mouse Tracking 1352 fBuffer->ReportAnyMouseEvent(true); 1353 break; 1354 case 1034: 1355 // TODO: Interprete "meta" key, sets eighth bit. 1356 // Not supported yet. 1357 break; 1358 case 1036: 1359 // TODO: Send ESC when Meta modifies a key 1360 // Not supported yet. 1361 break; 1362 case 1039: 1363 // TODO: Send ESC when Alt modifies a key 1364 // Not supported yet. 1365 break; 1366 case 1049: 1367 // Save cursor as in DECSC and use Alternate Screen Buffer, clearing 1368 // it first. 1369 fBuffer->SaveCursor(); 1370 fBuffer->UseAlternateScreenBuffer(true); 1371 break; 1372 } 1373 } 1374 1375 1376 void 1377 TermParse::_DecPrivateModeReset(int value) 1378 { 1379 switch (value) { 1380 case 1: 1381 // Normal Cursor Keys (whatever that means). 1382 // Not supported yet. 1383 break; 1384 case 3: 1385 // 80 Column Mode. 1386 // Not supported yet. 1387 break; 1388 case 4: 1389 // Jump (Fast) Scroll. 1390 // Not supported yet. 1391 break; 1392 case 5: 1393 // Normal Video (Leaves Reverse Video, cf. there). 1394 // Not supported yet. 1395 break; 1396 case 6: 1397 // Reset Origin Mode. 1398 fBuffer->SetOriginMode(false); 1399 break; 1400 case 9: 1401 // Disable Mouse X and Y on button press. 1402 fBuffer->ReportX10MouseEvent(false); 1403 break; 1404 case 12: 1405 // Stop Blinking Cursor. 1406 fBuffer->SetCursorBlinking(false); 1407 break; 1408 case 25: 1409 // Hide Cursor 1410 fBuffer->SetCursorHidden(true); 1411 break; 1412 case 47: 1413 // Use Normal Screen Buffer. 1414 fBuffer->UseNormalScreenBuffer(); 1415 break; 1416 case 1000: 1417 // Don't send Mouse X & Y on button press and release. 1418 fBuffer->ReportNormalMouseEvent(false); 1419 break; 1420 case 1002: 1421 // Don't send Mouse X and Y on button press and release, and on motion 1422 // when the mouse enter a new cell 1423 fBuffer->ReportButtonMouseEvent(false); 1424 break; 1425 case 1003: 1426 // Disable All Motion Mouse Tracking. 1427 fBuffer->ReportAnyMouseEvent(false); 1428 break; 1429 case 1034: 1430 // Don't interprete "meta" key. 1431 // Not supported yet. 1432 break; 1433 case 1036: 1434 // TODO: Don't send ESC when Meta modifies a key 1435 // Not supported yet. 1436 break; 1437 case 1039: 1438 // TODO: Don't send ESC when Alt modifies a key 1439 // Not supported yet. 1440 break; 1441 case 1049: 1442 // Use Normal Screen Buffer and restore cursor as in DECRC. 1443 fBuffer->UseNormalScreenBuffer(); 1444 fBuffer->RestoreCursor(); 1445 break; 1446 } 1447 } 1448 1449 1450 void 1451 TermParse::_ProcessOperatingSystemControls(uchar* params) 1452 { 1453 int mode = 0; 1454 for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) { 1455 mode *= 10; 1456 mode += c - '0'; 1457 } 1458 1459 // eat the separator 1460 if (*params == ';') 1461 params++; 1462 1463 static uint8 indexes[kTermColorCount]; 1464 static rgb_color colors[kTermColorCount]; 1465 1466 switch (mode) { 1467 case 0: // icon name and window title 1468 case 2: // window title 1469 fBuffer->SetTitle((const char*)params); 1470 break; 1471 case 4: // set colors (0 - 255) 1472 case 104: // reset colors (0 - 255) 1473 { 1474 bool reset = (mode / 100) == 1; 1475 1476 // colors can be in "idx1:name1;...;idxN:nameN;" sequence too! 1477 uint32 count = 0; 1478 char* p = strtok((char*)params, ";"); 1479 do { 1480 indexes[count] = atoi(p); 1481 1482 if (!reset) { 1483 p = strtok(NULL, ";"); 1484 if (p == NULL) 1485 break; 1486 1487 if (gXColorsTable.LookUpColor(p, &colors[count]) == B_OK) 1488 count++; 1489 } else 1490 count++; 1491 1492 p = strtok(NULL, ";"); 1493 } while (p != NULL && count < kTermColorCount); 1494 1495 if (count > 0) { 1496 if (!reset) 1497 fBuffer->SetColors(indexes, colors, count); 1498 else 1499 fBuffer->ResetColors(indexes, count); 1500 } 1501 } 1502 break; 1503 // set dynamic colors (10 - 19) 1504 case 10: // text foreground 1505 case 11: // text background 1506 { 1507 int32 offset = mode - 10; 1508 int32 count = 0; 1509 char* p = strtok((char*)params, ";"); 1510 do { 1511 if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) { 1512 // dyna-colors are pos-sensitive - no chance to continue 1513 break; 1514 } 1515 1516 indexes[count] = 10 + offset + count; 1517 count++; 1518 p = strtok(NULL, ";"); 1519 1520 } while (p != NULL && (offset + count) < 10); 1521 1522 if (count > 0) { 1523 fBuffer->SetColors(indexes, colors, count, true); 1524 } 1525 } 1526 break; 1527 // reset dynamic colors (10 - 19) 1528 case 110: // text foreground 1529 case 111: // text background 1530 { 1531 indexes[0] = mode; 1532 fBuffer->ResetColors(indexes, 1, true); 1533 } 1534 break; 1535 default: 1536 // printf("%d -> %s\n", mode, params); 1537 break; 1538 } 1539 } 1540