xref: /haiku/src/apps/terminal/TermParse.cpp (revision e7c8829c5d8e5d34a2a1e111f1c06aceff256013)
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->EraseBelow();
644 						fBuffer->EraseAbove();
645 						break;
646 				}
647 				parsestate = groundtable;
648 				break;
649 
650 			case CASE_EL:		// delete line
651 				/* EL */
652 				fBuffer->DeleteColumns();
653 				parsestate = groundtable;
654 				break;
655 
656 			case CASE_IL:
657 				/* IL */
658 				if ((row = param[0]) < 1)
659 					row = 1;
660 				fBuffer->InsertLines(row);
661 				parsestate = groundtable;
662 				break;
663 
664 			case CASE_DL:
665 				/* DL */
666 				if ((row = param[0]) < 1)
667 					row = 1;
668 				fBuffer->DeleteLines(row);
669 				parsestate = groundtable;
670 				break;
671 
672 			case CASE_DCH:
673 				/* DCH */
674 				if ((row = param[0]) < 1)
675 					row = 1;
676 				fBuffer->DeleteChars(row);
677 				parsestate = groundtable;
678 				break;
679 
680 			case CASE_SET:
681 				/* SET */
682 				if (param[0] == 4)
683 					fBuffer->SetInsertMode(MODE_INSERT);
684 				parsestate = groundtable;
685 				break;
686 
687 			case CASE_RST:
688 				/* RST */
689 				if (param[0] == 4)
690 					fBuffer->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 					_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 = fBuffer->Height();
781 					else
782 						bot = param[1];
783 
784 					top--;
785 					bot--;
786 
787 					if (bot > top)
788 						fBuffer->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 					for (int i = 0; i < nparam; i++)
800 						_DecPrivateModeSet(param[i]);
801 					parsestate = groundtable;
802 					break;
803 
804 				case CASE_DECRST:
805 					/* DECRST */
806 					for (int i = 0; i < nparam; i++)
807 						_DecPrivateModeReset(param[i]);
808 					parsestate = groundtable;
809 					break;
810 
811 				case CASE_DECALN:
812 					/* DECALN */
813 					//      if(screen->cursor_state)
814 					//	HideCursor();
815 					//      ScrnRefresh(screen, 0, 0, screen->max_row + 1,
816 					//		  screen->max_col + 1, False);
817 					parsestate = groundtable;
818 					break;
819 
820 			//	case CASE_GSETS:
821 			//		screen->gsets[scstype] = GSET(c) | cs96;
822 			//		parsestate = groundtable;
823 			//		break;
824 
825 				case CASE_DECSC:
826 					/* DECSC */
827 					fBuffer->SaveCursor();
828 					parsestate = groundtable;
829 					break;
830 
831 				case CASE_DECRC:
832 					/* DECRC */
833 					fBuffer->RestoreCursor();
834 					parsestate = groundtable;
835 					break;
836 
837 				case CASE_HTS:
838 					/* HTS */
839 					//      TabSet(term->tabs, screen->cur_col);
840 					parsestate = groundtable;
841 					break;
842 
843 				case CASE_RI:
844 					/* RI */
845 					if (fBuffer->Cursor().y == 0)
846 						fBuffer->ScrollBy(-1);
847 					else
848 						fBuffer->MoveCursorUp(1);
849 					parsestate = groundtable;
850 					break;
851 
852 				case CASE_SS2:
853 					/* SS2 */
854 					curess = c;
855 					parsestate = groundtable;
856 					break;
857 
858 				case CASE_SS3:
859 					/* SS3 */
860 					curess = c;
861 					parsestate = groundtable;
862 					break;
863 
864 				case CASE_CSI_STATE:
865 					/* enter csi state */
866 					nparam = 1;
867 					param[0] = DEFAULT;
868 					parsestate = gCsiTable;
869 					break;
870 
871 				case CASE_OSC:
872 				{
873 					/* Operating System Command: ESC ] */
874 					char string[512];
875 					uint32 len = 0;
876 					uchar mode_char;
877 					_NextParseChar(mode_char);
878 					if (mode_char != '0'
879 						&& mode_char != '1'
880 						&& mode_char != '2') {
881 						parsestate = groundtable;
882 						break;
883 					}
884 					uchar current_char;
885 					_NextParseChar(current_char);
886 					while (_NextParseChar(current_char) == B_OK
887 						&& current_char != 0x7) {
888 						if (!isprint(current_char & 0x7f)
889 							|| len+2 >= sizeof(string))
890 							break;
891 						string[len++] = current_char;
892 					}
893 					if (current_char == 0x7) {
894 						string[len] = '\0';
895 						switch (mode_char) {
896 							case '0':
897 							case '2':
898 								fBuffer->SetTitle(string);
899 								break;
900 							case '1':
901 								break;
902 						}
903 					}
904 					parsestate = groundtable;
905 					break;
906 				}
907 
908 				case CASE_RIS:		// ESC c ... Reset terminal.
909 					break;
910 
911 				case CASE_LS2:
912 					/* LS2 */
913 					//      screen->curgl = 2;
914 					parsestate = groundtable;
915 					break;
916 
917 				case CASE_LS3:
918 					/* LS3 */
919 					//      screen->curgl = 3;
920 					parsestate = groundtable;
921 					break;
922 
923 				case CASE_LS3R:
924 					/* LS3R */
925 					//      screen->curgr = 3;
926 					parsestate = groundtable;
927 					break;
928 
929 				case CASE_LS2R:
930 					/* LS2R */
931 					//      screen->curgr = 2;
932 					parsestate = groundtable;
933 					break;
934 
935 				case CASE_LS1R:
936 					/* LS1R */
937 					//      screen->curgr = 1;
938 					parsestate = groundtable;
939 					break;
940 
941 				case CASE_VPA:		// ESC [...d move cursor absolute vertical
942 					/* VPA (CV) */
943 					if ((row = param[0]) < 1)
944 						row = 1;
945 
946 					// note beterm wants it 1-based unlike usual terminals
947 					fBuffer->SetCursorY(row - 1);
948 					parsestate = groundtable;
949 					break;
950 
951 				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
952 					/* HPA (CH) */
953 					if ((col = param[0]) < 1)
954 						col = 1;
955 
956 					// note beterm wants it 1-based unlike usual terminals
957 					fBuffer->SetCursorX(col - 1);
958 					parsestate = groundtable;
959 					break;
960 
961 				case CASE_SU:	// scroll screen up
962 					if ((row = param[0]) < 1)
963 						row = 1;
964 					fBuffer->ScrollBy(row);
965 					parsestate = groundtable;
966 					break;
967 
968 				case CASE_SD:	// scroll screen down
969 					if ((row = param[0]) < 1)
970 						row = 1;
971 					fBuffer->ScrollBy(-row);
972 					parsestate = groundtable;
973 					break;
974 
975 
976 				case CASE_ECH:	// erase characters
977 					if ((col = param[0]) < 1)
978 						col = 1;
979 					fBuffer->EraseChars(col);
980 					parsestate = groundtable;
981 					break;
982 
983 				default:
984 					break;
985 		}
986 	}
987 
988 	return B_OK;
989 }
990 
991 
992 /*static*/ int32
993 TermParse::_ptyreader_thread(void *data)
994 {
995 	return reinterpret_cast<TermParse *>(data)->PtyReader();
996 }
997 
998 
999 /*static*/ int32
1000 TermParse::_escparse_thread(void *data)
1001 {
1002 	return reinterpret_cast<TermParse *>(data)->EscParse();
1003 }
1004 
1005 
1006 status_t
1007 TermParse::_ReadParserBuffer()
1008 {
1009 	// We have to unlock the terminal buffer while waiting for data from the
1010 	// PTY. We don't have to unlock when we don't need to wait, but we do it
1011 	// anyway, so that TermView won't be starved when trying to synchronize.
1012 	fBuffer->Unlock();
1013 
1014 	// wait for new input from pty
1015 	if (fReadBufferSize == 0) {
1016 		fParserWaiting = true;
1017 
1018 		status_t status = B_OK;
1019 		while (fReadBufferSize == 0 && status == B_OK) {
1020 			do {
1021 				status = acquire_sem(fReaderSem);
1022 			} while (status == B_INTERRUPTED);
1023 		}
1024 
1025 		fParserWaiting = false;
1026 
1027 		if (status < B_OK) {
1028 			fBuffer->Lock();
1029 			return status;
1030 		}
1031 	}
1032 
1033 	int32 toRead = fReadBufferSize;
1034 	if (toRead > ESC_PARSER_BUFFER_SIZE)
1035 		toRead = ESC_PARSER_BUFFER_SIZE;
1036 
1037 	for (int32 i = 0; i < toRead; i++) {
1038 		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1039 		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1040 	}
1041 
1042 	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1043 
1044   	// If the pty reader thread waits and we have made enough space in the
1045 	// buffer now, let it run again.
1046 	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1047 		&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1048 		release_sem(fReaderLocker);
1049 	}
1050 
1051 	fParserBufferSize = toRead;
1052 	fParserBufferOffset = 0;
1053 
1054 	fBuffer->Lock();
1055 	return B_OK;
1056 }
1057 
1058 
1059 void
1060 TermParse::_DeviceStatusReport(int n)
1061 {
1062 	char sbuf[16] ;
1063 	int len;
1064 
1065 	switch (n) {
1066 		case 5:
1067 		{
1068 			const char* toWrite = "\033[0n";
1069 			write(fFd, toWrite, strlen(toWrite));
1070 			break ;
1071 		}
1072 		case 6:
1073 			len = sprintf(sbuf, "\033[%ld;%ldR", fBuffer->Height(),
1074 				fBuffer->Width()) ;
1075 			write(fFd, sbuf, len);
1076 			break ;
1077 		default:
1078 			return;
1079 	}
1080 }
1081 
1082 
1083 void
1084 TermParse::_DecPrivateModeSet(int value)
1085 {
1086 	switch (value) {
1087 		case 1:
1088 			// Application Cursor Keys (whatever that means).
1089 			// Not supported yet.
1090 			break;
1091 		case 5:
1092 			// Reverse Video (inverses colors for the complete screen
1093 			// -- when followed by normal video, that's shortly flashes the
1094 			// screen).
1095 			// Not supported yet.
1096 			break;
1097 		case 9:
1098 			// Set Mouse X and Y on button press.
1099 			fBuffer->ReportX10MouseEvent(true);
1100 			break;
1101 		case 12:
1102 			// Start Blinking Cursor.
1103 			// Not supported yet.
1104 			break;
1105 		case 25:
1106 			// Show Cursor.
1107 			// Not supported yet.
1108 			break;
1109 		case 47:
1110 			// Use Alternate Screen Buffer.
1111 			fBuffer->UseAlternateScreenBuffer(false);
1112 			break;
1113 		case 1000:
1114 			// Send Mouse X & Y on button press and release.
1115 			fBuffer->ReportNormalMouseEvent(true);
1116 			break;
1117 		case 1002:
1118 			// Send Mouse X and Y on button press and release, and on motion
1119 			// when the mouse enter a new cell
1120 			fBuffer->ReportButtonMouseEvent(true);
1121 			break;
1122 		case 1003:
1123 			// Use All Motion Mouse Tracking
1124 			fBuffer->ReportAnyMouseEvent(true);
1125 			break;
1126 		case 1034:
1127 			// TODO: Interprete "meta" key, sets eighth bit.
1128 			// Not supported yet.
1129 			break;
1130 		case 1036:
1131 			// TODO: Send ESC when Meta modifies a key
1132 			// Not supported yet.
1133 			break;
1134 		case 1039:
1135 			// TODO: Send ESC when Alt modifies a key
1136 			// Not supported yet.
1137 			break;
1138 		case 1049:
1139 			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1140 			// it first.
1141 			fBuffer->SaveCursor();
1142 			fBuffer->UseAlternateScreenBuffer(true);
1143 			break;
1144 	}
1145 }
1146 
1147 
1148 void
1149 TermParse::_DecPrivateModeReset(int value)
1150 {
1151 	switch (value) {
1152 		case 1:
1153 			// Normal Cursor Keys (whatever that means).
1154 			// Not supported yet.
1155 			break;
1156 		case 3:
1157 			// 80 Column Mode.
1158 			// Not supported yet.
1159 			break;
1160 		case 4:
1161 			// Jump (Fast) Scroll.
1162 			// Not supported yet.
1163 			break;
1164 		case 5:
1165 			// Normal Video (Leaves Reverse Video, cf. there).
1166 			// Not supported yet.
1167 			break;
1168 		case 9:
1169 			// Disable Mouse X and Y on button press.
1170 			fBuffer->ReportX10MouseEvent(false);
1171 			break;
1172 		case 12:
1173 			// Stop Blinking Cursor.
1174 			// Not supported yet.
1175 			break;
1176 		case 25:
1177 			// Hide Cursor
1178 			// Not supported yet.
1179 			break;
1180 		case 47:
1181 			// Use Normal Screen Buffer.
1182 			fBuffer->UseNormalScreenBuffer();
1183 			break;
1184 		case 1000:
1185 			// Don't send Mouse X & Y on button press and release.
1186 			fBuffer->ReportNormalMouseEvent(false);
1187 			break;
1188 		case 1002:
1189 			// Don't send Mouse X and Y on button press and release, and on motion
1190 			// when the mouse enter a new cell
1191 			fBuffer->ReportButtonMouseEvent(false);
1192 			break;
1193 		case 1003:
1194 			// Disable All Motion Mouse Tracking.
1195 			fBuffer->ReportAnyMouseEvent(false);
1196 			break;
1197 		case 1034:
1198 			// Don't interprete "meta" key.
1199 			// Not supported yet.
1200 			break;
1201 		case 1036:
1202 			// TODO: Don't send ESC when Meta modifies a key
1203 			// Not supported yet.
1204 			break;
1205 		case 1039:
1206 			// TODO: Don't send ESC when Alt modifies a key
1207 			// Not supported yet.
1208 			break;
1209 		case 1049:
1210 			// Use Normal Screen Buffer and restore cursor as in DECRC.
1211 			fBuffer->UseNormalScreenBuffer();
1212 			fBuffer->RestoreCursor();
1213 			break;
1214 	}
1215 }
1216