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