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