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