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