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