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