xref: /haiku/src/apps/terminal/TermParse.cpp (revision cf02b29e4e0dd6d61c4bb25fcc8620e99d4908bf)
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 
23 #include "CodeConv.h"
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 status_t
51 TermParse::_NextParseChar(uchar &c)
52 {
53 	if (fParserBufferOffset >= fParserBufferSize) {
54 		// parser buffer empty
55 		status_t error = _ReadParserBuffer();
56 		if (error != B_OK)
57 			return error;
58 	}
59 
60 	c = fParserBuffer[fParserBufferOffset++];
61 
62 	return B_OK;
63 }
64 
65 
66 TermParse::TermParse(int fd)
67 	:
68 	fFd(fd),
69 	fParseThread(-1),
70 	fReaderThread(-1),
71 	fReaderSem(-1),
72 	fReaderLocker(-1),
73 	fBufferPosition(0),
74 	fReadBufferSize(0),
75 	fParserBufferSize(0),
76 	fParserBufferOffset(0),
77 	fParserWaiting(false),
78 	fBuffer(NULL),
79 	fQuitting(true)
80 {
81 }
82 
83 
84 TermParse::~TermParse()
85 {
86 	StopThreads();
87 }
88 
89 
90 status_t
91 TermParse::StartThreads(TerminalBuffer *buffer)
92 {
93 	if (fBuffer != NULL)
94 		return B_ERROR;
95 
96 	fQuitting = false;
97 	fBuffer = buffer;
98 
99 	status_t status = InitPtyReader();
100 	if (status < B_OK) {
101 		fBuffer = NULL;
102 		return status;
103 	}
104 
105 	status = InitTermParse();
106 	if (status < B_OK) {
107 		StopPtyReader();
108 		fBuffer = NULL;
109 		return status;
110 	}
111 
112 	return B_OK;
113 }
114 
115 
116 status_t
117 TermParse::StopThreads()
118 {
119 	if (fBuffer == NULL)
120 		return B_ERROR;
121 
122 	fQuitting = true;
123 
124 	StopPtyReader();
125 	StopTermParse();
126 
127 	fBuffer = NULL;
128 
129 	return B_OK;
130 }
131 
132 
133 //! Initialize and spawn EscParse thread.
134 status_t
135 TermParse::InitTermParse()
136 {
137 	if (fParseThread >= 0)
138 		return B_ERROR; // we might want to return B_OK instead ?
139 
140 	fParseThread = spawn_thread(_escparse_thread, "EscParse",
141 		B_DISPLAY_PRIORITY, this);
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 && fParserWaiting)
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 	fprintf(stderr, "parsestate: ");
295 	for (i = 0; tables[i].p; i++)
296 		if (tables[i].p == parsestate)
297 			fprintf(stderr, "%s\t", tables[i].name);
298 	fprintf(stderr, "char: 0x%02x (%d)\n", c, c);
299 }
300 
301 
302 int32
303 TermParse::EscParse()
304 {
305 	int tmp;
306 	int top, bot;
307 	int cs96 = 0;
308 	uchar curess = 0;
309 
310 	char cbuf[4], dstbuf[4];
311 	char *ptr;
312 
313 	int now_coding = -1;
314 
315 	ushort attr = BACKCOLOR;
316 
317 	int param[NPARAM];
318 	int nparam = 1;
319 
320 	int row, col;
321 
322 	/* default coding system is UTF8 */
323 	int *groundtable = gUTF8GroundTable;
324 	int *parsestate = groundtable;
325 
326 	int width = 1;
327 	BAutolock locker(fBuffer);
328 
329 	while (!fQuitting) {
330 		uchar c;
331 		if (_NextParseChar(c) < B_OK)
332 			break;
333 
334 		//DumpState(groundtable, parsestate, c);
335 
336 		if (now_coding != fBuffer->Encoding()) {
337 			/*
338 			 * Change coding, change parse table.
339 			 */
340 			switch (fBuffer->Encoding()) {
341 				case B_ISO1_CONVERSION:
342 				case B_ISO2_CONVERSION:
343 				case B_ISO3_CONVERSION:
344 				case B_ISO4_CONVERSION:
345 				case B_ISO5_CONVERSION:
346 				case B_ISO6_CONVERSION:
347 				case B_ISO7_CONVERSION:
348 				case B_ISO8_CONVERSION:
349 				case B_ISO9_CONVERSION:
350 				case B_ISO10_CONVERSION:
351 					groundtable = gISO8859GroundTable;
352 					break;
353 				case B_SJIS_CONVERSION:
354 					groundtable = gSJISGroundTable;
355 					break;
356 				case B_EUC_CONVERSION:
357 				case B_EUC_KR_CONVERSION:
358 				case B_JIS_CONVERSION:
359 				case B_GBK_CONVERSION:
360 				case B_BIG5_CONVERSION:
361 					groundtable = gISO8859GroundTable;
362 					break;
363 				case M_UTF8:
364 				default:
365 					groundtable = gUTF8GroundTable;
366 					break;
367 			}
368 			parsestate = groundtable;
369 			now_coding = fBuffer->Encoding();
370     	}
371 
372 //debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]);
373 		switch (parsestate[c]) {
374 			case CASE_PRINT:
375 				fBuffer->InsertChar((char)c, attr);
376 				break;
377 
378 			case CASE_PRINT_GR:
379 				/* case iso8859 gr character, or euc */
380 				ptr = cbuf;
381 				if (now_coding == B_EUC_CONVERSION
382 					|| now_coding == B_EUC_KR_CONVERSION
383 					|| now_coding == B_JIS_CONVERSION
384 					|| now_coding == B_GBK_CONVERSION
385 					|| now_coding == B_BIG5_CONVERSION) {
386 					switch (parsestate[curess]) {
387 						case CASE_SS2:		/* JIS X 0201 */
388 							width = 1;
389 							*ptr++ = curess;
390 							*ptr++ = c;
391 							*ptr = 0;
392 							curess = 0;
393 							break;
394 
395 						case CASE_SS3:		/* JIS X 0212 */
396 							width = 1;
397 							*ptr++ = curess;
398 							*ptr++ = c;
399 							_NextParseChar(c);
400 							*ptr++ = c;
401 							*ptr = 0;
402 							curess = 0;
403 							break;
404 
405 						default:		/* JIS X 0208 */
406 							width = 2;
407 							*ptr++ = c;
408 							_NextParseChar(c);
409 							*ptr++ = c;
410 							*ptr = 0;
411 							break;
412 					}
413 				} else {
414 					/* ISO-8859-1...10 and MacRoman */
415 					*ptr++ = c;
416 					*ptr = 0;
417 				}
418 
419 				if (now_coding != B_JIS_CONVERSION) {
420 					CodeConv::ConvertToInternal(cbuf, -1, dstbuf, now_coding);
421 				} else {
422 					CodeConv::ConvertToInternal(cbuf, -1, dstbuf,
423 						B_EUC_CONVERSION);
424 				}
425 
426 				fBuffer->InsertChar(dstbuf, 4, width, attr);
427 				break;
428 
429 			case CASE_PRINT_CS96:
430 				cbuf[0] = c | 0x80;
431 				_NextParseChar(c);
432 				cbuf[1] = c | 0x80;
433 				cbuf[2] = 0;
434 				CodeConv::ConvertToInternal(cbuf, 2, dstbuf, B_EUC_CONVERSION);
435 				fBuffer->InsertChar(dstbuf, 4, attr);
436 				break;
437 
438 			case CASE_LF:
439 				fBuffer->InsertLF();
440 				break;
441 
442 			case CASE_CR:
443 				fBuffer->InsertCR();
444 				break;
445 
446 			case CASE_SJIS_KANA:
447 				cbuf[0] = c;
448 				cbuf[1] = '\0';
449 				CodeConv::ConvertToInternal(cbuf, 1, dstbuf, now_coding);
450 				fBuffer->InsertChar(dstbuf, 4, attr);
451 				break;
452 
453 			case CASE_SJIS_INSTRING:
454 				cbuf[0] = c;
455 				_NextParseChar(c);
456 				cbuf[1] = c;
457 				cbuf[2] = '\0';
458 				CodeConv::ConvertToInternal(cbuf, 2, dstbuf, now_coding);
459 				fBuffer->InsertChar(dstbuf, 4, attr);
460 				break;
461 
462 			case CASE_UTF8_2BYTE:
463 				cbuf[0] = c;
464 				_NextParseChar(c);
465 				if (groundtable[c] != CASE_UTF8_INSTRING)
466 					break;
467 				cbuf[1] = c;
468 				cbuf[2] = '\0';
469 
470 				fBuffer->InsertChar(cbuf, 2, attr);
471 				break;
472 
473 			case CASE_UTF8_3BYTE:
474 				cbuf[0] = c;
475 				_NextParseChar(c);
476 				if (groundtable[c] != CASE_UTF8_INSTRING)
477 					break;
478 				cbuf[1] = c;
479 
480 				_NextParseChar(c);
481 				if (groundtable[c] != CASE_UTF8_INSTRING)
482 					break;
483 				cbuf[2] = c;
484 				cbuf[3] = '\0';
485 				fBuffer->InsertChar(cbuf, 3, attr);
486 				break;
487 
488 			case CASE_MBCS:
489 				/* ESC $ */
490 				parsestate = gMbcsTable;
491 				break;
492 
493 			case CASE_GSETS:
494 				/* ESC $ ? */
495 				parsestate = gCS96GroundTable;
496 				cs96 = 1;
497 				break;
498 
499 			case CASE_SCS_STATE:
500 			{
501 				cs96 = 0;
502 				uchar dummy;
503 				_NextParseChar(dummy);
504 				parsestate = groundtable;
505 				break;
506 			}
507 			case CASE_GROUND_STATE:
508 				/* exit ignore mode */
509 				parsestate = groundtable;
510 				break;
511 
512 			case CASE_BELL:
513 				beep();
514 				break;
515 
516 			case CASE_BS:
517 				fBuffer->MoveCursorLeft(1);
518 				break;
519 
520 			case CASE_TAB:
521 				tmp = fBuffer->Cursor().x;
522 				tmp %= 8;
523 				fBuffer->MoveCursorRight(8 - tmp);
524 				break;
525 
526 			case CASE_ESC:
527 				/* escape */
528 				parsestate = gEscTable;
529 				break;
530 
531 			case CASE_IGNORE_STATE:
532 				/* Ies: ignore anything else */
533 				parsestate = gIgnoreTable;
534 				break;
535 
536 			case CASE_IGNORE_ESC:
537 				/* Ign: escape */
538 				parsestate = gIesTable;
539 				break;
540 
541 			case CASE_IGNORE:
542 				/* Ignore character */
543 				break;
544 
545 			case CASE_SI:
546 				break;
547 
548 			case CASE_SO:
549 				break;
550 
551 			case CASE_SCR_STATE:	// ESC #
552 				/* enter scr state */
553 				parsestate = gScrTable;
554 				break;
555 
556 			case CASE_ESC_IGNORE:
557 				/* unknown escape sequence */
558 				parsestate = gEscIgnoreTable;
559 				break;
560 
561 			case CASE_ESC_DIGIT:	// ESC [ number
562 				/* digit in csi or dec mode */
563 				if ((row = param[nparam - 1]) == DEFAULT)
564 					row = 0;
565 				param[nparam - 1] = 10 * row + (c - '0');
566 				break;
567 
568 			case CASE_ESC_SEMI:		// ESC ;
569 				/* semicolon in csi or dec mode */
570 				if (nparam < NPARAM)
571 					param[nparam++] = DEFAULT;
572 				break;
573 
574 			case CASE_DEC_STATE:
575 				/* enter dec mode */
576 				parsestate = gDecTable;
577 				break;
578 
579 			case CASE_ICH:		// ESC [@ insert charactor
580 				/* ICH */
581 				if ((row = param[0]) < 1)
582 					row = 1;
583 				fBuffer->InsertSpace(row);
584 				parsestate = groundtable;
585 				break;
586 
587 			case CASE_CUU:		// ESC [A cursor up, up arrow key.
588 				/* CUU */
589 				if ((row = param[0]) < 1)
590 					row = 1;
591 				fBuffer->MoveCursorUp(row);
592 				parsestate = groundtable;
593 				break;
594 
595 			case CASE_CUD:		// ESC [B cursor down, down arrow key.
596 				/* CUD */
597 				if ((row = param[0]) < 1)
598 					row = 1;
599 				fBuffer->MoveCursorDown(row);
600 				parsestate = groundtable;
601 				break;
602 
603 			case CASE_CUF:		// ESC [C cursor forword
604 				/* CUF */
605 				if ((row = param[0]) < 1)
606 					row = 1;
607 				fBuffer->MoveCursorRight(row);
608 				parsestate = groundtable;
609 				break;
610 
611 			case CASE_CUB:		// ESC [D cursor backword
612 				/* CUB */
613 				if ((row = param[0]) < 1)
614 					row = 1;
615 				fBuffer->MoveCursorLeft(row);
616 				parsestate = groundtable;
617 				break;
618 
619 			case CASE_CUP:		// ESC [...H move cursor
620 				/* CUP | HVP */
621 				if ((row = param[0]) < 1)
622 					row = 1;
623 				if (nparam < 2 || (col = param[1]) < 1)
624 					col = 1;
625 
626 				fBuffer->SetCursor(col - 1, row - 1 );
627 				parsestate = groundtable;
628 				break;
629 
630 			case CASE_ED:		// ESC [ ...J clear screen
631 				/* ED */
632 				switch (param[0]) {
633 					case DEFAULT:
634 					case 0:
635 						fBuffer->EraseBelow();
636 						break;
637 
638 					case 1:
639 						fBuffer->EraseAbove();
640 						break;
641 
642 					case 2:
643 						fBuffer->EraseAll();
644 						break;
645 				}
646 				parsestate = groundtable;
647 				break;
648 
649 			case CASE_EL:		// delete line
650 				/* EL */
651 				fBuffer->DeleteColumns();
652 				parsestate = groundtable;
653 				break;
654 
655 			case CASE_IL:
656 				/* IL */
657 				if ((row = param[0]) < 1)
658 					row = 1;
659 				fBuffer->InsertLines(row);
660 				parsestate = groundtable;
661 				break;
662 
663 			case CASE_DL:
664 				/* DL */
665 				if ((row = param[0]) < 1)
666 					row = 1;
667 				fBuffer->DeleteLines(row);
668 				parsestate = groundtable;
669 				break;
670 
671 			case CASE_DCH:
672 				/* DCH */
673 				if ((row = param[0]) < 1)
674 					row = 1;
675 				fBuffer->DeleteChars(row);
676 				parsestate = groundtable;
677 				break;
678 
679 			case CASE_SET:
680 				/* SET */
681 				if (param[0] == 4)
682 					fBuffer->SetInsertMode(MODE_INSERT);
683 				parsestate = groundtable;
684 				break;
685 
686 			case CASE_RST:
687 				/* RST */
688 				if (param[0] == 4)
689 					fBuffer->SetInsertMode(MODE_OVER);
690 				parsestate = groundtable;
691 				break;
692 
693 			case CASE_SGR:
694 				/* SGR */
695 				for (row = 0; row < nparam; ++row) {
696 					switch (param[row]) {
697 						case DEFAULT:
698 						case 0: /* Reset attribute */
699 							attr = 0;
700 							break;
701 
702 						case 1:
703 						case 5:	/* Bold		*/
704 							attr |= BOLD;
705 							break;
706 
707 						case 4:	/* Underline	*/
708 							attr |= UNDERLINE;
709 							break;
710 
711 						case 7:	/* Inverse	*/
712 							attr |= INVERSE;
713 							break;
714 
715 						case 22:	/* Not Bold	*/
716 							attr &= ~BOLD;
717 							break;
718 
719 						case 24:	/* Not Underline	*/
720 							attr &= ~UNDERLINE;
721 							break;
722 
723 						case 27:	/* Not Inverse	*/
724 							attr &= ~INVERSE;
725 							break;
726 
727 						case 30:
728 						case 31:
729 						case 32:
730 						case 33:
731 						case 34:
732 						case 35:
733 						case 36:
734 						case 37:
735 							attr &= ~FORECOLOR;
736 							attr |= FORECOLORED(param[row] - 30);
737 							attr |= FORESET;
738 							break;
739 
740 						case 39:
741 							attr &= ~FORESET;
742 							break;
743 
744 						case 40:
745 						case 41:
746 						case 42:
747 						case 43:
748 						case 44:
749 						case 45:
750 						case 46:
751 						case 47:
752 							attr &= ~BACKCOLOR;
753 							attr |= BACKCOLORED(param[row] - 40);
754 							attr |= BACKSET;
755 							break;
756 
757 						case 49:
758 							attr &= ~BACKSET;
759 							break;
760 					}
761 				}
762 				parsestate = groundtable;
763 				break;
764 
765 				case CASE_CPR:
766 					// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
767 					// 21-JUL-99
768 					_DeviceStatusReport(param[0]);
769 					parsestate = groundtable;
770 					break;
771 
772 				case CASE_DECSTBM:
773 					/* DECSTBM - set scrolling region */
774 
775 					if ((top = param[0]) < 1)
776 						top = 1;
777 
778 					if (nparam < 2)
779 						bot = fBuffer->Height();
780 					else
781 						bot = param[1];
782 
783 					top--;
784 					bot--;
785 
786 					if (bot > top)
787 						fBuffer->SetScrollRegion(top, bot);
788 
789 					parsestate = groundtable;
790 					break;
791 
792 				case CASE_DECREQTPARM:
793 					parsestate = groundtable;
794 					break;
795 
796 				case CASE_DECSET:
797 					/* DECSET */
798 					for (int i = 0; i < nparam; i++)
799 						_DecPrivateModeSet(param[i]);
800 					parsestate = groundtable;
801 					break;
802 
803 				case CASE_DECRST:
804 					/* DECRST */
805 					for (int i = 0; i < nparam; i++)
806 						_DecPrivateModeReset(param[i]);
807 					parsestate = groundtable;
808 					break;
809 
810 				case CASE_DECALN:
811 					/* DECALN */
812 					//      if(screen->cursor_state)
813 					//	HideCursor();
814 					//      ScrnRefresh(screen, 0, 0, screen->max_row + 1,
815 					//		  screen->max_col + 1, False);
816 					parsestate = groundtable;
817 					break;
818 
819 			//	case CASE_GSETS:
820 			//		screen->gsets[scstype] = GSET(c) | cs96;
821 			//		parsestate = groundtable;
822 			//		break;
823 
824 				case CASE_DECSC:
825 					/* DECSC */
826 					fBuffer->SaveCursor();
827 					parsestate = groundtable;
828 					break;
829 
830 				case CASE_DECRC:
831 					/* DECRC */
832 					fBuffer->RestoreCursor();
833 					parsestate = groundtable;
834 					break;
835 
836 				case CASE_HTS:
837 					/* HTS */
838 					//      TabSet(term->tabs, screen->cur_col);
839 					parsestate = groundtable;
840 					break;
841 
842 				case CASE_RI:
843 					/* RI */
844 					if (fBuffer->Cursor().y == 0)
845 						fBuffer->ScrollBy(-1);
846 					else
847 						fBuffer->MoveCursorUp(1);
848 					parsestate = groundtable;
849 					break;
850 
851 				case CASE_SS2:
852 					/* SS2 */
853 					curess = c;
854 					parsestate = groundtable;
855 					break;
856 
857 				case CASE_SS3:
858 					/* SS3 */
859 					curess = c;
860 					parsestate = groundtable;
861 					break;
862 
863 				case CASE_CSI_STATE:
864 					/* enter csi state */
865 					nparam = 1;
866 					param[0] = DEFAULT;
867 					parsestate = gCsiTable;
868 					break;
869 
870 				case CASE_OSC:
871 				{
872 					/* Operating System Command: ESC ] */
873 					char string[512];
874 					uint32 len = 0;
875 					uchar mode_char;
876 					_NextParseChar(mode_char);
877 					if (mode_char != '0'
878 						&& mode_char != '1'
879 						&& mode_char != '2') {
880 						parsestate = groundtable;
881 						break;
882 					}
883 					uchar current_char;
884 					_NextParseChar(current_char);
885 					while (_NextParseChar(current_char) == B_OK
886 						&& current_char != 0x7) {
887 						if (!isprint(current_char & 0x7f)
888 							|| len+2 >= sizeof(string))
889 							break;
890 						string[len++] = current_char;
891 					}
892 					if (current_char == 0x7) {
893 						string[len] = '\0';
894 						switch (mode_char) {
895 							case '0':
896 							case '2':
897 								fBuffer->SetTitle(string);
898 								break;
899 							case '1':
900 								break;
901 						}
902 					}
903 					parsestate = groundtable;
904 					break;
905 				}
906 
907 				case CASE_RIS:		// ESC c ... Reset terminal.
908 					break;
909 
910 				case CASE_LS2:
911 					/* LS2 */
912 					//      screen->curgl = 2;
913 					parsestate = groundtable;
914 					break;
915 
916 				case CASE_LS3:
917 					/* LS3 */
918 					//      screen->curgl = 3;
919 					parsestate = groundtable;
920 					break;
921 
922 				case CASE_LS3R:
923 					/* LS3R */
924 					//      screen->curgr = 3;
925 					parsestate = groundtable;
926 					break;
927 
928 				case CASE_LS2R:
929 					/* LS2R */
930 					//      screen->curgr = 2;
931 					parsestate = groundtable;
932 					break;
933 
934 				case CASE_LS1R:
935 					/* LS1R */
936 					//      screen->curgr = 1;
937 					parsestate = groundtable;
938 					break;
939 
940 				case CASE_VPA:		// ESC [...d move cursor absolute vertical
941 					/* VPA (CV) */
942 					if ((row = param[0]) < 1)
943 						row = 1;
944 
945 					// note beterm wants it 1-based unlike usual terminals
946 					fBuffer->SetCursorY(row - 1);
947 					parsestate = groundtable;
948 					break;
949 
950 				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
951 					/* HPA (CH) */
952 					if ((col = param[0]) < 1)
953 						col = 1;
954 
955 					// note beterm wants it 1-based unlike usual terminals
956 					fBuffer->SetCursorX(col - 1);
957 					parsestate = groundtable;
958 					break;
959 
960 				case CASE_SU:	// scroll screen up
961 					if ((row = param[0]) < 1)
962 						row = 1;
963 					fBuffer->ScrollBy(row);
964 					parsestate = groundtable;
965 					break;
966 
967 				case CASE_SD:	// scroll screen down
968 					if ((row = param[0]) < 1)
969 						row = 1;
970 					fBuffer->ScrollBy(-row);
971 					parsestate = groundtable;
972 					break;
973 
974 
975 				case CASE_ECH:	// erase characters
976 					if ((col = param[0]) < 1)
977 						col = 1;
978 					fBuffer->EraseChars(col);
979 					parsestate = groundtable;
980 					break;
981 
982 				default:
983 					break;
984 		}
985 	}
986 
987 	return B_OK;
988 }
989 
990 
991 /*static*/ int32
992 TermParse::_ptyreader_thread(void *data)
993 {
994 	return reinterpret_cast<TermParse *>(data)->PtyReader();
995 }
996 
997 
998 /*static*/ int32
999 TermParse::_escparse_thread(void *data)
1000 {
1001 	return reinterpret_cast<TermParse *>(data)->EscParse();
1002 }
1003 
1004 
1005 status_t
1006 TermParse::_ReadParserBuffer()
1007 {
1008 	// We have to unlock the terminal buffer while waiting for data from the
1009 	// PTY. We don't have to unlock when we don't need to wait, but we do it
1010 	// anyway, so that TermView won't be starved when trying to synchronize.
1011 	fBuffer->Unlock();
1012 
1013 	// wait for new input from pty
1014 	if (fReadBufferSize == 0) {
1015 		fParserWaiting = true;
1016 
1017 		status_t status = B_OK;
1018 		while (fReadBufferSize == 0 && status == B_OK) {
1019 			do {
1020 				status = acquire_sem(fReaderSem);
1021 			} while (status == B_INTERRUPTED);
1022 		}
1023 
1024 		fParserWaiting = false;
1025 
1026 		if (status < B_OK) {
1027 			fBuffer->Lock();
1028 			return status;
1029 		}
1030 	}
1031 
1032 	int32 toRead = fReadBufferSize;
1033 	if (toRead > ESC_PARSER_BUFFER_SIZE)
1034 		toRead = ESC_PARSER_BUFFER_SIZE;
1035 
1036 	for (int32 i = 0; i < toRead; i++) {
1037 		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1038 		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1039 	}
1040 
1041 	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1042 
1043   	// If the pty reader thread waits and we have made enough space in the
1044 	// buffer now, let it run again.
1045 	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1046 		&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1047 		release_sem(fReaderLocker);
1048 	}
1049 
1050 	fParserBufferSize = toRead;
1051 	fParserBufferOffset = 0;
1052 
1053 	fBuffer->Lock();
1054 	return B_OK;
1055 }
1056 
1057 
1058 void
1059 TermParse::_DeviceStatusReport(int n)
1060 {
1061 	char sbuf[16] ;
1062 	int len;
1063 
1064 	switch (n) {
1065 		case 5:
1066 		{
1067 			const char* toWrite = "\033[0n";
1068 			write(fFd, toWrite, strlen(toWrite));
1069 			break ;
1070 		}
1071 		case 6:
1072 			len = sprintf(sbuf, "\033[%ld;%ldR", fBuffer->Height(),
1073 				fBuffer->Width()) ;
1074 			write(fFd, sbuf, len);
1075 			break ;
1076 		default:
1077 			return;
1078 	}
1079 }
1080 
1081 
1082 void
1083 TermParse::_DecPrivateModeSet(int value)
1084 {
1085 	switch (value) {
1086 		case 1:
1087 			// Application Cursor Keys (whatever that means).
1088 			// Not supported yet.
1089 			break;
1090 		case 5:
1091 			// Reverse Video (inverses colors for the complete screen
1092 			// -- when followed by normal video, that's shortly flashes the
1093 			// screen).
1094 			// Not supported yet.
1095 			break;
1096 		case 9:
1097 			// Set Mouse X and Y on button press.
1098 			fBuffer->ReportX10MouseEvent(true);
1099 			break;
1100 		case 12:
1101 			// Start Blinking Cursor.
1102 			// Not supported yet.
1103 			break;
1104 		case 25:
1105 			// Show Cursor.
1106 			// Not supported yet.
1107 			break;
1108 		case 47:
1109 			// Use Alternate Screen Buffer.
1110 			fBuffer->UseAlternateScreenBuffer(false);
1111 			break;
1112 		case 1000:
1113 			// Send Mouse X & Y on button press and release.
1114 			fBuffer->ReportNormalMouseEvent(true);
1115 			break;
1116 		case 1002:
1117 			// Send Mouse X and Y on button press and release, and on motion
1118 			// when the mouse enter a new cell
1119 			fBuffer->ReportButtonMouseEvent(true);
1120 			break;
1121 		case 1003:
1122 			// Use All Motion Mouse Tracking
1123 			fBuffer->ReportAnyMouseEvent(true);
1124 			break;
1125 		case 1034:
1126 			// TODO: Interprete "meta" key, sets eighth bit.
1127 			// Not supported yet.
1128 			break;
1129 		case 1036:
1130 			// TODO: Send ESC when Meta modifies a key
1131 			// Not supported yet.
1132 			break;
1133 		case 1039:
1134 			// TODO: Send ESC when Alt modifies a key
1135 			// Not supported yet.
1136 			break;
1137 		case 1049:
1138 			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1139 			// it first.
1140 			fBuffer->SaveCursor();
1141 			fBuffer->UseAlternateScreenBuffer(true);
1142 			break;
1143 	}
1144 }
1145 
1146 
1147 void
1148 TermParse::_DecPrivateModeReset(int value)
1149 {
1150 	switch (value) {
1151 		case 1:
1152 			// Normal Cursor Keys (whatever that means).
1153 			// Not supported yet.
1154 			break;
1155 		case 3:
1156 			// 80 Column Mode.
1157 			// Not supported yet.
1158 			break;
1159 		case 4:
1160 			// Jump (Fast) Scroll.
1161 			// Not supported yet.
1162 			break;
1163 		case 5:
1164 			// Normal Video (Leaves Reverse Video, cf. there).
1165 			// Not supported yet.
1166 			break;
1167 		case 9:
1168 			// Disable Mouse X and Y on button press.
1169 			fBuffer->ReportX10MouseEvent(false);
1170 			break;
1171 		case 12:
1172 			// Stop Blinking Cursor.
1173 			// Not supported yet.
1174 			break;
1175 		case 25:
1176 			// Hide Cursor
1177 			// Not supported yet.
1178 			break;
1179 		case 47:
1180 			// Use Normal Screen Buffer.
1181 			fBuffer->UseNormalScreenBuffer();
1182 			break;
1183 		case 1000:
1184 			// Don't send Mouse X & Y on button press and release.
1185 			fBuffer->ReportNormalMouseEvent(false);
1186 			break;
1187 		case 1002:
1188 			// Don't send Mouse X and Y on button press and release, and on motion
1189 			// when the mouse enter a new cell
1190 			fBuffer->ReportButtonMouseEvent(false);
1191 			break;
1192 		case 1003:
1193 			// Disable All Motion Mouse Tracking.
1194 			fBuffer->ReportAnyMouseEvent(false);
1195 			break;
1196 		case 1034:
1197 			// Don't interprete "meta" key.
1198 			// Not supported yet.
1199 			break;
1200 		case 1036:
1201 			// TODO: Don't send ESC when Meta modifies a key
1202 			// Not supported yet.
1203 			break;
1204 		case 1039:
1205 			// TODO: Don't send ESC when Alt modifies a key
1206 			// Not supported yet.
1207 			break;
1208 		case 1049:
1209 			// Use Normal Screen Buffer and restore cursor as in DECRC.
1210 			fBuffer->UseNormalScreenBuffer();
1211 			fBuffer->RestoreCursor();
1212 			break;
1213 	}
1214 }
1215