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