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