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