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