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