xref: /haiku/src/apps/terminal/TermParse.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 	int32 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: /* Bright / Bold     */
837 								fAttr |= FORECOLORED(8);
838 								fAttr |= FORESET;
839 								break;
840 
841 							case 4:	/* Underline	*/
842 								fAttr |= UNDERLINE;
843 								break;
844 
845 							case 5:
846 								fAttr |= BOLD;
847 								break;
848 
849 							case 7:	/* Inverse	*/
850 								fAttr |= INVERSE;
851 								break;
852 
853 							case 2:	/* Faint: decreased intensity */
854 							case 21:	/* Bright/Bold: off or Underline: Double */
855 							case 22:	/* Not Bold, not bright, not faint	*/
856 								fAttr &= ~(FORECOLORED(8) | BOLD);
857 								fAttr |= FORESET;
858 								break;
859 
860 							case 24:	/* Not Underline	*/
861 								fAttr &= ~UNDERLINE;
862 								break;
863 
864 							case 27:	/* Not Inverse	*/
865 								fAttr &= ~INVERSE;
866 								break;
867 
868 							case 30:
869 							case 31:
870 							case 32:
871 							case 33:
872 							case 34:
873 							case 35:
874 							case 36:
875 							case 37:
876 								fAttr &= ~FORECOLORED(7);
877 								fAttr |= FORECOLORED(param[row] - 30);
878 								fAttr |= FORESET;
879 								break;
880 
881 							case 38:
882 							{
883 								if (nparam != 3 || param[1] != 5)
884 									break;
885 								fAttr &= ~FORECOLORED(255 - 8);
886 								fAttr |= FORECOLORED(param[2]);
887 								fAttr |= FORESET;
888 
889 								row = nparam; // force exit of the parsing
890 
891 								break;
892 							}
893 
894 							case 39:
895 								fAttr &= ~FORESET;
896 								break;
897 
898 							case 40:
899 							case 41:
900 							case 42:
901 							case 43:
902 							case 44:
903 							case 45:
904 							case 46:
905 							case 47:
906 								fAttr &= ~BACKCOLOR;
907 								fAttr |= BACKCOLORED(param[row] - 40);
908 								fAttr |= BACKSET;
909 								break;
910 
911 							case 48:
912 							{
913 								if (nparam != 3 || param[1] != 5)
914 									break;
915 								fAttr &= ~BACKCOLOR;
916 								fAttr |= BACKCOLORED(param[2]);
917 								fAttr |= BACKSET;
918 
919 								row = nparam; // force exit of the parsing
920 
921 								break;
922 							}
923 
924 							case 49:
925 								fAttr &= ~BACKSET;
926 								break;
927 						}
928 					}
929 					parsestate = groundtable;
930 					break;
931 				}
932 
933 				case CASE_CPR:
934 				// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
935 				// 21-JUL-99
936 				_DeviceStatusReport(param[0]);
937 				parsestate = groundtable;
938 				break;
939 
940 				case CASE_DA1:
941 				// DA - report device attributes
942 				if (param[0] < 1) {
943 					// claim to be a VT102
944 					write(fFd, "\033[?6c", 5);
945 				}
946 				parsestate = groundtable;
947 				break;
948 
949 				case CASE_DECSTBM:
950 				/* DECSTBM - set scrolling region */
951 
952 				if ((top = param[0]) < 1)
953 					top = 1;
954 
955 				if (nparam < 2)
956 					bottom = fBuffer->Height();
957 				else
958 					bottom = param[1];
959 
960 				top--;
961 					bottom--;
962 
963 					if (bottom > top)
964 						fBuffer->SetScrollRegion(top, bottom);
965 
966 					parsestate = groundtable;
967 					break;
968 
969 				case CASE_DECREQTPARM:
970 					// DEXREQTPARM - request terminal parameters
971 					_DecReqTermParms(param[0]);
972 					parsestate = groundtable;
973 					break;
974 
975 				case CASE_DECSET:
976 					/* DECSET */
977 					for (int i = 0; i < nparam; i++)
978 						_DecPrivateModeSet(param[i]);
979 					parsestate = groundtable;
980 					break;
981 
982 				case CASE_DECRST:
983 					/* DECRST */
984 					for (int i = 0; i < nparam; i++)
985 						_DecPrivateModeReset(param[i]);
986 					parsestate = groundtable;
987 					break;
988 
989 				case CASE_DECALN:
990 					/* DECALN */
991 					fBuffer->FillScreen(UTF8Char('E'), 1, 0);
992 					parsestate = groundtable;
993 					break;
994 
995 					//	case CASE_GSETS:
996 					//		screen->gsets[scstype] = GSET(c) | cs96;
997 					//		parsestate = groundtable;
998 					//		break;
999 
1000 				case CASE_DECSC:
1001 					/* DECSC */
1002 					_DecSaveCursor();
1003 					parsestate = groundtable;
1004 					break;
1005 
1006 				case CASE_DECRC:
1007 					/* DECRC */
1008 					_DecRestoreCursor();
1009 					parsestate = groundtable;
1010 					break;
1011 
1012 				case CASE_HTS:
1013 					/* HTS */
1014 					fBuffer->SetTabStop(fBuffer->Cursor().x);
1015 					parsestate = groundtable;
1016 					break;
1017 
1018 				case CASE_TBC:
1019 					/* TBC */
1020 					if (param[0] < 1)
1021 						fBuffer->ClearTabStop(fBuffer->Cursor().x);
1022 					else if (param[0] == 3)
1023 						fBuffer->ClearAllTabStops();
1024 					parsestate = groundtable;
1025 					break;
1026 
1027 				case CASE_RI:
1028 					/* RI */
1029 					fBuffer->InsertRI();
1030 					parsestate = groundtable;
1031 					break;
1032 
1033 				case CASE_SS2:
1034 					/* SS2 */
1035 					curess = c;
1036 					parsestate = groundtable;
1037 					break;
1038 
1039 				case CASE_SS3:
1040 					/* SS3 */
1041 					curess = c;
1042 					parsestate = groundtable;
1043 					break;
1044 
1045 				case CASE_CSI_STATE:
1046 					/* enter csi state */
1047 					nparam = 1;
1048 					param[0] = DEFAULT;
1049 					parsestate = gCsiTable;
1050 					break;
1051 
1052 				case CASE_OSC:
1053 					{
1054 						/* Operating System Command: ESC ] */
1055 						char string[512];
1056 						uint32 len = 0;
1057 						uchar mode_char = _NextParseChar();
1058 						if (mode_char != '0'
1059 								&& mode_char != '1'
1060 								&& mode_char != '2') {
1061 							parsestate = groundtable;
1062 							break;
1063 						}
1064 						uchar currentChar = _NextParseChar();
1065 						while ((currentChar = _NextParseChar()) != 0x7) {
1066 							if (!isprint(currentChar & 0x7f)
1067 									|| len+2 >= sizeof(string))
1068 								break;
1069 							string[len++] = currentChar;
1070 						}
1071 						if (currentChar == 0x7) {
1072 							string[len] = '\0';
1073 							switch (mode_char) {
1074 								case '0':
1075 								case '2':
1076 									fBuffer->SetTitle(string);
1077 									break;
1078 								case '1':
1079 									break;
1080 							}
1081 						}
1082 						parsestate = groundtable;
1083 						break;
1084 					}
1085 
1086 				case CASE_RIS:		// ESC c ... Reset terminal.
1087 					break;
1088 
1089 				case CASE_LS2:
1090 					/* LS2 */
1091 					//      screen->curgl = 2;
1092 					parsestate = groundtable;
1093 					break;
1094 
1095 				case CASE_LS3:
1096 					/* LS3 */
1097 					//      screen->curgl = 3;
1098 					parsestate = groundtable;
1099 					break;
1100 
1101 				case CASE_LS3R:
1102 					/* LS3R */
1103 					//      screen->curgr = 3;
1104 					parsestate = groundtable;
1105 					break;
1106 
1107 				case CASE_LS2R:
1108 					/* LS2R */
1109 					//      screen->curgr = 2;
1110 					parsestate = groundtable;
1111 					break;
1112 
1113 				case CASE_LS1R:
1114 					/* LS1R */
1115 					//      screen->curgr = 1;
1116 					parsestate = groundtable;
1117 					break;
1118 
1119 				case CASE_VPA:		// ESC [...d move cursor absolute vertical
1120 					/* VPA (CV) */
1121 					if ((row = param[0]) < 1)
1122 						row = 1;
1123 
1124 					// note beterm wants it 1-based unlike usual terminals
1125 					fBuffer->SetCursorY(row - 1);
1126 					parsestate = groundtable;
1127 					break;
1128 
1129 				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
1130 					/* HPA (CH) */
1131 					if ((column = param[0]) < 1)
1132 						column = 1;
1133 
1134 					// note beterm wants it 1-based unlike usual terminals
1135 					fBuffer->SetCursorX(column - 1);
1136 					parsestate = groundtable;
1137 					break;
1138 
1139 				case CASE_SU:	// scroll screen up
1140 					if ((row = param[0]) < 1)
1141 						row = 1;
1142 					fBuffer->ScrollBy(row);
1143 					parsestate = groundtable;
1144 					break;
1145 
1146 				case CASE_SD:	// scroll screen down
1147 					if ((row = param[0]) < 1)
1148 						row = 1;
1149 					fBuffer->ScrollBy(-row);
1150 					parsestate = groundtable;
1151 					break;
1152 
1153 
1154 				case CASE_ECH:	// erase characters
1155 					if ((column = param[0]) < 1)
1156 						column = 1;
1157 					fBuffer->EraseChars(column);
1158 					parsestate = groundtable;
1159 					break;
1160 
1161 				default:
1162 					break;
1163 			}
1164 		} catch (...) {
1165 			break;
1166 		}
1167 	}
1168 
1169 	return B_OK;
1170 }
1171 
1172 
1173 /*static*/ int32
1174 TermParse::_ptyreader_thread(void *data)
1175 {
1176 	return reinterpret_cast<TermParse *>(data)->PtyReader();
1177 }
1178 
1179 
1180 /*static*/ int32
1181 TermParse::_escparse_thread(void *data)
1182 {
1183 	return reinterpret_cast<TermParse *>(data)->EscParse();
1184 }
1185 
1186 
1187 status_t
1188 TermParse::_ReadParserBuffer()
1189 {
1190 	// We have to unlock the terminal buffer while waiting for data from the
1191 	// PTY. We don't have to unlock when we don't need to wait, but we do it
1192 	// anyway, so that TermView won't be starved when trying to synchronize.
1193 	fBuffer->Unlock();
1194 
1195 	// wait for new input from pty
1196 	if (fReadBufferSize == 0) {
1197 		status_t status = B_OK;
1198 		while (fReadBufferSize == 0 && status == B_OK) {
1199 			do {
1200 				status = acquire_sem(fReaderSem);
1201 			} while (status == B_INTERRUPTED);
1202 
1203 			// eat any sems that were released unconditionally
1204 			int32 semCount;
1205 			if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0)
1206 				acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0);
1207 		}
1208 
1209 		if (status < B_OK) {
1210 			fBuffer->Lock();
1211 			return status;
1212 		}
1213 	}
1214 
1215 	int32 toRead = fReadBufferSize;
1216 	if (toRead > ESC_PARSER_BUFFER_SIZE)
1217 		toRead = ESC_PARSER_BUFFER_SIZE;
1218 
1219 	for (int32 i = 0; i < toRead; i++) {
1220 		// TODO: This could be optimized using memcpy instead and
1221 		// calculating space left as in the PtyReader().
1222 		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1223 		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1224 	}
1225 
1226 	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1227 
1228 	// If the pty reader thread waits and we have made enough space in the
1229 	// buffer now, let it run again.
1230 	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1231 			&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1232 		release_sem(fReaderLocker);
1233 	}
1234 
1235 	fParserBufferSize = toRead;
1236 	fParserBufferOffset = 0;
1237 
1238 	fBuffer->Lock();
1239 	return B_OK;
1240 }
1241 
1242 
1243 void
1244 TermParse::_DeviceStatusReport(int n)
1245 {
1246 	char sbuf[16] ;
1247 	int len;
1248 
1249 	switch (n) {
1250 		case 5:
1251 			{
1252 				// Device status report requested
1253 				// reply with "no malfunction detected"
1254 				const char* toWrite = "\033[0n";
1255 				write(fFd, toWrite, strlen(toWrite));
1256 				break ;
1257 			}
1258 		case 6:
1259 			// Cursor position report requested
1260 			len = sprintf(sbuf, "\033[%" B_PRId32 ";%" B_PRId32 "R",
1261 					fBuffer->Cursor().y + 1,
1262 					fBuffer->Cursor().x + 1);
1263 			write(fFd, sbuf, len);
1264 			break ;
1265 		default:
1266 			return;
1267 	}
1268 }
1269 
1270 
1271 void
1272 TermParse::_DecReqTermParms(int value)
1273 {
1274 	// Terminal parameters report:
1275 	//   type (2 or 3);
1276 	//   no parity (1);
1277 	//   8 bits per character (1);
1278 	//   transmit speed 38400bps (128);
1279 	//   receive speed 38400bps (128);
1280 	//   bit rate multiplier 16 (1);
1281 	//   no flags (0)
1282 	char parms[] = "\033[?;1;1;128;128;1;0x";
1283 
1284 	if (value < 1)
1285 		parms[2] = '2';
1286 	else if (value == 1)
1287 		parms[2] = '3';
1288 	else
1289 		return;
1290 
1291 	write(fFd, parms, strlen(parms));
1292 }
1293 
1294 
1295 void
1296 TermParse::_DecPrivateModeSet(int value)
1297 {
1298 	switch (value) {
1299 		case 1:
1300 			// Application Cursor Keys (whatever that means).
1301 			// Not supported yet.
1302 			break;
1303 		case 5:
1304 			// Reverse Video (inverses colors for the complete screen
1305 			// -- when followed by normal video, that's shortly flashes the
1306 			// screen).
1307 			// Not supported yet.
1308 			break;
1309 		case 6:
1310 			// Set Origin Mode.
1311 			fBuffer->SetOriginMode(true);
1312 			break;
1313 		case 9:
1314 			// Set Mouse X and Y on button press.
1315 			fBuffer->ReportX10MouseEvent(true);
1316 			break;
1317 		case 12:
1318 			// Start Blinking Cursor.
1319 			// Not supported yet.
1320 			break;
1321 		case 25:
1322 			// Show Cursor.
1323 			// Not supported yet.
1324 			break;
1325 		case 47:
1326 			// Use Alternate Screen Buffer.
1327 			fBuffer->UseAlternateScreenBuffer(false);
1328 			break;
1329 		case 1000:
1330 			// Send Mouse X & Y on button press and release.
1331 			fBuffer->ReportNormalMouseEvent(true);
1332 			break;
1333 		case 1002:
1334 			// Send Mouse X and Y on button press and release, and on motion
1335 			// when the mouse enter a new cell
1336 			fBuffer->ReportButtonMouseEvent(true);
1337 			break;
1338 		case 1003:
1339 			// Use All Motion Mouse Tracking
1340 			fBuffer->ReportAnyMouseEvent(true);
1341 			break;
1342 		case 1034:
1343 			// TODO: Interprete "meta" key, sets eighth bit.
1344 			// Not supported yet.
1345 			break;
1346 		case 1036:
1347 			// TODO: Send ESC when Meta modifies a key
1348 			// Not supported yet.
1349 			break;
1350 		case 1039:
1351 			// TODO: Send ESC when Alt modifies a key
1352 			// Not supported yet.
1353 			break;
1354 		case 1049:
1355 			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1356 			// it first.
1357 			_DecSaveCursor();
1358 			fBuffer->UseAlternateScreenBuffer(true);
1359 			break;
1360 	}
1361 }
1362 
1363 
1364 void
1365 TermParse::_DecPrivateModeReset(int value)
1366 {
1367 	switch (value) {
1368 		case 1:
1369 			// Normal Cursor Keys (whatever that means).
1370 			// Not supported yet.
1371 			break;
1372 		case 3:
1373 			// 80 Column Mode.
1374 			// Not supported yet.
1375 			break;
1376 		case 4:
1377 			// Jump (Fast) Scroll.
1378 			// Not supported yet.
1379 			break;
1380 		case 5:
1381 			// Normal Video (Leaves Reverse Video, cf. there).
1382 			// Not supported yet.
1383 			break;
1384 		case 6:
1385 			// Reset Origin Mode.
1386 			fBuffer->SetOriginMode(false);
1387 			break;
1388 		case 9:
1389 			// Disable Mouse X and Y on button press.
1390 			fBuffer->ReportX10MouseEvent(false);
1391 			break;
1392 		case 12:
1393 			// Stop Blinking Cursor.
1394 			// Not supported yet.
1395 			break;
1396 		case 25:
1397 			// Hide Cursor
1398 			// Not supported yet.
1399 			break;
1400 		case 47:
1401 			// Use Normal Screen Buffer.
1402 			fBuffer->UseNormalScreenBuffer();
1403 			break;
1404 		case 1000:
1405 			// Don't send Mouse X & Y on button press and release.
1406 			fBuffer->ReportNormalMouseEvent(false);
1407 			break;
1408 		case 1002:
1409 			// Don't send Mouse X and Y on button press and release, and on motion
1410 			// when the mouse enter a new cell
1411 			fBuffer->ReportButtonMouseEvent(false);
1412 			break;
1413 		case 1003:
1414 			// Disable All Motion Mouse Tracking.
1415 			fBuffer->ReportAnyMouseEvent(false);
1416 			break;
1417 		case 1034:
1418 			// Don't interprete "meta" key.
1419 			// Not supported yet.
1420 			break;
1421 		case 1036:
1422 			// TODO: Don't send ESC when Meta modifies a key
1423 			// Not supported yet.
1424 			break;
1425 		case 1039:
1426 			// TODO: Don't send ESC when Alt modifies a key
1427 			// Not supported yet.
1428 			break;
1429 		case 1049:
1430 			// Use Normal Screen Buffer and restore cursor as in DECRC.
1431 			fBuffer->UseNormalScreenBuffer();
1432 			_DecRestoreCursor();
1433 			break;
1434 	}
1435 }
1436 
1437 
1438 void
1439 TermParse::_DecSaveCursor()
1440 {
1441 	fBuffer->SaveCursor();
1442 	fBuffer->SaveOriginMode();
1443 	fSavedAttr = fAttr;
1444 }
1445 
1446 
1447 void
1448 TermParse::_DecRestoreCursor()
1449 {
1450 	fBuffer->RestoreCursor();
1451 	fBuffer->RestoreOriginMode();
1452 	fAttr = fSavedAttr;
1453 }
1454