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 for (uint i = 0; !isParsed && i < sizeof(params); i++) { 1052 params[i] = _NextParseChar(); 1053 switch (params[i]) { 1054 // BEL 1055 case 0x07: 1056 isParsed = true; 1057 break; 1058 // 8-bit ST 1059 case 0x9c: 1060 isParsed = true; 1061 break; 1062 // 7-bit ST is "ESC \" 1063 case '\\': 1064 // hm... Was \x1b replaced by 0 during parsing? 1065 if (i > 0 && params[i - 1] == 0) { 1066 isParsed = true; 1067 break; 1068 } 1069 default: 1070 if (!isprint(params[i] & 0x7f)) 1071 break; 1072 continue; 1073 } 1074 params[i] = '\0'; 1075 } 1076 1077 if (isParsed) 1078 _ProcessOperatingSystemControls(params); 1079 1080 parsestate = groundtable; 1081 break; 1082 } 1083 1084 case CASE_RIS: // ESC c ... Reset terminal. 1085 break; 1086 1087 case CASE_LS2: 1088 /* select G2 into GL */ 1089 curGL = 2; 1090 parsestate = groundtable; 1091 break; 1092 1093 case CASE_LS3: 1094 /* select G3 into GL */ 1095 curGL = 3; 1096 parsestate = groundtable; 1097 break; 1098 1099 case CASE_LS3R: 1100 /* select G3 into GR */ 1101 curGR = 3; 1102 parsestate = groundtable; 1103 break; 1104 1105 case CASE_LS2R: 1106 /* select G2 into GR */ 1107 curGR = 2; 1108 parsestate = groundtable; 1109 break; 1110 1111 case CASE_LS1R: 1112 /* select G1 into GR */ 1113 curGR = 1; 1114 parsestate = groundtable; 1115 break; 1116 1117 case CASE_VPA: // ESC [...d move cursor absolute vertical 1118 /* VPA (CV) */ 1119 if ((row = param[0]) < 1) 1120 row = 1; 1121 1122 // note beterm wants it 1-based unlike usual terminals 1123 fBuffer->SetCursorY(row - 1); 1124 parsestate = groundtable; 1125 break; 1126 1127 case CASE_HPA: // ESC [...G move cursor absolute horizontal 1128 /* HPA (CH) */ 1129 if ((column = param[0]) < 1) 1130 column = 1; 1131 1132 // note beterm wants it 1-based unlike usual terminals 1133 fBuffer->SetCursorX(column - 1); 1134 parsestate = groundtable; 1135 break; 1136 1137 case CASE_SU: // scroll screen up 1138 if ((row = param[0]) < 1) 1139 row = 1; 1140 fBuffer->ScrollBy(row); 1141 parsestate = groundtable; 1142 break; 1143 1144 case CASE_SD: // scroll screen down 1145 if ((row = param[0]) < 1) 1146 row = 1; 1147 fBuffer->ScrollBy(-row); 1148 parsestate = groundtable; 1149 break; 1150 1151 1152 case CASE_ECH: // erase characters 1153 if ((column = param[0]) < 1) 1154 column = 1; 1155 fBuffer->EraseChars(column); 1156 parsestate = groundtable; 1157 break; 1158 1159 default: 1160 break; 1161 } 1162 } catch (...) { 1163 break; 1164 } 1165 } 1166 1167 return B_OK; 1168 } 1169 1170 1171 /*static*/ int32 1172 TermParse::_ptyreader_thread(void *data) 1173 { 1174 return reinterpret_cast<TermParse *>(data)->PtyReader(); 1175 } 1176 1177 1178 /*static*/ int32 1179 TermParse::_escparse_thread(void *data) 1180 { 1181 return reinterpret_cast<TermParse *>(data)->EscParse(); 1182 } 1183 1184 1185 status_t 1186 TermParse::_ReadParserBuffer() 1187 { 1188 // We have to unlock the terminal buffer while waiting for data from the 1189 // PTY. We don't have to unlock when we don't need to wait, but we do it 1190 // anyway, so that TermView won't be starved when trying to synchronize. 1191 fBuffer->Unlock(); 1192 1193 // wait for new input from pty 1194 if (fReadBufferSize == 0) { 1195 status_t status = B_OK; 1196 while (fReadBufferSize == 0 && status == B_OK) { 1197 do { 1198 status = acquire_sem(fReaderSem); 1199 } while (status == B_INTERRUPTED); 1200 1201 // eat any sems that were released unconditionally 1202 int32 semCount; 1203 if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0) 1204 acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0); 1205 } 1206 1207 if (status < B_OK) { 1208 fBuffer->Lock(); 1209 return status; 1210 } 1211 } 1212 1213 int32 toRead = fReadBufferSize; 1214 if (toRead > ESC_PARSER_BUFFER_SIZE) 1215 toRead = ESC_PARSER_BUFFER_SIZE; 1216 1217 for (int32 i = 0; i < toRead; i++) { 1218 // TODO: This could be optimized using memcpy instead and 1219 // calculating space left as in the PtyReader(). 1220 fParserBuffer[i] = fReadBuffer[fBufferPosition]; 1221 fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE; 1222 } 1223 1224 int32 bufferSize = atomic_add(&fReadBufferSize, -toRead); 1225 1226 // If the pty reader thread waits and we have made enough space in the 1227 // buffer now, let it run again. 1228 if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE 1229 && bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) { 1230 release_sem(fReaderLocker); 1231 } 1232 1233 fParserBufferSize = toRead; 1234 fParserBufferOffset = 0; 1235 1236 fBuffer->Lock(); 1237 return B_OK; 1238 } 1239 1240 1241 void 1242 TermParse::_DeviceStatusReport(int n) 1243 { 1244 char sbuf[16] ; 1245 int len; 1246 1247 switch (n) { 1248 case 5: 1249 { 1250 // Device status report requested 1251 // reply with "no malfunction detected" 1252 const char* toWrite = "\033[0n"; 1253 write(fFd, toWrite, strlen(toWrite)); 1254 break ; 1255 } 1256 case 6: 1257 // Cursor position report requested 1258 len = sprintf(sbuf, "\033[%" B_PRId32 ";%" B_PRId32 "R", 1259 fBuffer->Cursor().y + 1, 1260 fBuffer->Cursor().x + 1); 1261 write(fFd, sbuf, len); 1262 break ; 1263 default: 1264 return; 1265 } 1266 } 1267 1268 1269 void 1270 TermParse::_DecReqTermParms(int value) 1271 { 1272 // Terminal parameters report: 1273 // type (2 or 3); 1274 // no parity (1); 1275 // 8 bits per character (1); 1276 // transmit speed 38400bps (128); 1277 // receive speed 38400bps (128); 1278 // bit rate multiplier 16 (1); 1279 // no flags (0) 1280 char parms[] = "\033[?;1;1;128;128;1;0x"; 1281 1282 if (value < 1) 1283 parms[2] = '2'; 1284 else if (value == 1) 1285 parms[2] = '3'; 1286 else 1287 return; 1288 1289 write(fFd, parms, strlen(parms)); 1290 } 1291 1292 1293 void 1294 TermParse::_DecPrivateModeSet(int value) 1295 { 1296 switch (value) { 1297 case 1: 1298 // Application Cursor Keys (whatever that means). 1299 // Not supported yet. 1300 break; 1301 case 5: 1302 // Reverse Video (inverses colors for the complete screen 1303 // -- when followed by normal video, that's shortly flashes the 1304 // screen). 1305 // Not supported yet. 1306 break; 1307 case 6: 1308 // Set Origin Mode. 1309 fBuffer->SetOriginMode(true); 1310 break; 1311 case 9: 1312 // Set Mouse X and Y on button press. 1313 fBuffer->ReportX10MouseEvent(true); 1314 break; 1315 case 12: 1316 // Start Blinking Cursor. 1317 fBuffer->SetCursorBlinking(true); 1318 break; 1319 case 25: 1320 // Show Cursor. 1321 fBuffer->SetCursorHidden(false); 1322 break; 1323 case 47: 1324 // Use Alternate Screen Buffer. 1325 fBuffer->UseAlternateScreenBuffer(false); 1326 break; 1327 case 1000: 1328 // Send Mouse X & Y on button press and release. 1329 fBuffer->ReportNormalMouseEvent(true); 1330 break; 1331 case 1002: 1332 // Send Mouse X and Y on button press and release, and on motion 1333 // when the mouse enter a new cell 1334 fBuffer->ReportButtonMouseEvent(true); 1335 break; 1336 case 1003: 1337 // Use All Motion Mouse Tracking 1338 fBuffer->ReportAnyMouseEvent(true); 1339 break; 1340 case 1034: 1341 // TODO: Interprete "meta" key, sets eighth bit. 1342 // Not supported yet. 1343 break; 1344 case 1036: 1345 // TODO: Send ESC when Meta modifies a key 1346 // Not supported yet. 1347 break; 1348 case 1039: 1349 // TODO: Send ESC when Alt modifies a key 1350 // Not supported yet. 1351 break; 1352 case 1049: 1353 // Save cursor as in DECSC and use Alternate Screen Buffer, clearing 1354 // it first. 1355 fBuffer->SaveCursor(); 1356 fBuffer->UseAlternateScreenBuffer(true); 1357 break; 1358 } 1359 } 1360 1361 1362 void 1363 TermParse::_DecPrivateModeReset(int value) 1364 { 1365 switch (value) { 1366 case 1: 1367 // Normal Cursor Keys (whatever that means). 1368 // Not supported yet. 1369 break; 1370 case 3: 1371 // 80 Column Mode. 1372 // Not supported yet. 1373 break; 1374 case 4: 1375 // Jump (Fast) Scroll. 1376 // Not supported yet. 1377 break; 1378 case 5: 1379 // Normal Video (Leaves Reverse Video, cf. there). 1380 // Not supported yet. 1381 break; 1382 case 6: 1383 // Reset Origin Mode. 1384 fBuffer->SetOriginMode(false); 1385 break; 1386 case 9: 1387 // Disable Mouse X and Y on button press. 1388 fBuffer->ReportX10MouseEvent(false); 1389 break; 1390 case 12: 1391 // Stop Blinking Cursor. 1392 fBuffer->SetCursorBlinking(false); 1393 break; 1394 case 25: 1395 // Hide Cursor 1396 fBuffer->SetCursorHidden(true); 1397 break; 1398 case 47: 1399 // Use Normal Screen Buffer. 1400 fBuffer->UseNormalScreenBuffer(); 1401 break; 1402 case 1000: 1403 // Don't send Mouse X & Y on button press and release. 1404 fBuffer->ReportNormalMouseEvent(false); 1405 break; 1406 case 1002: 1407 // Don't send Mouse X and Y on button press and release, and on motion 1408 // when the mouse enter a new cell 1409 fBuffer->ReportButtonMouseEvent(false); 1410 break; 1411 case 1003: 1412 // Disable All Motion Mouse Tracking. 1413 fBuffer->ReportAnyMouseEvent(false); 1414 break; 1415 case 1034: 1416 // Don't interprete "meta" key. 1417 // Not supported yet. 1418 break; 1419 case 1036: 1420 // TODO: Don't send ESC when Meta modifies a key 1421 // Not supported yet. 1422 break; 1423 case 1039: 1424 // TODO: Don't send ESC when Alt modifies a key 1425 // Not supported yet. 1426 break; 1427 case 1049: 1428 // Use Normal Screen Buffer and restore cursor as in DECRC. 1429 fBuffer->UseNormalScreenBuffer(); 1430 fBuffer->RestoreCursor(); 1431 break; 1432 } 1433 } 1434 1435 1436 void 1437 TermParse::_ProcessOperatingSystemControls(uchar* params) 1438 { 1439 int mode = 0; 1440 for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) { 1441 mode *= 10; 1442 mode += c - '0'; 1443 } 1444 1445 // eat the separator 1446 if (*params == ';') 1447 params++; 1448 1449 static uint8 indexes[kTermColorCount]; 1450 static rgb_color colors[kTermColorCount]; 1451 1452 switch (mode) { 1453 case 0: // icon name and window title 1454 case 2: // window title 1455 fBuffer->SetTitle((const char*)params); 1456 break; 1457 case 4: // set colors (0 - 255) 1458 case 104: // reset colors (0 - 255) 1459 { 1460 bool reset = (mode / 100) == 1; 1461 1462 // colors can be in "idx1:name1;...;idxN:nameN;" sequence too! 1463 uint32 count = 0; 1464 char* p = strtok((char*)params, ";"); 1465 do { 1466 indexes[count] = atoi(p); 1467 1468 if (!reset) { 1469 p = strtok(NULL, ";"); 1470 if (p == NULL) 1471 break; 1472 1473 if (gXColorsTable.LookUpColor(p, &colors[count]) == B_OK) 1474 count++; 1475 } else 1476 count++; 1477 1478 p = strtok(NULL, ";"); 1479 } while (p != NULL && count < kTermColorCount); 1480 1481 if (count > 0) { 1482 if (!reset) 1483 fBuffer->SetColors(indexes, colors, count); 1484 else 1485 fBuffer->ResetColors(indexes, count); 1486 } 1487 } 1488 break; 1489 // set dynamic colors (10 - 19) 1490 case 10: // text foreground 1491 case 11: // text background 1492 { 1493 int32 offset = mode - 10; 1494 int32 count = 0; 1495 char* p = strtok((char*)params, ";"); 1496 do { 1497 if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) { 1498 // dyna-colors are pos-sensitive - no chance to continue 1499 break; 1500 } 1501 1502 indexes[count] = 10 + offset + count; 1503 count++; 1504 p = strtok(NULL, ";"); 1505 1506 } while (p != NULL && (offset + count) < 10); 1507 1508 if (count > 0) { 1509 fBuffer->SetColors(indexes, colors, count, true); 1510 } 1511 } 1512 break; 1513 // reset dynamic colors (10 - 19) 1514 case 110: // text foreground 1515 case 111: // text background 1516 { 1517 indexes[0] = mode; 1518 fBuffer->ResetColors(indexes, 1, true); 1519 } 1520 break; 1521 default: 1522 // printf("%d -> %s\n", mode, params); 1523 break; 1524 } 1525 } 1526