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