xref: /haiku/src/apps/terminal/TermParse.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
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 gUTF8GroundTable[];		/* UTF8 Ground table */
31 extern int gCS96GroundTable[];		/* CS96 Ground table */
32 extern int gISO8859GroundTable[];	/* ISO8859 & EUC Ground table */
33 extern int gSJISGroundTable[];		/* Shift-JIS Ground table */
34 
35 extern int gEscTable[];		/* ESC */
36 extern int gCsiTable[];		/* ESC [ */
37 extern int gDecTable[];		/* ESC [ ? */
38 extern int gScrTable[];		/* ESC # */
39 extern int gIgnoreTable[];		/* ignore table */
40 extern int gIesTable[];		/* ignore ESC table */
41 extern int gEscIgnoreTable[];		/* ESC ignore table */
42 extern int gMbcsTable[];		/* 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 void
285 TermParse::DumpState(int *groundtable, int *parsestate, uchar c)
286 {
287 	static const struct {
288 		int *p;
289 		const char *name;
290 	} tables[] = {
291 #define T(t) \
292 	{ t, #t }
293 	T(gUTF8GroundTable),
294 	T(gCS96GroundTable),
295 	T(gISO8859GroundTable),
296 	T(gSJISGroundTable),
297 	T(gEscTable),
298 	T(gCsiTable),
299 	T(gDecTable),
300 	T(gScrTable),
301 	T(gIgnoreTable),
302 	T(gIesTable),
303 	T(gEscIgnoreTable),
304 	T(gMbcsTable),
305 	{ NULL, NULL }
306 	};
307 	int i;
308 	fprintf(stderr, "groundtable: ");
309 	for (i = 0; tables[i].p; i++)
310 		if (tables[i].p == groundtable)
311 			fprintf(stderr, "%s\t", tables[i].name);
312 	fprintf(stderr, "parsestate: ");
313 	for (i = 0; tables[i].p; i++)
314 		if (tables[i].p == parsestate)
315 			fprintf(stderr, "%s\t", tables[i].name);
316 	fprintf(stderr, "char: 0x%02x (%d)\n", c, c);
317 }
318 
319 
320 int32
321 TermParse::EscParse()
322 {
323 	int tmp;
324 	int top, bot;
325 	int cs96 = 0;
326 	uchar curess = 0;
327 
328 	uchar cbuf[4], dstbuf[4];
329 	uchar *ptr;
330 
331 	int now_coding = -1;
332 
333 	ushort attr = BACKCOLOR;
334 
335 	int param[NPARAM];
336 	int nparam = 1;
337 
338 	int row, col;
339 
340 	/* default coding system is UTF8 */
341 	int *groundtable = gUTF8GroundTable;
342 	int *parsestate = groundtable;
343 
344 	while (!fQuitting) {
345 		uchar c;
346 		if (GetReaderBuf(c) < B_OK)
347 			break;
348 
349 		//DumpState(groundtable, parsestate, c);
350 
351 		if (now_coding != fView->Encoding()) {
352 			/*
353 			 * Change coding, change parse table.
354 			 */
355 			switch (fView->Encoding()) {
356 				case B_ISO1_CONVERSION:
357 				case B_ISO2_CONVERSION:
358 				case B_ISO3_CONVERSION:
359 				case B_ISO4_CONVERSION:
360 				case B_ISO5_CONVERSION:
361 				case B_ISO6_CONVERSION:
362 				case B_ISO7_CONVERSION:
363 				case B_ISO8_CONVERSION:
364 				case B_ISO9_CONVERSION:
365 				case B_ISO10_CONVERSION:
366 					groundtable = gISO8859GroundTable;
367 					break;
368 				case B_SJIS_CONVERSION:
369 					groundtable = gSJISGroundTable;
370 					break;
371 				case B_EUC_CONVERSION:
372 				case B_EUC_KR_CONVERSION:
373 				case B_JIS_CONVERSION:
374 					groundtable = gISO8859GroundTable;
375 					break;
376 				case M_UTF8:
377 				default:
378 					groundtable = gUTF8GroundTable;
379 					break;
380 			}
381 			parsestate = groundtable;
382 			now_coding = fView->Encoding();
383     	}
384 
385 		switch (parsestate[c]) {
386 			case CASE_PRINT:
387 				cbuf[0] = c;
388 				cbuf[1] = '\0';
389 				fView->Insert(cbuf, attr);
390 				break;
391 
392 			case CASE_PRINT_GR:
393 				/* case iso8859 gr character, or euc */
394 				ptr = cbuf;
395 				if (now_coding == B_EUC_CONVERSION || now_coding == B_EUC_KR_CONVERSION
396 					|| now_coding == B_JIS_CONVERSION) {
397 					switch (parsestate[curess]) {
398 						case CASE_SS2:		/* JIS X 0201 */
399 							*ptr++ = curess;
400 							*ptr++ = c;
401 							*ptr = 0;
402 							curess = 0;
403 							break;
404 
405 						case CASE_SS3:		/* JIS X 0212 */
406 							*ptr++ = curess;
407 							*ptr++ = c;
408 							GetReaderBuf(*ptr++);
409 							*ptr = 0;
410 							curess = 0;
411 							break;
412 
413 						default:		/* JIS X 0208 */
414 							*ptr++ = c;
415 							GetReaderBuf(*ptr++);
416 							*ptr = 0;
417 							break;
418 					}
419 				} else {
420 					/* ISO-8859-1...10 and MacRoman */
421 					*ptr++ = c;
422 					*ptr = 0;
423 				}
424 
425 				if (now_coding != B_JIS_CONVERSION)
426 					CodeConv::ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, now_coding);
427 				else
428 					CodeConv::ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, B_EUC_CONVERSION);
429 
430 				fView->Insert(dstbuf, attr);
431 				break;
432 
433 			case CASE_PRINT_CS96:
434 				cbuf[0] = c | 0x80;
435 				GetReaderBuf(cbuf[1]);
436 				cbuf[1] |= 0x80;
437 				cbuf[2] = 0;
438 				CodeConv::ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, B_EUC_CONVERSION);
439 				fView->Insert(dstbuf, attr);
440 				break;
441 
442 			case CASE_LF:
443 				fView->InsertLF();
444 				break;
445 
446 			case CASE_CR:
447 				fView->InsertCR();
448 				break;
449 
450 			case CASE_SJIS_KANA:
451 				cbuf[0] = (uchar)c;
452 				cbuf[1] = '\0';
453 				CodeConv::ConvertToInternal((char*)cbuf, 1, (char*)dstbuf, now_coding);
454 				fView->Insert(dstbuf, attr);
455 				break;
456 
457 			case CASE_SJIS_INSTRING:
458 				cbuf[0] = (uchar)c;
459 				GetReaderBuf(cbuf[1]);
460 				cbuf[2] = '\0';
461 				CodeConv::ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, now_coding);
462 				fView->Insert(dstbuf, attr);
463 				break;
464 
465 			case CASE_UTF8_2BYTE:
466 				cbuf[0] = (uchar)c;
467 				GetReaderBuf(c);
468 				if (groundtable[c] != CASE_UTF8_INSTRING)
469 					break;
470 				cbuf[1] = (uchar)c;
471 				cbuf[2] = '\0';
472 
473 				fView->Insert(cbuf, attr);
474 				break;
475 
476 			case CASE_UTF8_3BYTE:
477 				cbuf[0] = c;
478 				GetReaderBuf(c);
479 				if (groundtable[c] != CASE_UTF8_INSTRING)
480 					break;
481 				cbuf[1] = c;
482 
483 				GetReaderBuf(c);
484 				if (groundtable[c] != CASE_UTF8_INSTRING)
485 					break;
486 				cbuf[2] = c;
487 				cbuf[3] = '\0';
488 				fView->Insert(cbuf, attr);
489 				break;
490 
491 			case CASE_MBCS:
492 				/* ESC $ */
493 				parsestate = gMbcsTable;
494 				break;
495 
496 			case CASE_GSETS:
497 				/* ESC $ ? */
498 				parsestate = gCS96GroundTable;
499 				cs96 = 1;
500 				break;
501 
502 			case CASE_SCS_STATE:
503 			{
504 				cs96 = 0;
505 				uchar dummy;
506 				GetReaderBuf(dummy);
507 				parsestate = groundtable;
508 				break;
509 			}
510 			case CASE_GROUND_STATE:
511 				/* exit ignore mode */
512 				parsestate = groundtable;
513 				break;
514 
515 			case CASE_BELL:
516 				beep();
517 				break;
518 
519 			case CASE_BS:
520 				fView->MoveCurLeft(1);
521 				break;
522 
523 			case CASE_TAB:
524 				tmp = fView->GetCurX();
525 				tmp %= 8;
526 				fView->MoveCurRight(8 - tmp);
527 				break;
528 
529 			case CASE_ESC:
530 				/* escape */
531 				parsestate = gEscTable;
532 				break;
533 
534 			case CASE_IGNORE_STATE:
535 				/* Ies: ignore anything else */
536 				parsestate = gIgnoreTable;
537 				break;
538 
539 			case CASE_IGNORE_ESC:
540 				/* Ign: escape */
541 				parsestate = gIesTable;
542 				break;
543 
544 			case CASE_IGNORE:
545 				/* Ignore character */
546 				break;
547 
548 			case CASE_SI:
549 				break;
550 
551 			case CASE_SO:
552 				break;
553 
554 			case CASE_SCR_STATE:	// ESC #
555 				/* enter scr state */
556 				parsestate = gScrTable;
557 				break;
558 
559 			case CASE_ESC_IGNORE:
560 				/* unknown escape sequence */
561 				parsestate = gEscIgnoreTable;
562 				break;
563 
564 			case CASE_ESC_DIGIT:	// ESC [ number
565 				/* digit in csi or dec mode */
566 				if ((row = param[nparam - 1]) == DEFAULT)
567 					row = 0;
568 				param[nparam - 1] = 10 * row + (c - '0');
569 				break;
570 
571 			case CASE_ESC_SEMI:		// ESC ;
572 				/* semicolon in csi or dec mode */
573 				if (nparam < NPARAM)
574 					param[nparam++] = DEFAULT;
575 				break;
576 
577 			case CASE_DEC_STATE:
578 				/* enter dec mode */
579 				parsestate = gDecTable;
580 				break;
581 
582 			case CASE_ICH:		// ESC [@ insert charactor
583 				/* ICH */
584 				if ((row = param[0]) < 1)
585 					row = 1;
586 				fView->InsertSpace(row);
587 				parsestate = groundtable;
588 				break;
589 
590 			case CASE_CUU:		// ESC [A cursor up, up arrow key.
591 				/* CUU */
592 				if ((row = param[0]) < 1)
593 					row = 1;
594 				fView->MoveCurUp(row);
595 				parsestate = groundtable;
596 				break;
597 
598 			case CASE_CUD:		// ESC [B cursor down, down arrow key.
599 				/* CUD */
600 				if ((row = param[0]) < 1)
601 					row = 1;
602 				fView->MoveCurDown(row);
603 				parsestate = groundtable;
604 				break;
605 
606 			case CASE_CUF:		// ESC [C cursor forword
607 				/* CUF */
608 				if ((row = param[0]) < 1)
609 					row = 1;
610 				fView->MoveCurRight(row);
611 				parsestate = groundtable;
612 				break;
613 
614 			case CASE_CUB:		// ESC [D cursor backword
615 				/* CUB */
616 				if ((row = param[0]) < 1)
617 					row = 1;
618 				fView->MoveCurLeft(row);
619 				parsestate = groundtable;
620 				break;
621 
622 			case CASE_CUP:		// ESC [...H move cursor
623 				/* CUP | HVP */
624 				if ((row = param[0]) < 1)
625 					row = 1;
626 				if (nparam < 2 || (col = param[1]) < 1)
627 					col = 1;
628 
629 				fView->SetCurPos(col - 1, row - 1 );
630 				parsestate = groundtable;
631 				break;
632 
633 			case CASE_ED:		// ESC [ ...J clear screen
634 				/* ED */
635 				switch (param[0]) {
636 					case DEFAULT:
637 					case 0:
638 						fView->EraseBelow();
639 						break;
640 
641 					case 1:
642 						break;
643 
644 					case 2:
645 						fView->SetCurPos(0, 0);
646 						fView->EraseBelow();
647 						break;
648 				}
649 				parsestate = groundtable;
650 				break;
651 
652 			case CASE_EL:		// delete line
653 				/* EL */
654 				fView->DeleteColumns();
655 				parsestate = groundtable;
656 				break;
657 
658 			case CASE_IL:
659 				/* IL */
660 				if ((row = param[0]) < 1)
661 					row = 1;
662 				fView->InsertNewLine(row);
663 				parsestate = groundtable;
664 				break;
665 
666 			case CASE_DL:
667 				/* DL */
668 				if ((row = param[0]) < 1)
669 					row = 1;
670 				fView->DeleteLine(row);
671 				parsestate = groundtable;
672 				break;
673 
674 			case CASE_DCH:
675 				/* DCH */
676 				if ((row = param[0]) < 1)
677 					row = 1;
678 				fView->DeleteChar(row);
679 				parsestate = groundtable;
680 				break;
681 
682 			case CASE_SET:
683 				/* SET */
684 				fView->SetInsertMode(MODE_INSERT);
685 				parsestate = groundtable;
686 				break;
687 
688 			case CASE_RST:
689 				/* RST */
690 				fView->SetInsertMode(MODE_OVER);
691 				parsestate = groundtable;
692 				break;
693 
694 			case CASE_SGR:
695 				/* SGR */
696 				for (row = 0; row < nparam; ++row) {
697 					switch (param[row]) {
698 						case DEFAULT:
699 						case 0: /* Reset attribute */
700 							attr = 0;
701 							break;
702 
703 						case 1:
704 						case 5:	/* Bold		*/
705 							attr |= BOLD;
706 							break;
707 
708 						case 4:	/* Underline	*/
709 							attr |= UNDERLINE;
710 							break;
711 
712 						case 7:	/* Inverse	*/
713 							attr |= INVERSE;
714 							break;
715 
716 						case 22:	/* Not Bold	*/
717 							attr &= ~BOLD;
718 							break;
719 
720 						case 24:	/* Not Underline	*/
721 							attr &= ~UNDERLINE;
722 							break;
723 
724 						case 27:	/* Not Inverse	*/
725 							attr &= ~INVERSE;
726 							break;
727 
728 						case 30:
729 						case 31:
730 						case 32:
731 						case 33:
732 						case 34:
733 						case 35:
734 						case 36:
735 						case 37:
736 							attr &= ~FORECOLOR;
737 							attr |= FORECOLORED(param[row] - 30);
738 							attr |= FORESET;
739 							break;
740 
741 						case 39:
742 							attr &= ~FORESET;
743 							break;
744 
745 						case 40:
746 						case 41:
747 						case 42:
748 						case 43:
749 						case 44:
750 						case 45:
751 						case 46:
752 						case 47:
753 							attr &= ~BACKCOLOR;
754 							attr |= BACKCOLORED(param[row] - 40);
755 							attr |= BACKSET;
756 							break;
757 
758 						case 49:
759 							attr &= ~BACKSET;
760 							break;
761 					}
762 				}
763 				parsestate = groundtable;
764 				break;
765 
766 				case CASE_CPR:
767 					// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
768 					// 21-JUL-99
769 					fView->DeviceStatusReport(param[0]);
770 					parsestate = groundtable;
771 					break;
772 
773 				case CASE_DECSTBM:
774 					/* DECSTBM - set scrolling region */
775 
776 					if ((top = param[0]) < 1)
777 						top = 1;
778 
779 					if (nparam < 2)
780 						bot = -1;
781 					else
782 						bot = param[1];
783 
784 					top--;
785 					bot--;
786 
787 					if (bot > top)
788 						fView->SetScrollRegion(top, bot);
789 
790 					parsestate = groundtable;
791 					break;
792 
793 				case CASE_DECREQTPARM:
794 					parsestate = groundtable;
795 					break;
796 
797 				case CASE_DECSET:
798 					/* DECSET */
799 					//      dpmodes(term, bitset);
800 					parsestate = groundtable;
801 					break;
802 
803 				case CASE_DECRST:
804 					/* DECRST */
805 					//      dpmodes(term, bitclr);
806 					parsestate = groundtable;
807 					break;
808 
809 				case CASE_DECALN:
810 					/* DECALN */
811 					//      if(screen->cursor_state)
812 					//	HideCursor();
813 					//      ScrnRefresh(screen, 0, 0, screen->max_row + 1,
814 					//		  screen->max_col + 1, False);
815 					parsestate = groundtable;
816 					break;
817 
818 			//	case CASE_GSETS:
819 			//		screen->gsets[scstype] = GSET(c) | cs96;
820 			//		parsestate = groundtable;
821 			//		break;
822 
823 				case CASE_DECSC:
824 					/* DECSC */
825 					fView->SaveCursor();
826 					parsestate = groundtable;
827 					break;
828 
829 				case CASE_DECRC:
830 					/* DECRC */
831 					fView->RestoreCursor();
832 					parsestate = groundtable;
833 					break;
834 
835 				case CASE_HTS:
836 					/* HTS */
837 					//      TabSet(term->tabs, screen->cur_col);
838 					parsestate = groundtable;
839 					break;
840 
841 				case CASE_RI:
842 					/* RI */
843 					fView->ScrollRegion(-1, -1, SCRDOWN, 1);
844 					parsestate = groundtable;
845 					break;
846 
847 				case CASE_SS2:
848 					/* SS2 */
849 					curess = c;
850 					parsestate = groundtable;
851 					break;
852 
853 				case CASE_SS3:
854 					/* SS3 */
855 					curess = c;
856 					parsestate = groundtable;
857 					break;
858 
859 				case CASE_CSI_STATE:
860 					/* enter csi state */
861 					nparam = 1;
862 					param[0] = DEFAULT;
863 					parsestate = gCsiTable;
864 					break;
865 
866 				case CASE_OSC:
867 				{
868 					/* Operating System Command: ESC ] */
869 					char string[512];
870 					uint32 len = 0;
871 					uchar mode_char;
872 					GetReaderBuf(mode_char);
873 					if (mode_char != '0'
874 						&& mode_char != '1'
875 						&& mode_char != '2') {
876 						parsestate = groundtable;
877 						break;
878 					}
879 					uchar current_char;
880 					GetReaderBuf(current_char);
881 					while (GetReaderBuf(current_char) == B_OK
882 						&& current_char != 0x7) {
883 						if (!isprint(current_char & 0x7f)
884 							|| len+2 >= sizeof(string))
885 							break;
886 						string[len++] = current_char;
887 					}
888 					if (current_char == 0x7) {
889 						string[len] = '\0';
890 						switch (mode_char) {
891 							case '0':
892 							case '2':
893 								fView->SetTitle(string);
894 								break;
895 							case '1':
896 								break;
897 						}
898 					}
899 					parsestate = groundtable;
900 					break;
901 				}
902 
903 				case CASE_RIS:		// ESC c ... Reset terminal.
904 					break;
905 
906 				case CASE_LS2:
907 					/* LS2 */
908 					//      screen->curgl = 2;
909 					parsestate = groundtable;
910 					break;
911 
912 				case CASE_LS3:
913 					/* LS3 */
914 					//      screen->curgl = 3;
915 					parsestate = groundtable;
916 					break;
917 
918 				case CASE_LS3R:
919 					/* LS3R */
920 					//      screen->curgr = 3;
921 					parsestate = groundtable;
922 					break;
923 
924 				case CASE_LS2R:
925 					/* LS2R */
926 					//      screen->curgr = 2;
927 					parsestate = groundtable;
928 					break;
929 
930 				case CASE_LS1R:
931 					/* LS1R */
932 					//      screen->curgr = 1;
933 					parsestate = groundtable;
934 					break;
935 
936 				case CASE_VPA:		// ESC [...d move cursor absolute vertical
937 					/* VPA (CV) */
938 					if ((row = param[0]) < 1)
939 						row = 1;
940 
941 					// note beterm wants it 1-based unlike usual terminals
942 					fView->SetCurY(row - 1);
943 					parsestate = groundtable;
944 					break;
945 
946 				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
947 					/* HPA (CH) */
948 					if ((col = param[0]) < 1)
949 						col = 1;
950 
951 					// note beterm wants it 1-based unlike usual terminals
952 					fView->SetCurX(col - 1);
953 					parsestate = groundtable;
954 					break;
955 
956 				default:
957 					break;
958 		}
959 	}
960 
961 	return B_OK;
962 }
963 
964 
965 /*static*/ int32
966 TermParse::_ptyreader_thread(void *data)
967 {
968 	return reinterpret_cast<TermParse *>(data)->PtyReader();
969 }
970 
971 
972 /*static*/ int32
973 TermParse::_escparse_thread(void *data)
974 {
975 	return reinterpret_cast<TermParse *>(data)->EscParse();
976 }
977