1 /* 2 * Copyright 2001-2007, 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 #include "TermParse.h" 8 9 #include "CodeConv.h" 10 #include "TermConst.h" 11 #include "TermView.h" 12 #include "VTparse.h" 13 14 #include <ctype.h> 15 #include <errno.h> 16 #include <stdio.h> 17 #include <signal.h> 18 #include <unistd.h> 19 20 #include <Beep.h> 21 #include <Message.h> 22 23 24 ////////////////////////////////////////////////////////////////////////////// 25 // EscParse ... Escape sequence parse and character encoding. 26 // 27 ////////////////////////////////////////////////////////////////////////////// 28 29 30 extern int utf8_groundtable[]; /* UTF8 Ground table */ 31 extern int cs96_groundtable[]; /* CS96 Ground table */ 32 extern int iso8859_groundtable[]; /* ISO8859 & EUC Ground table */ 33 extern int sjis_groundtable[]; /* Shift-JIS Ground table */ 34 35 extern int esctable[]; /* ESC */ 36 extern int csitable[]; /* ESC [ */ 37 extern int dectable[]; /* ESC [ ? */ 38 extern int scrtable[]; /* ESC # */ 39 extern int igntable[]; /* ignore table */ 40 extern int iestable[]; /* ignore ESC table */ 41 extern int eigtable[]; /* ESC ignore table */ 42 extern int mbcstable[]; /* ESC $ */ 43 44 45 46 #define DEFAULT -1 47 #define NPARAM 10 // Max parameters 48 49 50 TermParse::TermParse(int fd) 51 : 52 fFd(fd), 53 fParseThread(-1), 54 fReaderThread(-1), 55 fReaderSem(-1), 56 fReaderLocker(-1), 57 fBufferPosition(0), 58 fLockFlag(0), 59 fView(NULL), 60 fQuitting(true) 61 { 62 } 63 64 65 TermParse::~TermParse() 66 { 67 StopThreads(); 68 } 69 70 71 status_t 72 TermParse::StartThreads(TermView *view) 73 { 74 if (fView != NULL) 75 return B_ERROR; 76 77 fQuitting = false; 78 fView = view; 79 80 status_t status = InitPtyReader(); 81 if (status < B_OK) { 82 fView = NULL; 83 return status; 84 } 85 86 status = InitTermParse(); 87 if (status < B_OK) { 88 StopPtyReader(); 89 fView = NULL; 90 return status; 91 } 92 93 return B_OK; 94 } 95 96 97 status_t 98 TermParse::StopThreads() 99 { 100 if (fView == NULL) 101 return B_ERROR; 102 103 fQuitting = true; 104 105 StopPtyReader(); 106 StopTermParse(); 107 108 fView = NULL; 109 110 return B_OK; 111 } 112 113 114 //! Get char from pty reader buffer. 115 status_t 116 TermParse::GetReaderBuf(uchar &c) 117 { 118 status_t status; 119 do { 120 status = acquire_sem_etc(fReaderSem, 1, B_TIMEOUT, 10000); 121 } while (status == B_INTERRUPTED); 122 123 if (status == B_TIMED_OUT) { 124 fView->ScrollAtCursor(); 125 fView->UpdateLine(); 126 127 // Reset cursor blinking time and turn on cursor blinking. 128 fView->SetCurDraw(true); 129 130 // wait new input from pty. 131 do { 132 status = acquire_sem(fReaderSem); 133 } while (status == B_INTERRUPTED); 134 if (status < B_OK) 135 return status; 136 } else if (status == B_OK) { 137 // Do nothing 138 } else 139 return status; 140 141 c = fReadBuffer[fBufferPosition % READ_BUF_SIZE]; 142 fBufferPosition++; 143 // If PtyReader thread locked, decrement counter and unlock thread. 144 if (fLockFlag != 0) { 145 if (--fLockFlag == 0) 146 release_sem(fReaderLocker); 147 } 148 149 fView->SetCurDraw(false); 150 return B_OK; 151 } 152 153 154 //! Initialize and spawn EscParse thread. 155 status_t 156 TermParse::InitTermParse() 157 { 158 if (fParseThread >= 0) 159 return B_ERROR; // we might want to return B_OK instead ? 160 161 fParseThread = spawn_thread(_escparse_thread, "EscParse", 162 B_DISPLAY_PRIORITY, this); 163 164 resume_thread(fParseThread); 165 166 return B_OK; 167 } 168 169 170 //! Initialize and spawn PtyReader thread. 171 status_t 172 TermParse::InitPtyReader() 173 { 174 if (fReaderThread >= 0) 175 return B_ERROR; // same as above 176 177 fReaderSem = create_sem(0, "pty_reader_sem"); 178 if (fReaderSem < 0) 179 return fReaderSem; 180 181 fReaderLocker = create_sem(0, "pty_locker_sem"); 182 if (fReaderLocker < 0) { 183 delete_sem(fReaderSem); 184 fReaderSem = -1; 185 return fReaderLocker; 186 } 187 188 fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader", 189 B_NORMAL_PRIORITY, this); 190 if (fReaderThread < 0) { 191 delete_sem(fReaderSem); 192 fReaderSem = -1; 193 delete_sem(fReaderLocker); 194 fReaderLocker = -1; 195 return fReaderThread; 196 } 197 198 resume_thread(fReaderThread); 199 200 return B_OK; 201 } 202 203 204 void 205 TermParse::StopTermParse() 206 { 207 if (fParseThread >= 0) { 208 status_t dummy; 209 wait_for_thread(fParseThread, &dummy); 210 fParseThread = -1; 211 } 212 } 213 214 215 void 216 TermParse::StopPtyReader() 217 { 218 if (fReaderSem >= 0) { 219 delete_sem(fReaderSem); 220 fReaderSem = -1; 221 } 222 if (fReaderLocker >= 0) { 223 delete_sem(fReaderLocker); 224 fReaderLocker = -1; 225 } 226 227 if (fReaderThread >= 0) { 228 suspend_thread(fReaderThread); 229 230 status_t status; 231 wait_for_thread(fReaderThread, &status); 232 233 fReaderThread = -1; 234 } 235 } 236 237 238 //! Get data from pty device. 239 int32 240 TermParse::PtyReader() 241 { 242 uint read_p = 0; 243 while (!fQuitting) { 244 // If Pty Buffer nearly full, snooze this thread, and continue. 245 if ((read_p - fBufferPosition) > READ_BUF_SIZE - 16) { 246 fLockFlag = READ_BUF_SIZE / 2; 247 status_t status; 248 do { 249 status = acquire_sem(fReaderLocker); 250 } while (status == B_INTERRUPTED); 251 if (status < B_OK) 252 return status; 253 } 254 255 // Read PTY 256 uchar buf[READ_BUF_SIZE]; 257 int nread = read(fFd, buf, READ_BUF_SIZE - (read_p - fBufferPosition)); 258 if (nread <= 0) { 259 fView->NotifyQuit(errno); 260 return B_OK; 261 } 262 263 // Copy read string to PtyBuffer. 264 265 int left = READ_BUF_SIZE - (read_p % READ_BUF_SIZE); 266 int mod = read_p % READ_BUF_SIZE; 267 268 if (nread >= left) { 269 memcpy(fReadBuffer + mod, buf, left); 270 memcpy(fReadBuffer, buf + left, nread - left); 271 } else 272 memcpy(fReadBuffer + mod, buf, nread); 273 274 read_p += nread; 275 276 // Release semaphore. Number of semaphore counter is nread. 277 release_sem_etc(fReaderSem, nread, 0); 278 } 279 280 return B_OK; 281 } 282 283 284 int32 285 TermParse::EscParse() 286 { 287 int tmp; 288 int top, bot; 289 int cs96; 290 uchar curess = 0; 291 292 uchar cbuf[4], dstbuf[4]; 293 uchar *ptr; 294 295 int now_coding = -1; 296 297 ushort attr = BACKCOLOR; 298 299 int param[NPARAM]; 300 int nparam = 1; 301 302 int row, col; 303 304 /* default coding system is UTF8 */ 305 int *groundtable = utf8_groundtable; 306 int *parsestate = groundtable; 307 308 while (!fQuitting) { 309 uchar c; 310 if (GetReaderBuf(c) < B_OK) 311 break; 312 313 if (now_coding != fView->Encoding()) { 314 /* 315 * Change coding, change parse table. 316 */ 317 switch (fView->Encoding()) { 318 case B_ISO1_CONVERSION: 319 case B_ISO2_CONVERSION: 320 case B_ISO3_CONVERSION: 321 case B_ISO4_CONVERSION: 322 case B_ISO5_CONVERSION: 323 case B_ISO6_CONVERSION: 324 case B_ISO7_CONVERSION: 325 case B_ISO8_CONVERSION: 326 case B_ISO9_CONVERSION: 327 case B_ISO10_CONVERSION: 328 groundtable = iso8859_groundtable; 329 break; 330 case B_SJIS_CONVERSION: 331 groundtable = sjis_groundtable; 332 break; 333 case B_EUC_CONVERSION: 334 case B_EUC_KR_CONVERSION: 335 case B_JIS_CONVERSION: 336 groundtable = iso8859_groundtable; 337 break; 338 case M_UTF8: 339 default: 340 groundtable = utf8_groundtable; 341 break; 342 } 343 parsestate = groundtable; 344 now_coding = fView->Encoding(); 345 } 346 347 switch (parsestate[c]) { 348 case CASE_PRINT: 349 cbuf[0] = c; 350 cbuf[1] = '\0'; 351 fView->Insert(cbuf, attr); 352 break; 353 354 case CASE_PRINT_GR: 355 /* case iso8859 gr character, or euc */ 356 ptr = cbuf; 357 if (now_coding == B_EUC_CONVERSION || now_coding == B_EUC_KR_CONVERSION 358 || now_coding == B_JIS_CONVERSION) { 359 switch (parsestate[curess]) { 360 case CASE_SS2: /* JIS X 0201 */ 361 *ptr++ = curess; 362 *ptr++ = c; 363 *ptr = 0; 364 curess = 0; 365 break; 366 367 case CASE_SS3: /* JIS X 0212 */ 368 *ptr++ = curess; 369 *ptr++ = c; 370 GetReaderBuf(*ptr++); 371 *ptr = 0; 372 curess = 0; 373 break; 374 375 default: /* JIS X 0208 */ 376 *ptr++ = c; 377 GetReaderBuf(*ptr++); 378 *ptr = 0; 379 break; 380 } 381 } else { 382 /* ISO-8859-1...10 and MacRoman */ 383 *ptr++ = c; 384 *ptr = 0; 385 } 386 387 if (now_coding != B_JIS_CONVERSION) 388 CodeConv::ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, now_coding); 389 else 390 CodeConv::ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, B_EUC_CONVERSION); 391 392 fView->Insert(dstbuf, attr); 393 break; 394 395 case CASE_PRINT_CS96: 396 cbuf[0] = c | 0x80; 397 GetReaderBuf(cbuf[1]); 398 cbuf[1] |= 0x80; 399 cbuf[2] = 0; 400 CodeConv::ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, B_EUC_CONVERSION); 401 fView->Insert(dstbuf, attr); 402 break; 403 404 case CASE_LF: 405 fView->InsertLF(); 406 break; 407 408 case CASE_CR: 409 fView->InsertCR(); 410 break; 411 412 case CASE_SJIS_KANA: 413 cbuf[0] = (uchar)c; 414 cbuf[1] = '\0'; 415 CodeConv::ConvertToInternal((char*)cbuf, 1, (char*)dstbuf, now_coding); 416 fView->Insert(dstbuf, attr); 417 break; 418 419 case CASE_SJIS_INSTRING: 420 cbuf[0] = (uchar)c; 421 GetReaderBuf(cbuf[1]); 422 cbuf[2] = '\0'; 423 CodeConv::ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, now_coding); 424 fView->Insert(dstbuf, attr); 425 break; 426 427 case CASE_UTF8_2BYTE: 428 cbuf[0] = (uchar)c; 429 GetReaderBuf(c); 430 if (groundtable[c] != CASE_UTF8_INSTRING) 431 break; 432 cbuf[1] = (uchar)c; 433 cbuf[2] = '\0'; 434 435 fView->Insert(cbuf, attr); 436 break; 437 438 case CASE_UTF8_3BYTE: 439 cbuf[0] = c; 440 GetReaderBuf(c); 441 if (groundtable[c] != CASE_UTF8_INSTRING) 442 break; 443 cbuf[1] = c; 444 445 GetReaderBuf(c); 446 if (groundtable[c] != CASE_UTF8_INSTRING) 447 break; 448 cbuf[2] = c; 449 cbuf[3] = '\0'; 450 fView->Insert(cbuf, attr); 451 break; 452 453 case CASE_MBCS: 454 /* ESC $ */ 455 parsestate = mbcstable; 456 break; 457 458 case CASE_GSETS: 459 /* ESC $ ? */ 460 parsestate = cs96_groundtable; 461 cs96 = 1; 462 break; 463 464 case CASE_SCS_STATE: 465 { 466 cs96 = 0; 467 uchar dummy; 468 GetReaderBuf(dummy); 469 parsestate = groundtable; 470 break; 471 } 472 case CASE_GROUND_STATE: 473 /* exit ignore mode */ 474 parsestate = groundtable; 475 break; 476 477 case CASE_BELL: 478 beep(); 479 break; 480 481 case CASE_BS: 482 fView->MoveCurLeft(1); 483 break; 484 485 case CASE_TAB: 486 tmp = fView->GetCurX(); 487 tmp %= 8; 488 fView->MoveCurRight(8 - tmp); 489 break; 490 491 case CASE_ESC: 492 /* escape */ 493 parsestate = esctable; 494 break; 495 496 case CASE_IGNORE_STATE: 497 /* Ies: ignore anything else */ 498 parsestate = igntable; 499 break; 500 501 case CASE_IGNORE_ESC: 502 /* Ign: escape */ 503 parsestate = iestable; 504 break; 505 506 case CASE_IGNORE: 507 /* Ignore character */ 508 break; 509 510 case CASE_SI: 511 break; 512 513 case CASE_SO: 514 break; 515 516 case CASE_SCR_STATE: // ESC # 517 /* enter scr state */ 518 parsestate = scrtable; 519 break; 520 521 case CASE_ESC_IGNORE: 522 /* unknown escape sequence */ 523 parsestate = eigtable; 524 break; 525 526 case CASE_ESC_DIGIT: // ESC [ number 527 /* digit in csi or dec mode */ 528 if ((row = param[nparam - 1]) == DEFAULT) 529 row = 0; 530 param[nparam - 1] = 10 * row + (c - '0'); 531 break; 532 533 case CASE_ESC_SEMI: // ESC ; 534 /* semicolon in csi or dec mode */ 535 if (nparam < NPARAM) 536 param[nparam++] = DEFAULT; 537 break; 538 539 case CASE_DEC_STATE: 540 /* enter dec mode */ 541 parsestate = dectable; 542 break; 543 544 case CASE_ICH: // ESC [@ insert charactor 545 /* ICH */ 546 if ((row = param[0]) < 1) 547 row = 1; 548 fView->InsertSpace(row); 549 parsestate = groundtable; 550 break; 551 552 case CASE_CUU: // ESC [A cursor up, up arrow key. 553 /* CUU */ 554 if ((row = param[0]) < 1) 555 row = 1; 556 fView->MoveCurUp(row); 557 parsestate = groundtable; 558 break; 559 560 case CASE_CUD: // ESC [B cursor down, down arrow key. 561 /* CUD */ 562 if ((row = param[0]) < 1) 563 row = 1; 564 fView->MoveCurDown(row); 565 parsestate = groundtable; 566 break; 567 568 case CASE_CUF: // ESC [C cursor forword 569 /* CUF */ 570 if ((row = param[0]) < 1) 571 row = 1; 572 fView->MoveCurRight(row); 573 parsestate = groundtable; 574 break; 575 576 case CASE_CUB: // ESC [D cursor backword 577 /* CUB */ 578 if ((row = param[0]) < 1) 579 row = 1; 580 fView->MoveCurLeft(row); 581 parsestate = groundtable; 582 break; 583 584 case CASE_CUP: // ESC [...H move cursor 585 /* CUP | HVP */ 586 if ((row = param[0]) < 1) 587 row = 1; 588 if (nparam < 2 || (col = param[1]) < 1) 589 col = 1; 590 591 fView->SetCurPos(col - 1, row - 1 ); 592 parsestate = groundtable; 593 break; 594 595 case CASE_ED: // ESC [ ...J clear screen 596 /* ED */ 597 switch (param[0]) { 598 case DEFAULT: 599 case 0: 600 fView->EraseBelow(); 601 break; 602 603 case 1: 604 break; 605 606 case 2: 607 fView->SetCurPos(0, 0); 608 fView->EraseBelow(); 609 break; 610 } 611 parsestate = groundtable; 612 break; 613 614 case CASE_EL: // delete line 615 /* EL */ 616 fView->DeleteColumns(); 617 parsestate = groundtable; 618 break; 619 620 case CASE_IL: 621 /* IL */ 622 if ((row = param[0]) < 1) 623 row = 1; 624 fView->InsertNewLine(row); 625 parsestate = groundtable; 626 break; 627 628 case CASE_DL: 629 /* DL */ 630 if ((row = param[0]) < 1) 631 row = 1; 632 fView->DeleteLine(row); 633 parsestate = groundtable; 634 break; 635 636 case CASE_DCH: 637 /* DCH */ 638 if ((row = param[0]) < 1) 639 row = 1; 640 fView->DeleteChar(row); 641 parsestate = groundtable; 642 break; 643 644 case CASE_SET: 645 /* SET */ 646 fView->SetInsertMode(MODE_INSERT); 647 parsestate = groundtable; 648 break; 649 650 case CASE_RST: 651 /* RST */ 652 fView->SetInsertMode(MODE_OVER); 653 parsestate = groundtable; 654 break; 655 656 case CASE_SGR: 657 /* SGR */ 658 for (row = 0; row < nparam; ++row) { 659 switch (param[row]) { 660 case DEFAULT: 661 case 0: /* Reset attribute */ 662 attr = 0; 663 break; 664 665 case 1: 666 case 5: /* Bold */ 667 attr |= BOLD; 668 break; 669 670 case 4: /* Underline */ 671 attr |= UNDERLINE; 672 break; 673 674 case 7: /* Inverse */ 675 attr |= INVERSE; 676 break; 677 678 case 30: 679 case 31: 680 case 32: 681 case 33: 682 case 34: 683 case 35: 684 case 36: 685 case 37: 686 attr &= ~FORECOLOR; 687 attr |= FORECOLORED(param[row] - 30); 688 attr |= FORESET; 689 break; 690 691 case 39: 692 attr &= ~FORESET; 693 break; 694 695 case 40: 696 case 41: 697 case 42: 698 case 43: 699 case 44: 700 case 45: 701 case 46: 702 case 47: 703 attr &= ~BACKCOLOR; 704 attr |= BACKCOLORED(param[row] - 40); 705 attr |= BACKSET; 706 break; 707 708 case 49: 709 attr &= ~BACKSET; 710 break; 711 } 712 } 713 parsestate = groundtable; 714 break; 715 716 case CASE_CPR: 717 // Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp) 718 // 21-JUL-99 719 fView->DeviceStatusReport(param[0]); 720 parsestate = groundtable; 721 break; 722 723 case CASE_DECSTBM: 724 /* DECSTBM - set scrolling region */ 725 726 if ((top = param[0]) < 1) 727 top = 1; 728 729 if (nparam < 2) 730 bot = -1; 731 else 732 bot = param[1]; 733 734 top--; 735 bot--; 736 737 if (bot > top) 738 fView->SetScrollRegion(top, bot); 739 740 parsestate = groundtable; 741 break; 742 743 case CASE_DECREQTPARM: 744 parsestate = groundtable; 745 break; 746 747 case CASE_DECSET: 748 /* DECSET */ 749 // dpmodes(term, bitset); 750 parsestate = groundtable; 751 break; 752 753 case CASE_DECRST: 754 /* DECRST */ 755 // dpmodes(term, bitclr); 756 parsestate = groundtable; 757 break; 758 759 case CASE_DECALN: 760 /* DECALN */ 761 // if(screen->cursor_state) 762 // HideCursor(); 763 // ScrnRefresh(screen, 0, 0, screen->max_row + 1, 764 // screen->max_col + 1, False); 765 parsestate = groundtable; 766 break; 767 768 // case CASE_GSETS: 769 // screen->gsets[scstype] = GSET(c) | cs96; 770 // parsestate = groundtable; 771 // break; 772 773 case CASE_DECSC: 774 /* DECSC */ 775 fView->SaveCursor(); 776 parsestate = groundtable; 777 break; 778 779 case CASE_DECRC: 780 /* DECRC */ 781 fView->RestoreCursor(); 782 parsestate = groundtable; 783 break; 784 785 case CASE_HTS: 786 /* HTS */ 787 // TabSet(term->tabs, screen->cur_col); 788 parsestate = groundtable; 789 break; 790 791 case CASE_RI: 792 /* RI */ 793 fView->ScrollRegion(-1, -1, SCRDOWN, 1); 794 parsestate = groundtable; 795 break; 796 797 case CASE_SS2: 798 /* SS2 */ 799 curess = c; 800 parsestate = groundtable; 801 break; 802 803 case CASE_SS3: 804 /* SS3 */ 805 curess = c; 806 parsestate = groundtable; 807 break; 808 809 case CASE_CSI_STATE: 810 /* enter csi state */ 811 nparam = 1; 812 param[0] = DEFAULT; 813 parsestate = csitable; 814 break; 815 816 case CASE_OSC: 817 { 818 /* Operating System Command: ESC ] */ 819 char string[512]; 820 uint32 len = 0; 821 uchar mode_char; 822 GetReaderBuf(mode_char); 823 if (mode_char != '0' 824 && mode_char != '1' 825 && mode_char != '2') { 826 parsestate = groundtable; 827 break; 828 } 829 uchar current_char; 830 GetReaderBuf(current_char); 831 while (GetReaderBuf(current_char) == B_OK 832 && current_char != 0x7) { 833 if (!isprint(current_char & 0x7f) 834 || len+2 >= sizeof(string)) 835 break; 836 string[len++] = current_char; 837 } 838 if (current_char == 0x7) { 839 string[len] = '\0'; 840 switch (mode_char) { 841 case '0': 842 case '2': 843 fView->SetTitle(string); 844 break; 845 case '1': 846 break; 847 } 848 } 849 parsestate = groundtable; 850 break; 851 } 852 853 case CASE_RIS: // ESC c ... Reset terminal. 854 break; 855 856 case CASE_LS2: 857 /* LS2 */ 858 // screen->curgl = 2; 859 parsestate = groundtable; 860 break; 861 862 case CASE_LS3: 863 /* LS3 */ 864 // screen->curgl = 3; 865 parsestate = groundtable; 866 break; 867 868 case CASE_LS3R: 869 /* LS3R */ 870 // screen->curgr = 3; 871 parsestate = groundtable; 872 break; 873 874 case CASE_LS2R: 875 /* LS2R */ 876 // screen->curgr = 2; 877 parsestate = groundtable; 878 break; 879 880 case CASE_LS1R: 881 /* LS1R */ 882 // screen->curgr = 1; 883 parsestate = groundtable; 884 break; 885 886 default: 887 break; 888 } 889 } 890 891 return B_OK; 892 } 893 894 895 /*static*/ int32 896 TermParse::_ptyreader_thread(void *data) 897 { 898 return reinterpret_cast<TermParse *>(data)->PtyReader(); 899 } 900 901 902 /*static*/ int32 903 TermParse::_escparse_thread(void *data) 904 { 905 return reinterpret_cast<TermParse *>(data)->EscParse(); 906 } 907