xref: /haiku/src/apps/terminal/TermParse.cpp (revision 60c26cd332a044bb9003091b9196cc404ebe5482)
1 /*
2  * Copyright 2001-2010, 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 
9 //! Escape sequence parse and character encoding.
10 
11 
12 #include "TermParse.h"
13 
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <signal.h>
18 #include <string.h>
19 #include <unistd.h>
20 
21 #include <Autolock.h>
22 #include <Beep.h>
23 #include <Catalog.h>
24 #include <Locale.h>
25 #include <Message.h>
26 #include <UTF8.h>
27 
28 #include "TermConst.h"
29 #include "TerminalBuffer.h"
30 #include "VTparse.h"
31 
32 
33 extern int gUTF8GroundTable[];		/* UTF8 Ground table */
34 extern int gCS96GroundTable[];		/* CS96 Ground table */
35 extern int gISO8859GroundTable[];	/* ISO8859 & EUC Ground table */
36 extern int gSJISGroundTable[];		/* Shift-JIS Ground table */
37 
38 extern int gEscTable[];				/* ESC */
39 extern int gCsiTable[];				/* ESC [ */
40 extern int gDecTable[];				/* ESC [ ? */
41 extern int gScrTable[];				/* ESC # */
42 extern int gIgnoreTable[];			/* ignore table */
43 extern int gIesTable[];				/* ignore ESC table */
44 extern int gEscIgnoreTable[];		/* ESC ignore table */
45 extern int gMbcsTable[];			/* ESC $ */
46 
47 
48 #define DEFAULT -1
49 #define NPARAM 10		// Max parameters
50 
51 
52 //! Get char from pty reader buffer.
53 inline uchar
54 TermParse::_NextParseChar()
55 {
56 	if (fParserBufferOffset >= fParserBufferSize) {
57 		// parser buffer empty
58 		status_t error = _ReadParserBuffer();
59 		if (error != B_OK)
60 			throw error;
61 	}
62 
63 	return fParserBuffer[fParserBufferOffset++];
64 }
65 
66 
67 TermParse::TermParse(int fd)
68 	:
69 	fFd(fd),
70 	fParseThread(-1),
71 	fReaderThread(-1),
72 	fReaderSem(-1),
73 	fReaderLocker(-1),
74 	fBufferPosition(0),
75 	fReadBufferSize(0),
76 	fParserBufferSize(0),
77 	fParserBufferOffset(0),
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 	if (fParseThread < 0)
144 		return fParseThread;
145 
146 	resume_thread(fParseThread);
147 
148 	return B_OK;
149 }
150 
151 
152 //! Initialize and spawn PtyReader thread.
153 status_t
154 TermParse::_InitPtyReader()
155 {
156 	if (fReaderThread >= 0)
157 		return B_ERROR; // same as above
158 
159 	fReaderSem = create_sem(0, "pty_reader_sem");
160 	if (fReaderSem < 0)
161 		return fReaderSem;
162 
163 	fReaderLocker = create_sem(0, "pty_locker_sem");
164 	if (fReaderLocker < 0) {
165 		delete_sem(fReaderSem);
166 		fReaderSem = -1;
167 		return fReaderLocker;
168 	}
169 
170 	fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader",
171 		B_NORMAL_PRIORITY, this);
172   	if (fReaderThread < 0) {
173 		delete_sem(fReaderSem);
174 		fReaderSem = -1;
175 		delete_sem(fReaderLocker);
176 		fReaderLocker = -1;
177 		return fReaderThread;
178 	}
179 
180 	resume_thread(fReaderThread);
181 
182 	return B_OK;
183 }
184 
185 
186 void
187 TermParse::_StopTermParse()
188 {
189 	if (fParseThread >= 0) {
190 		status_t dummy;
191 		wait_for_thread(fParseThread, &dummy);
192 		fParseThread = -1;
193 	}
194 }
195 
196 
197 void
198 TermParse::_StopPtyReader()
199 {
200 	if (fReaderSem >= 0) {
201 		delete_sem(fReaderSem);
202 		fReaderSem = -1;
203 	}
204 	if (fReaderLocker >= 0) {
205 		delete_sem(fReaderLocker);
206 		fReaderLocker = -1;
207 	}
208 
209 	if (fReaderThread >= 0) {
210 		suspend_thread(fReaderThread);
211 
212 		status_t status;
213 		wait_for_thread(fReaderThread, &status);
214 
215 		fReaderThread = -1;
216 	}
217 }
218 
219 
220 //! Get data from pty device.
221 int32
222 TermParse::PtyReader()
223 {
224 	int32 bufferSize = 0;
225 	int32 readPos = 0;
226 	while (!fQuitting) {
227 		// If Pty Buffer nearly full, snooze this thread, and continue.
228 		while (READ_BUF_SIZE - bufferSize < MIN_PTY_BUFFER_SPACE) {
229 			status_t status;
230 			do {
231 				status = acquire_sem(fReaderLocker);
232 			} while (status == B_INTERRUPTED);
233 			if (status < B_OK)
234 				return status;
235 
236 			bufferSize = fReadBufferSize;
237 		}
238 
239 		// Read PTY
240 		uchar buf[READ_BUF_SIZE];
241 		ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize);
242 		if (nread <= 0) {
243 			fBuffer->NotifyQuit(errno);
244 			return B_OK;
245 		}
246 
247 		// Copy read string to PtyBuffer.
248 
249 		int32 left = READ_BUF_SIZE - readPos;
250 
251 		if (nread >= left) {
252 			memcpy(fReadBuffer + readPos, buf, left);
253 			memcpy(fReadBuffer, buf + left, nread - left);
254 		} else
255 			memcpy(fReadBuffer + readPos, buf, nread);
256 
257 		bufferSize = atomic_add(&fReadBufferSize, nread);
258 		if (bufferSize == 0)
259 			release_sem(fReaderSem);
260 
261 		bufferSize += nread;
262 		readPos = (readPos + nread) % READ_BUF_SIZE;
263 	}
264 
265 	return B_OK;
266 }
267 
268 
269 void
270 TermParse::DumpState(int *groundtable, int *parsestate, uchar c)
271 {
272 	static const struct {
273 		int *p;
274 		const char *name;
275 	} tables[] = {
276 #define T(t) \
277 	{ t, #t }
278 		T(gUTF8GroundTable),
279 		T(gCS96GroundTable),
280 		T(gISO8859GroundTable),
281 		T(gSJISGroundTable),
282 		T(gEscTable),
283 		T(gCsiTable),
284 		T(gDecTable),
285 		T(gScrTable),
286 		T(gIgnoreTable),
287 		T(gIesTable),
288 		T(gEscIgnoreTable),
289 		T(gMbcsTable),
290 		{ NULL, NULL }
291 	};
292 	int i;
293 	fprintf(stderr, "groundtable: ");
294 	for (i = 0; tables[i].p; i++) {
295 		if (tables[i].p == groundtable)
296 			fprintf(stderr, "%s\t", tables[i].name);
297 	}
298 	fprintf(stderr, "parsestate: ");
299 	for (i = 0; tables[i].p; i++) {
300 		if (tables[i].p == parsestate)
301 			fprintf(stderr, "%s\t", tables[i].name);
302 	}
303 	fprintf(stderr, "char: 0x%02x (%d)\n", c, c);
304 }
305 
306 
307 int32
308 TermParse::EscParse()
309 {
310 	int top;
311 	int bottom;
312 	int cs96 = 0;
313 	uchar curess = 0;
314 
315 	char cbuf[4];
316 	char dstbuf[4];
317 	char *ptr;
318 
319 	int currentEncoding = -1;
320 
321 	int param[NPARAM];
322 	int nparam = 1;
323 
324 	int row;
325 	int column;
326 
327 	/* default encoding system is UTF8 */
328 	int *groundtable = gUTF8GroundTable;
329 	int *parsestate = gUTF8GroundTable;
330 
331 	int32 srcLen;
332 	int32 dstLen;
333 	long dummyState = 0;
334 
335 	int width = 1;
336 	BAutolock locker(fBuffer);
337 
338 	fAttr = fSavedAttr = BACKCOLOR;
339 
340 	while (!fQuitting) {
341 		try {
342 			uchar c = _NextParseChar();
343 
344 			//DumpState(groundtable, parsestate, c);
345 
346 			if (currentEncoding != fBuffer->Encoding()) {
347 				// Change coding, change parse table.
348 				switch (fBuffer->Encoding()) {
349 					case B_ISO1_CONVERSION:
350 					case B_ISO2_CONVERSION:
351 					case B_ISO3_CONVERSION:
352 					case B_ISO4_CONVERSION:
353 					case B_ISO5_CONVERSION:
354 					case B_ISO6_CONVERSION:
355 					case B_ISO7_CONVERSION:
356 					case B_ISO8_CONVERSION:
357 					case B_ISO9_CONVERSION:
358 					case B_ISO10_CONVERSION:
359 						groundtable = gISO8859GroundTable;
360 						break;
361 					case B_SJIS_CONVERSION:
362 						groundtable = gSJISGroundTable;
363 						break;
364 					case B_EUC_CONVERSION:
365 					case B_EUC_KR_CONVERSION:
366 					case B_JIS_CONVERSION:
367 					case B_GBK_CONVERSION:
368 					case B_BIG5_CONVERSION:
369 						groundtable = gISO8859GroundTable;
370 						break;
371 					case M_UTF8:
372 					default:
373 						groundtable = gUTF8GroundTable;
374 						break;
375 				}
376 				parsestate = groundtable;
377 				currentEncoding = fBuffer->Encoding();
378 			}
379 
380 	//debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]);
381 			switch (parsestate[c]) {
382 				case CASE_PRINT:
383 					fBuffer->InsertChar((char)c, fAttr);
384 					break;
385 
386 				case CASE_PRINT_GR:
387 					/* case iso8859 gr character, or euc */
388 					ptr = cbuf;
389 					if (currentEncoding == B_EUC_CONVERSION
390 						|| currentEncoding == B_EUC_KR_CONVERSION
391 						|| currentEncoding == B_JIS_CONVERSION
392 						|| currentEncoding == B_GBK_CONVERSION
393 						|| currentEncoding == B_BIG5_CONVERSION) {
394 						switch (parsestate[curess]) {
395 							case CASE_SS2:		/* JIS X 0201 */
396 								width = 1;
397 								*ptr++ = curess;
398 								*ptr++ = c;
399 								*ptr = 0;
400 								curess = 0;
401 								break;
402 
403 							case CASE_SS3:		/* JIS X 0212 */
404 								width = 1;
405 								*ptr++ = curess;
406 								*ptr++ = c;
407 								c = _NextParseChar();
408 								*ptr++ = c;
409 								*ptr = 0;
410 								curess = 0;
411 								break;
412 
413 							default:		/* JIS X 0208 */
414 								width = 2;
415 								*ptr++ = c;
416 								c = _NextParseChar();
417 								*ptr++ = c;
418 								*ptr = 0;
419 								break;
420 						}
421 					} else {
422 						/* ISO-8859-1...10 and MacRoman */
423 						*ptr++ = c;
424 						*ptr = 0;
425 					}
426 
427 					srcLen = strlen(cbuf);
428 					if (currentEncoding != B_JIS_CONVERSION) {
429 						convert_to_utf8(currentEncoding, cbuf, &srcLen,
430 							dstbuf, &dstLen, &dummyState, '?');
431 					} else {
432 						convert_to_utf8(B_EUC_CONVERSION, cbuf, &srcLen,
433 							dstbuf, &dstLen, &dummyState, '?');
434 					}
435 
436 					fBuffer->InsertChar(dstbuf, dstLen, width, fAttr);
437 					break;
438 
439 				case CASE_PRINT_CS96:
440 					cbuf[0] = c | 0x80;
441 					c = _NextParseChar();
442 					cbuf[1] = c | 0x80;
443 					cbuf[2] = 0;
444 					srcLen = 2;
445 					dstLen = 0;
446 					convert_to_utf8(B_EUC_CONVERSION, cbuf, &srcLen,
447 						dstbuf, &dstLen, &dummyState, '?');
448 					fBuffer->InsertChar(dstbuf, dstLen, fAttr);
449 					break;
450 
451 				case CASE_LF:
452 					fBuffer->InsertLF();
453 					break;
454 
455 				case CASE_CR:
456 					fBuffer->InsertCR();
457 					break;
458 
459 				case CASE_SJIS_KANA:
460 					cbuf[0] = c;
461 					cbuf[1] = '\0';
462 					srcLen = 1;
463 					dstLen = 0;
464 					convert_to_utf8(currentEncoding, cbuf, &srcLen,
465 						dstbuf, &dstLen, &dummyState, '?');
466 					fBuffer->InsertChar(dstbuf, dstLen, fAttr);
467 					break;
468 
469 				case CASE_SJIS_INSTRING:
470 					cbuf[0] = c;
471 					c = _NextParseChar();
472 					cbuf[1] = c;
473 					cbuf[2] = '\0';
474 					srcLen = 2;
475 					dstLen = 0;
476 					convert_to_utf8(currentEncoding, cbuf, &srcLen,
477 						dstbuf, &dstLen, &dummyState, '?');
478 					fBuffer->InsertChar(dstbuf, dstLen, fAttr);
479 					break;
480 
481 				case CASE_UTF8_2BYTE:
482 					cbuf[0] = c;
483 					c = _NextParseChar();
484 					if (groundtable[c] != CASE_UTF8_INSTRING)
485 						break;
486 					cbuf[1] = c;
487 					cbuf[2] = '\0';
488 
489 					fBuffer->InsertChar(cbuf, 2, fAttr);
490 					break;
491 
492 				case CASE_UTF8_3BYTE:
493 					cbuf[0] = c;
494 					c = _NextParseChar();
495 					if (groundtable[c] != CASE_UTF8_INSTRING)
496 						break;
497 					cbuf[1] = c;
498 
499 					c = _NextParseChar();
500 					if (groundtable[c] != CASE_UTF8_INSTRING)
501 						break;
502 					cbuf[2] = c;
503 					cbuf[3] = '\0';
504 					fBuffer->InsertChar(cbuf, 3, fAttr);
505 					break;
506 
507 				case CASE_MBCS:
508 					/* ESC $ */
509 					parsestate = gMbcsTable;
510 					break;
511 
512 				case CASE_GSETS:
513 					/* ESC $ ? */
514 					parsestate = gCS96GroundTable;
515 					cs96 = 1;
516 					break;
517 
518 				case CASE_SCS_STATE:
519 				{
520 					cs96 = 0;
521 					_NextParseChar();
522 						// skip next char
523 					parsestate = groundtable;
524 					break;
525 				}
526 				case CASE_GROUND_STATE:
527 					/* exit ignore mode */
528 					parsestate = groundtable;
529 					break;
530 
531 				case CASE_BELL:
532 					beep();
533 					break;
534 
535 				case CASE_BS:
536 					fBuffer->MoveCursorLeft(1);
537 					break;
538 
539 				case CASE_TAB:
540 					fBuffer->InsertTab();
541 					break;
542 
543 				case CASE_ESC:
544 					/* escape */
545 					parsestate = gEscTable;
546 					break;
547 
548 				case CASE_IGNORE_STATE:
549 					/* Ies: ignore anything else */
550 					parsestate = gIgnoreTable;
551 					break;
552 
553 				case CASE_IGNORE_ESC:
554 					/* Ign: escape */
555 					parsestate = gIesTable;
556 					break;
557 
558 				case CASE_IGNORE:
559 					/* Ignore character */
560 					break;
561 
562 				case CASE_SI:
563 					break;
564 
565 				case CASE_SO:
566 					break;
567 
568 				case CASE_SCR_STATE:	// ESC #
569 					/* enter scr state */
570 					parsestate = gScrTable;
571 					break;
572 
573 				case CASE_ESC_IGNORE:
574 					/* unknown escape sequence */
575 					parsestate = gEscIgnoreTable;
576 					break;
577 
578 				case CASE_ESC_DIGIT:	// ESC [ number
579 					/* digit in csi or dec mode */
580 					if ((row = param[nparam - 1]) == DEFAULT)
581 						row = 0;
582 					param[nparam - 1] = 10 * row + (c - '0');
583 					break;
584 
585 				case CASE_ESC_SEMI:		// ESC ;
586 					/* semicolon in csi or dec mode */
587 					if (nparam < NPARAM)
588 						param[nparam++] = DEFAULT;
589 					break;
590 
591 				case CASE_DEC_STATE:
592 					/* enter dec mode */
593 					parsestate = gDecTable;
594 					break;
595 
596 				case CASE_ICH:		// ESC [@ insert charactor
597 					/* ICH */
598 					if ((row = param[0]) < 1)
599 						row = 1;
600 					fBuffer->InsertSpace(row);
601 					parsestate = groundtable;
602 					break;
603 
604 				case CASE_CUU:		// ESC [A cursor up, up arrow key.
605 					/* CUU */
606 					if ((row = param[0]) < 1)
607 						row = 1;
608 					fBuffer->MoveCursorUp(row);
609 					parsestate = groundtable;
610 					break;
611 
612 				case CASE_CUD:		// ESC [B cursor down, down arrow key.
613 					/* CUD */
614 					if ((row = param[0]) < 1)
615 						row = 1;
616 					fBuffer->MoveCursorDown(row);
617 					parsestate = groundtable;
618 					break;
619 
620 				case CASE_CUF:		// ESC [C cursor forword
621 					/* CUF */
622 					if ((row = param[0]) < 1)
623 						row = 1;
624 					fBuffer->MoveCursorRight(row);
625 					parsestate = groundtable;
626 					break;
627 
628 				case CASE_CUB:		// ESC [D cursor backword
629 					/* CUB */
630 					if ((row = param[0]) < 1)
631 						row = 1;
632 					fBuffer->MoveCursorLeft(row);
633 					parsestate = groundtable;
634 					break;
635 
636 				case CASE_CUP:		// ESC [...H move cursor
637 					/* CUP | HVP */
638 					if ((row = param[0]) < 1)
639 						row = 1;
640 					if (nparam < 2 || (column = param[1]) < 1)
641 						column = 1;
642 
643 					fBuffer->SetCursor(column - 1, row - 1 );
644 					parsestate = groundtable;
645 					break;
646 
647 				case CASE_ED:		// ESC [ ...J clear screen
648 					/* ED */
649 					switch (param[0]) {
650 						case DEFAULT:
651 						case 0:
652 							fBuffer->EraseBelow();
653 							break;
654 
655 						case 1:
656 							fBuffer->EraseAbove();
657 							break;
658 
659 						case 2:
660 							fBuffer->EraseAll();
661 							break;
662 					}
663 					parsestate = groundtable;
664 					break;
665 
666 				case CASE_EL:		// ESC [ ...K delete line
667 					/* EL */
668 					switch (param[0]) {
669 						case DEFAULT:
670 						case 0:
671 							fBuffer->DeleteColumns();
672 							break;
673 
674 						case 1:
675 							fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1);
676 							break;
677 
678 						case 2:
679 							fBuffer->DeleteColumnsFrom(0);
680 							break;
681 					}
682 					parsestate = groundtable;
683 					break;
684 
685 				case CASE_IL:
686 					/* IL */
687 					if ((row = param[0]) < 1)
688 						row = 1;
689 					fBuffer->InsertLines(row);
690 					parsestate = groundtable;
691 					break;
692 
693 				case CASE_DL:
694 					/* DL */
695 					if ((row = param[0]) < 1)
696 						row = 1;
697 					fBuffer->DeleteLines(row);
698 					parsestate = groundtable;
699 					break;
700 
701 				case CASE_DCH:
702 					/* DCH */
703 					if ((row = param[0]) < 1)
704 						row = 1;
705 					fBuffer->DeleteChars(row);
706 					parsestate = groundtable;
707 					break;
708 
709 				case CASE_SET:
710 					/* SET */
711 					if (param[0] == 4)
712 						fBuffer->SetInsertMode(MODE_INSERT);
713 					parsestate = groundtable;
714 					break;
715 
716 				case CASE_RST:
717 					/* RST */
718 					if (param[0] == 4)
719 						fBuffer->SetInsertMode(MODE_OVER);
720 					parsestate = groundtable;
721 					break;
722 
723 				case CASE_SGR:
724 					/* SGR */
725 					for (row = 0; row < nparam; ++row) {
726 						switch (param[row]) {
727 							case DEFAULT:
728 							case 0: /* Reset attribute */
729 								fAttr = 0;
730 								break;
731 
732 							case 1:
733 							case 5:	/* Bold		*/
734 								fAttr |= BOLD;
735 								break;
736 
737 							case 4:	/* Underline	*/
738 								fAttr |= UNDERLINE;
739 								break;
740 
741 							case 7:	/* Inverse	*/
742 								fAttr |= INVERSE;
743 								break;
744 
745 							case 22:	/* Not Bold	*/
746 								fAttr &= ~BOLD;
747 								break;
748 
749 							case 24:	/* Not Underline	*/
750 								fAttr &= ~UNDERLINE;
751 								break;
752 
753 							case 27:	/* Not Inverse	*/
754 								fAttr &= ~INVERSE;
755 								break;
756 
757 							case 30:
758 							case 31:
759 							case 32:
760 							case 33:
761 							case 34:
762 							case 35:
763 							case 36:
764 							case 37:
765 								fAttr &= ~FORECOLOR;
766 								fAttr |= FORECOLORED(param[row] - 30);
767 								fAttr |= FORESET;
768 								break;
769 
770 							case 39:
771 								fAttr &= ~FORESET;
772 								break;
773 
774 							case 40:
775 							case 41:
776 							case 42:
777 							case 43:
778 							case 44:
779 							case 45:
780 							case 46:
781 							case 47:
782 								fAttr &= ~BACKCOLOR;
783 								fAttr |= BACKCOLORED(param[row] - 40);
784 								fAttr |= BACKSET;
785 								break;
786 
787 							case 49:
788 								fAttr &= ~BACKSET;
789 								break;
790 						}
791 					}
792 					parsestate = groundtable;
793 					break;
794 
795 					case CASE_CPR:
796 						// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
797 						// 21-JUL-99
798 						_DeviceStatusReport(param[0]);
799 						parsestate = groundtable;
800 						break;
801 
802 					case CASE_DA1:
803 						// DA - report device attributes
804 						if (param[0] < 1) {
805 							// claim to be a VT102
806 							write(fFd, "\033[?6c", 5);
807 						}
808 						parsestate = groundtable;
809 						break;
810 
811 					case CASE_DECSTBM:
812 						/* DECSTBM - set scrolling region */
813 
814 						if ((top = param[0]) < 1)
815 							top = 1;
816 
817 						if (nparam < 2)
818 							bottom = fBuffer->Height();
819 						else
820 							bottom = param[1];
821 
822 						top--;
823 						bottom--;
824 
825 						if (bottom > top)
826 							fBuffer->SetScrollRegion(top, bottom);
827 
828 						parsestate = groundtable;
829 						break;
830 
831 					case CASE_DECREQTPARM:
832 						// DEXREQTPARM - request terminal parameters
833 						_DecReqTermParms(param[0]);
834 						parsestate = groundtable;
835 						break;
836 
837 					case CASE_DECSET:
838 						/* DECSET */
839 						for (int i = 0; i < nparam; i++)
840 							_DecPrivateModeSet(param[i]);
841 						parsestate = groundtable;
842 						break;
843 
844 					case CASE_DECRST:
845 						/* DECRST */
846 						for (int i = 0; i < nparam; i++)
847 							_DecPrivateModeReset(param[i]);
848 						parsestate = groundtable;
849 						break;
850 
851 					case CASE_DECALN:
852 						/* DECALN */
853 						fBuffer->FillScreen(UTF8Char('E'), 1, 0);
854 						parsestate = groundtable;
855 						break;
856 
857 				//	case CASE_GSETS:
858 				//		screen->gsets[scstype] = GSET(c) | cs96;
859 				//		parsestate = groundtable;
860 				//		break;
861 
862 					case CASE_DECSC:
863 						/* DECSC */
864 						_DecSaveCursor();
865 						parsestate = groundtable;
866 						break;
867 
868 					case CASE_DECRC:
869 						/* DECRC */
870 						_DecRestoreCursor();
871 						parsestate = groundtable;
872 						break;
873 
874 					case CASE_HTS:
875 						/* HTS */
876 						fBuffer->SetTabStop(fBuffer->Cursor().x);
877 						parsestate = groundtable;
878 						break;
879 
880 					case CASE_TBC:
881 						/* TBC */
882 						if (param[0] < 1)
883 							fBuffer->ClearTabStop(fBuffer->Cursor().x);
884 						else if (param[0] == 3)
885 							fBuffer->ClearAllTabStops();
886 						parsestate = groundtable;
887 						break;
888 
889 					case CASE_RI:
890 						/* RI */
891 						fBuffer->InsertRI();
892 						parsestate = groundtable;
893 						break;
894 
895 					case CASE_SS2:
896 						/* SS2 */
897 						curess = c;
898 						parsestate = groundtable;
899 						break;
900 
901 					case CASE_SS3:
902 						/* SS3 */
903 						curess = c;
904 						parsestate = groundtable;
905 						break;
906 
907 					case CASE_CSI_STATE:
908 						/* enter csi state */
909 						nparam = 1;
910 						param[0] = DEFAULT;
911 						parsestate = gCsiTable;
912 						break;
913 
914 					case CASE_OSC:
915 					{
916 						/* Operating System Command: ESC ] */
917 						char string[512];
918 						uint32 len = 0;
919 						uchar mode_char = _NextParseChar();
920 						if (mode_char != '0'
921 							&& mode_char != '1'
922 							&& mode_char != '2') {
923 							parsestate = groundtable;
924 							break;
925 						}
926 						uchar currentChar = _NextParseChar();
927 						while ((currentChar = _NextParseChar()) != 0x7) {
928 							if (!isprint(currentChar & 0x7f)
929 								|| len+2 >= sizeof(string))
930 								break;
931 							string[len++] = currentChar;
932 						}
933 						if (currentChar == 0x7) {
934 							string[len] = '\0';
935 							switch (mode_char) {
936 								case '0':
937 								case '2':
938 									fBuffer->SetTitle(string);
939 									break;
940 								case '1':
941 									break;
942 							}
943 						}
944 						parsestate = groundtable;
945 						break;
946 					}
947 
948 					case CASE_RIS:		// ESC c ... Reset terminal.
949 						break;
950 
951 					case CASE_LS2:
952 						/* LS2 */
953 						//      screen->curgl = 2;
954 						parsestate = groundtable;
955 						break;
956 
957 					case CASE_LS3:
958 						/* LS3 */
959 						//      screen->curgl = 3;
960 						parsestate = groundtable;
961 						break;
962 
963 					case CASE_LS3R:
964 						/* LS3R */
965 						//      screen->curgr = 3;
966 						parsestate = groundtable;
967 						break;
968 
969 					case CASE_LS2R:
970 						/* LS2R */
971 						//      screen->curgr = 2;
972 						parsestate = groundtable;
973 						break;
974 
975 					case CASE_LS1R:
976 						/* LS1R */
977 						//      screen->curgr = 1;
978 						parsestate = groundtable;
979 						break;
980 
981 					case CASE_VPA:		// ESC [...d move cursor absolute vertical
982 						/* VPA (CV) */
983 						if ((row = param[0]) < 1)
984 							row = 1;
985 
986 						// note beterm wants it 1-based unlike usual terminals
987 						fBuffer->SetCursorY(row - 1);
988 						parsestate = groundtable;
989 						break;
990 
991 					case CASE_HPA:		// ESC [...G move cursor absolute horizontal
992 						/* HPA (CH) */
993 						if ((column = param[0]) < 1)
994 							column = 1;
995 
996 						// note beterm wants it 1-based unlike usual terminals
997 						fBuffer->SetCursorX(column - 1);
998 						parsestate = groundtable;
999 						break;
1000 
1001 					case CASE_SU:	// scroll screen up
1002 						if ((row = param[0]) < 1)
1003 							row = 1;
1004 						fBuffer->ScrollBy(row);
1005 						parsestate = groundtable;
1006 						break;
1007 
1008 					case CASE_SD:	// scroll screen down
1009 						if ((row = param[0]) < 1)
1010 							row = 1;
1011 						fBuffer->ScrollBy(-row);
1012 						parsestate = groundtable;
1013 						break;
1014 
1015 
1016 					case CASE_ECH:	// erase characters
1017 						if ((column = param[0]) < 1)
1018 							column = 1;
1019 						fBuffer->EraseChars(column);
1020 						parsestate = groundtable;
1021 						break;
1022 
1023 					default:
1024 						break;
1025 				}
1026 		} catch (...) {
1027 			break;
1028 		}
1029 	}
1030 
1031 	return B_OK;
1032 }
1033 
1034 
1035 /*static*/ int32
1036 TermParse::_ptyreader_thread(void *data)
1037 {
1038 	return reinterpret_cast<TermParse *>(data)->PtyReader();
1039 }
1040 
1041 
1042 /*static*/ int32
1043 TermParse::_escparse_thread(void *data)
1044 {
1045 	return reinterpret_cast<TermParse *>(data)->EscParse();
1046 }
1047 
1048 
1049 status_t
1050 TermParse::_ReadParserBuffer()
1051 {
1052 	// We have to unlock the terminal buffer while waiting for data from the
1053 	// PTY. We don't have to unlock when we don't need to wait, but we do it
1054 	// anyway, so that TermView won't be starved when trying to synchronize.
1055 	fBuffer->Unlock();
1056 
1057 	// wait for new input from pty
1058 	if (fReadBufferSize == 0) {
1059 		status_t status = B_OK;
1060 		while (fReadBufferSize == 0 && status == B_OK) {
1061 			do {
1062 				status = acquire_sem(fReaderSem);
1063 			} while (status == B_INTERRUPTED);
1064 
1065 			// eat any sems that were released unconditionally
1066 			int32 semCount;
1067 			if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0)
1068 				acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0);
1069 		}
1070 
1071 		if (status < B_OK) {
1072 			fBuffer->Lock();
1073 			return status;
1074 		}
1075 	}
1076 
1077 	int32 toRead = fReadBufferSize;
1078 	if (toRead > ESC_PARSER_BUFFER_SIZE)
1079 		toRead = ESC_PARSER_BUFFER_SIZE;
1080 
1081 	for (int32 i = 0; i < toRead; i++) {
1082 		// TODO: This could be optimized using memcpy instead and
1083 		// calculating space left as in the PtyReader().
1084 		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1085 		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1086 	}
1087 
1088 	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1089 
1090   	// If the pty reader thread waits and we have made enough space in the
1091 	// buffer now, let it run again.
1092 	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1093 		&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1094 		release_sem(fReaderLocker);
1095 	}
1096 
1097 	fParserBufferSize = toRead;
1098 	fParserBufferOffset = 0;
1099 
1100 	fBuffer->Lock();
1101 	return B_OK;
1102 }
1103 
1104 
1105 void
1106 TermParse::_DeviceStatusReport(int n)
1107 {
1108 	char sbuf[16] ;
1109 	int len;
1110 
1111 	switch (n) {
1112 		case 5:
1113 		{
1114 			// Device status report requested
1115 			// reply with "no malfunction detected"
1116 			const char* toWrite = "\033[0n";
1117 			write(fFd, toWrite, strlen(toWrite));
1118 			break ;
1119 		}
1120 		case 6:
1121 			// Cursor position report requested
1122 			len = sprintf(sbuf, "\033[%ld;%ldR",
1123 				fBuffer->Cursor().y + 1,
1124 				fBuffer->Cursor().x + 1);
1125 			write(fFd, sbuf, len);
1126 			break ;
1127 		default:
1128 			return;
1129 	}
1130 }
1131 
1132 
1133 void
1134 TermParse::_DecReqTermParms(int value)
1135 {
1136 	// Terminal parameters report:
1137 	//   type (2 or 3);
1138 	//   no parity (1);
1139 	//   8 bits per character (1);
1140 	//   transmit speed 38400bps (128);
1141 	//   receive speed 38400bps (128);
1142 	//   bit rate multiplier 16 (1);
1143 	//   no flags (0)
1144 	char parms[] = "\033[?;1;1;128;128;1;0x";
1145 
1146 	if (value < 1)
1147 		parms[2] = '2';
1148 	else if (value == 1)
1149 		parms[2] = '3';
1150 	else
1151 		return;
1152 
1153 	write(fFd, parms, strlen(parms));
1154 }
1155 
1156 
1157 void
1158 TermParse::_DecPrivateModeSet(int value)
1159 {
1160 	switch (value) {
1161 		case 1:
1162 			// Application Cursor Keys (whatever that means).
1163 			// Not supported yet.
1164 			break;
1165 		case 5:
1166 			// Reverse Video (inverses colors for the complete screen
1167 			// -- when followed by normal video, that's shortly flashes the
1168 			// screen).
1169 			// Not supported yet.
1170 			break;
1171 		case 6:
1172 			// Set Origin Mode.
1173 			fBuffer->SetOriginMode(true);
1174 			break;
1175 		case 9:
1176 			// Set Mouse X and Y on button press.
1177 			fBuffer->ReportX10MouseEvent(true);
1178 			break;
1179 		case 12:
1180 			// Start Blinking Cursor.
1181 			// Not supported yet.
1182 			break;
1183 		case 25:
1184 			// Show Cursor.
1185 			// Not supported yet.
1186 			break;
1187 		case 47:
1188 			// Use Alternate Screen Buffer.
1189 			fBuffer->UseAlternateScreenBuffer(false);
1190 			break;
1191 		case 1000:
1192 			// Send Mouse X & Y on button press and release.
1193 			fBuffer->ReportNormalMouseEvent(true);
1194 			break;
1195 		case 1002:
1196 			// Send Mouse X and Y on button press and release, and on motion
1197 			// when the mouse enter a new cell
1198 			fBuffer->ReportButtonMouseEvent(true);
1199 			break;
1200 		case 1003:
1201 			// Use All Motion Mouse Tracking
1202 			fBuffer->ReportAnyMouseEvent(true);
1203 			break;
1204 		case 1034:
1205 			// TODO: Interprete "meta" key, sets eighth bit.
1206 			// Not supported yet.
1207 			break;
1208 		case 1036:
1209 			// TODO: Send ESC when Meta modifies a key
1210 			// Not supported yet.
1211 			break;
1212 		case 1039:
1213 			// TODO: Send ESC when Alt modifies a key
1214 			// Not supported yet.
1215 			break;
1216 		case 1049:
1217 			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1218 			// it first.
1219 			_DecSaveCursor();
1220 			fBuffer->UseAlternateScreenBuffer(true);
1221 			break;
1222 	}
1223 }
1224 
1225 
1226 void
1227 TermParse::_DecPrivateModeReset(int value)
1228 {
1229 	switch (value) {
1230 		case 1:
1231 			// Normal Cursor Keys (whatever that means).
1232 			// Not supported yet.
1233 			break;
1234 		case 3:
1235 			// 80 Column Mode.
1236 			// Not supported yet.
1237 			break;
1238 		case 4:
1239 			// Jump (Fast) Scroll.
1240 			// Not supported yet.
1241 			break;
1242 		case 5:
1243 			// Normal Video (Leaves Reverse Video, cf. there).
1244 			// Not supported yet.
1245 			break;
1246 		case 6:
1247 			// Reset Origin Mode.
1248 			fBuffer->SetOriginMode(false);
1249 			break;
1250 		case 9:
1251 			// Disable Mouse X and Y on button press.
1252 			fBuffer->ReportX10MouseEvent(false);
1253 			break;
1254 		case 12:
1255 			// Stop Blinking Cursor.
1256 			// Not supported yet.
1257 			break;
1258 		case 25:
1259 			// Hide Cursor
1260 			// Not supported yet.
1261 			break;
1262 		case 47:
1263 			// Use Normal Screen Buffer.
1264 			fBuffer->UseNormalScreenBuffer();
1265 			break;
1266 		case 1000:
1267 			// Don't send Mouse X & Y on button press and release.
1268 			fBuffer->ReportNormalMouseEvent(false);
1269 			break;
1270 		case 1002:
1271 			// Don't send Mouse X and Y on button press and release, and on motion
1272 			// when the mouse enter a new cell
1273 			fBuffer->ReportButtonMouseEvent(false);
1274 			break;
1275 		case 1003:
1276 			// Disable All Motion Mouse Tracking.
1277 			fBuffer->ReportAnyMouseEvent(false);
1278 			break;
1279 		case 1034:
1280 			// Don't interprete "meta" key.
1281 			// Not supported yet.
1282 			break;
1283 		case 1036:
1284 			// TODO: Don't send ESC when Meta modifies a key
1285 			// Not supported yet.
1286 			break;
1287 		case 1039:
1288 			// TODO: Don't send ESC when Alt modifies a key
1289 			// Not supported yet.
1290 			break;
1291 		case 1049:
1292 			// Use Normal Screen Buffer and restore cursor as in DECRC.
1293 			fBuffer->UseNormalScreenBuffer();
1294 			_DecRestoreCursor();
1295 			break;
1296 	}
1297 }
1298 
1299 
1300 void
1301 TermParse::_DecSaveCursor()
1302 {
1303 	fBuffer->SaveCursor();
1304 	fBuffer->SaveOriginMode();
1305 	fSavedAttr = fAttr;
1306 }
1307 
1308 
1309 void
1310 TermParse::_DecRestoreCursor()
1311 {
1312 	fBuffer->RestoreCursor();
1313 	fBuffer->RestoreOriginMode();
1314 	fAttr = fSavedAttr;
1315 }
1316