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