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