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