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