xref: /haiku/src/apps/terminal/TermParse.cpp (revision e3dc9757d27727652a9862b2a71ab928d5af57e8)
1 /*
2  * Copyright 2001-2024, Haiku, Inc. All rights reserved.
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
_NextParseChar()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 
TermParse(int fd)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 
~TermParse()97 TermParse::~TermParse()
98 {
99 	StopThreads();
100 }
101 
102 
103 status_t
StartThreads(TerminalBuffer * buffer)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
StopThreads()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
_InitTermParse()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
_InitPtyReader()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
_StopTermParse()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
_StopPtyReader()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
PtyReader()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
DumpState(int * groundtable,int * parsestate,uchar c)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 *
_GuessGroundTable(int encoding)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
EscParse()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 						case 3:
722 							fBuffer->EraseScrollback();
723 							break;
724 					}
725 					parsestate = groundtable;
726 					break;
727 
728 				case CASE_EL:		// ESC [ ...K delete line
729 					/* EL */
730 					switch (param[0]) {
731 						case DEFAULT:
732 						case 0:
733 							fBuffer->DeleteColumns();
734 							break;
735 
736 						case 1:
737 							fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1);
738 							break;
739 
740 						case 2:
741 							fBuffer->DeleteColumnsFrom(0);
742 							break;
743 					}
744 					parsestate = groundtable;
745 					break;
746 
747 				case CASE_IL:
748 					/* IL */
749 					if ((row = param[0]) < 1)
750 						row = 1;
751 					fBuffer->InsertLines(row);
752 					parsestate = groundtable;
753 					break;
754 
755 				case CASE_DL:
756 					/* DL */
757 					if ((row = param[0]) < 1)
758 						row = 1;
759 					fBuffer->DeleteLines(row);
760 					parsestate = groundtable;
761 					break;
762 
763 				case CASE_DCH:
764 					/* DCH */
765 					if ((row = param[0]) < 1)
766 						row = 1;
767 					fBuffer->DeleteChars(row);
768 					parsestate = groundtable;
769 					break;
770 
771 				case CASE_SET:
772 					/* SET */
773 					if (param[0] == 4)
774 						fBuffer->SetInsertMode(MODE_INSERT);
775 					parsestate = groundtable;
776 					break;
777 
778 				case CASE_RST:
779 					/* RST */
780 					if (param[0] == 4)
781 						fBuffer->SetInsertMode(MODE_OVER);
782 					parsestate = groundtable;
783 					break;
784 
785 				case CASE_SGR:
786 				{
787 					/* SGR */
788 					Attributes attributes = fBuffer->GetAttributes();
789 					for (row = 0; row < nparam; ++row) {
790 						switch (param[row]) {
791 							case DEFAULT:
792 							case 0: /* Reset attribute */
793 								attributes.Reset();
794 								break;
795 
796 							case 1: /* Bold     */
797 							case 5:
798 								attributes |= BOLD;
799 								break;
800 
801 							case 4:	/* Underline	*/
802 								if ((row + 1) < nparam) {
803 									row++;
804 									switch (param[row]) {
805 										case 0:
806 											attributes.UnsetUnder();
807 											break;
808 										case 1:
809 											attributes.SetUnder(SINGLE_UNDERLINE);
810 											break;
811 										case 2:
812 											attributes.SetUnder(DOUBLE_UNDERLINE);
813 											break;
814 										case 3:
815 											attributes.SetUnder(CURLY_UNDERLINE);
816 											break;
817 										case 4:
818 											attributes.SetUnder(DOTTED_UNDERLINE);
819 											break;
820 										case 5:
821 											attributes.SetUnder(DASHED_UNDERLINE);
822 											break;
823 										default:
824 											row = nparam; // force exit of the parsing
825 											break;
826 									}
827 								} else
828 									attributes.SetUnder(SINGLE_UNDERLINE);
829 								break;
830 
831 							case 7:	/* Inverse	*/
832 								attributes |= INVERSE;
833 								break;
834 
835 							case 21:	/* Double Underline	*/
836 								attributes.SetUnder(DOUBLE_UNDERLINE);
837 								break;
838 
839 							case 22:	/* Not Bold	*/
840 								attributes &= ~BOLD;
841 								break;
842 
843 							case 24:	/* Not Underline	*/
844 								attributes.UnsetUnder();
845 								break;
846 
847 							case 27:	/* Not Inverse	*/
848 								attributes &= ~INVERSE;
849 								break;
850 
851 							case 90:
852 							case 91:
853 							case 92:
854 							case 93:
855 							case 94:
856 							case 95:
857 							case 96:
858 							case 97:
859 								param[row] -= 60;
860 							case 30:
861 							case 31:
862 							case 32:
863 							case 33:
864 							case 34:
865 							case 35:
866 							case 36:
867 							case 37:
868 								attributes.SetIndexedForeground(param[row] - 30);
869 								break;
870 
871 							case 38:
872 							{
873 								if (nparam >= 3 && param[row+1] == 5) {
874 									attributes.SetIndexedForeground(param[row+2]);
875 									row += 2;
876 								} else if (nparam >= 5 && param[row+1] == 2) {
877 									attributes.SetDirectForeground(param[row+2], param[row+3], param[row+4]);
878 									row += 4;
879 								} else {
880 									row = nparam; // force exit of the parsing
881 								}
882 
883 								break;
884 							}
885 
886 							case 39:
887 								attributes.UnsetForeground();
888 								break;
889 
890 							case 100:
891 							case 101:
892 							case 102:
893 							case 103:
894 							case 104:
895 							case 105:
896 							case 106:
897 							case 107:
898 								param[row] -= 60;
899 							case 40:
900 							case 41:
901 							case 42:
902 							case 43:
903 							case 44:
904 							case 45:
905 							case 46:
906 							case 47:
907 								attributes.SetIndexedBackground(param[row] - 40);
908 								break;
909 
910 							case 48:
911 							{
912 								if (nparam >= 3 && param[row+1] == 5) {
913 									attributes.SetIndexedBackground(param[row+2]);
914 									row += 2;
915 								} else if (nparam >= 5 && param[row+1] == 2) {
916 									attributes.SetDirectBackground(param[row+2], param[row+3], param[row+4]);
917 									row += 4;
918 								} else {
919 									row = nparam; // force exit of the parsing
920 								}
921 
922 								break;
923 							}
924 
925 							case 49:
926 								attributes.UnsetBackground();
927 								break;
928 
929 							case 58:
930 							{
931 								if (nparam >= 3 && param[row+1] == 5) {
932 									attributes.SetIndexedUnderline(param[row+2]);
933 									row += 2;
934 								} else if (nparam >= 5 && param[row+1] == 2) {
935 									attributes.SetDirectUnderline(param[row+2], param[row+3], param[row+4]);
936 									row += 4;
937 								} else {
938 									row = nparam; // force exit of the parsing
939 								}
940 
941 								break;
942 							}
943 
944 							case 59:
945 								attributes.UnsetUnderline();
946 								break;
947 						}
948 					}
949 					fBuffer->SetAttributes(attributes);
950 					parsestate = groundtable;
951 					break;
952 				}
953 
954 				case CASE_CPR:
955 				// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
956 				// 21-JUL-99
957 				_DeviceStatusReport(param[0]);
958 				parsestate = groundtable;
959 				break;
960 
961 				case CASE_DA1:
962 				// DA - report device attributes
963 				if (param[0] < 1) {
964 					// claim to be a VT102
965 					write(fFd, "\033[?6c", 5);
966 				}
967 				parsestate = groundtable;
968 				break;
969 
970 				case CASE_DECSTBM:
971 				/* DECSTBM - set scrolling region */
972 
973 				if ((top = param[0]) < 1)
974 					top = 1;
975 
976 				if (nparam < 2)
977 					bottom = fBuffer->Height();
978 				else
979 					bottom = param[1];
980 
981 				top--;
982 					bottom--;
983 
984 					if (bottom > top)
985 						fBuffer->SetScrollRegion(top, bottom);
986 
987 					parsestate = groundtable;
988 					break;
989 
990 				case CASE_DECSCUSR_ETC:
991 				// DECSCUSR - set cursor style VT520
992 					if (nparam == 2 && param[1] == ' ') {
993 						bool blinking = (param[0] & 0x01) != 0;
994 						int style = -1;
995 						switch (param[0]) {
996 							case 0:
997 								blinking = true;
998 							case 1:
999 							case 2:
1000 								style = BLOCK_CURSOR;
1001 								break;
1002 							case 3:
1003 							case 4:
1004 								style = UNDERLINE_CURSOR;
1005 								break;
1006 							case 5:
1007 							case 6:
1008 								style = IBEAM_CURSOR;
1009 								break;
1010 						}
1011 
1012 						if (style != -1)
1013 							fBuffer->SetCursorStyle(style, blinking);
1014 					}
1015 					parsestate = groundtable;
1016 					break;
1017 
1018 				case CASE_DECREQTPARM:
1019 					// DEXREQTPARM - request terminal parameters
1020 					_DecReqTermParms(param[0]);
1021 					parsestate = groundtable;
1022 					break;
1023 
1024 				case CASE_DECSET:
1025 					/* DECSET */
1026 					for (int i = 0; i < nparam; i++)
1027 						_DecPrivateModeSet(param[i]);
1028 					parsestate = groundtable;
1029 					break;
1030 
1031 				case CASE_DECRST:
1032 					/* DECRST */
1033 					for (int i = 0; i < nparam; i++)
1034 						_DecPrivateModeReset(param[i]);
1035 					parsestate = groundtable;
1036 					break;
1037 
1038 				case CASE_DECALN:
1039 					/* DECALN */
1040 					{
1041 						Attributes attr;
1042 						fBuffer->FillScreen(UTF8Char('E'), attr);
1043 						parsestate = groundtable;
1044 					}
1045 					break;
1046 
1047 					//	case CASE_GSETS:
1048 					//		screen->gsets[scstype] = GSET(c) | cs96;
1049 					//		parsestate = groundtable;
1050 					//		break;
1051 
1052 				case CASE_DECSC:
1053 					/* DECSC */
1054 					fBuffer->SaveCursor();
1055 					parsestate = groundtable;
1056 					break;
1057 
1058 				case CASE_DECRC:
1059 					/* DECRC */
1060 					fBuffer->RestoreCursor();
1061 					parsestate = groundtable;
1062 					break;
1063 
1064 				case CASE_HTS:
1065 					/* HTS */
1066 					fBuffer->SetTabStop(fBuffer->Cursor().x);
1067 					parsestate = groundtable;
1068 					break;
1069 
1070 				case CASE_TBC:
1071 					/* TBC */
1072 					if (param[0] < 1)
1073 						fBuffer->ClearTabStop(fBuffer->Cursor().x);
1074 					else if (param[0] == 3)
1075 						fBuffer->ClearAllTabStops();
1076 					parsestate = groundtable;
1077 					break;
1078 
1079 				case CASE_RI:
1080 					/* RI */
1081 					fBuffer->InsertRI();
1082 					parsestate = groundtable;
1083 					break;
1084 
1085 				case CASE_SS2:
1086 					/* SS2 */
1087 					parsestate = groundtable;
1088 					break;
1089 
1090 				case CASE_SS3:
1091 					/* SS3 */
1092 					parsestate = groundtable;
1093 					break;
1094 
1095 				case CASE_CSI_STATE:
1096 					/* enter csi state */
1097 					nparam = 1;
1098 					param[0] = DEFAULT;
1099 					parsestate = gCsiTable;
1100 					break;
1101 
1102 				case CASE_OSC:
1103 					{
1104 						/* Operating System Command: ESC ] */
1105 						uchar params[512];
1106 						// fill the buffer until BEL, ST or something else.
1107 						bool isParsed = false;
1108 						int32 skipCount = 0; // take care about UTF-8 characters
1109 						for (uint i = 0; !isParsed && i < sizeof(params); i++) {
1110 							params[i] = _NextParseChar();
1111 
1112 							if (skipCount > 0) {
1113 								skipCount--;
1114 								continue;
1115 							}
1116 
1117 							skipCount = UTF8Char::ByteCount(params[i]) - 1;
1118 							if (skipCount > 0)
1119 								continue;
1120 
1121 							switch (params[i]) {
1122 								// BEL
1123 								case 0x07:
1124 									isParsed = true;
1125 									break;
1126 								// 8-bit ST
1127 								case 0x9c:
1128 									isParsed = true;
1129 									break;
1130 								// 7-bit ST is "ESC \"
1131 								case '\\':
1132 								// hm... Was \x1b replaced by 0 during parsing?
1133 									if (i > 0 && params[i - 1] == 0) {
1134 										isParsed = true;
1135 										break;
1136 									}
1137 								default:
1138 									if (!isprint(params[i] & 0x7f))
1139 										break;
1140 									continue;
1141 							}
1142 							params[i] = '\0';
1143 						}
1144 
1145 						// watchdog for the 'end of buffer' case
1146 						params[sizeof(params) - 1] = '\0';
1147 
1148 						if (isParsed)
1149 							_ProcessOperatingSystemControls(params);
1150 
1151 						parsestate = groundtable;
1152 						break;
1153 					}
1154 
1155 				case CASE_RIS:		// ESC c ... Reset terminal.
1156 					break;
1157 
1158 				case CASE_LS2:
1159 					/* select G2 into GL */
1160 					curGL = 2;
1161 					parsestate = groundtable;
1162 					break;
1163 
1164 				case CASE_LS3:
1165 					/* select G3 into GL */
1166 					curGL = 3;
1167 					parsestate = groundtable;
1168 					break;
1169 
1170 				case CASE_LS3R:
1171 					/* select G3 into GR */
1172 					curGR = 3;
1173 					parsestate = groundtable;
1174 					break;
1175 
1176 				case CASE_LS2R:
1177 					/* select G2 into GR */
1178 					curGR = 2;
1179 					parsestate = groundtable;
1180 					break;
1181 
1182 				case CASE_LS1R:
1183 					/* select G1 into GR */
1184 					curGR = 1;
1185 					parsestate = groundtable;
1186 					break;
1187 
1188 				case CASE_VPA:		// ESC [...d move cursor absolute vertical
1189 					/* VPA (CV) */
1190 					if ((row = param[0]) < 1)
1191 						row = 1;
1192 
1193 					// note beterm wants it 1-based unlike usual terminals
1194 					fBuffer->SetCursorY(row - 1);
1195 					parsestate = groundtable;
1196 					break;
1197 
1198 				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
1199 					/* HPA (CH) */
1200 					if ((column = param[0]) < 1)
1201 						column = 1;
1202 
1203 					// note beterm wants it 1-based unlike usual terminals
1204 					fBuffer->SetCursorX(column - 1);
1205 					parsestate = groundtable;
1206 					break;
1207 
1208 				case CASE_SU:	// scroll screen up
1209 					if ((row = param[0]) < 1)
1210 						row = 1;
1211 					fBuffer->ScrollBy(row);
1212 					parsestate = groundtable;
1213 					break;
1214 
1215 				case CASE_SD:	// scroll screen down
1216 					if ((row = param[0]) < 1)
1217 						row = 1;
1218 					fBuffer->ScrollBy(-row);
1219 					parsestate = groundtable;
1220 					break;
1221 
1222 
1223 				case CASE_ECH:	// erase characters
1224 					if ((column = param[0]) < 1)
1225 						column = 1;
1226 					fBuffer->EraseChars(column);
1227 					parsestate = groundtable;
1228 					break;
1229 
1230 				case CASE_CBT:	// cursor back tab
1231 					if ((column = param[0]) < 1)
1232 						column = 1;
1233 					fBuffer->InsertCursorBackTab(column);
1234 					parsestate = groundtable;
1235 					break;
1236 
1237 				case CASE_CFT:	// cursor forward tab
1238 					if ((column= param[0]) < 1)
1239 						column = 1;
1240 					for (int32 i = 0; i < column; ++i)
1241 						fBuffer->InsertTab();
1242 					parsestate = groundtable;
1243 					break;
1244 
1245 				case CASE_CNL:	// cursor next line
1246 					if ((row= param[0]) < 1)
1247 						row = 1;
1248 					fBuffer->SetCursorX(0);
1249 					fBuffer->MoveCursorDown(row);
1250 					parsestate = groundtable;
1251 					break;
1252 
1253 				case CASE_CPL:	// cursor previous line
1254 					if ((row= param[0]) < 1)
1255 						row = 1;
1256 					fBuffer->SetCursorX(0);
1257 					fBuffer->MoveCursorUp(row);
1258 					parsestate = groundtable;
1259 					break;
1260 
1261 				case CASE_REP:		// ESC [...b repeat last graphic char
1262 				{
1263 					int repetitions = param[0];
1264 					int maxRepetitions = fBuffer->Width() * fBuffer->Height();
1265 					if (repetitions > maxRepetitions)
1266 						repetitions = maxRepetitions;
1267 					for (int i = 0; i < repetitions; i++)
1268 						fBuffer->InsertLastChar();
1269 					parsestate = groundtable;
1270 					break;
1271 				}
1272 				default:
1273 					break;
1274 			}
1275 		} catch (...) {
1276 			break;
1277 		}
1278 	}
1279 
1280 	return B_OK;
1281 }
1282 
1283 
1284 /*static*/ int32
_ptyreader_thread(void * data)1285 TermParse::_ptyreader_thread(void *data)
1286 {
1287 	return reinterpret_cast<TermParse *>(data)->PtyReader();
1288 }
1289 
1290 
1291 /*static*/ int32
_escparse_thread(void * data)1292 TermParse::_escparse_thread(void *data)
1293 {
1294 	return reinterpret_cast<TermParse *>(data)->EscParse();
1295 }
1296 
1297 
1298 status_t
_ReadParserBuffer()1299 TermParse::_ReadParserBuffer()
1300 {
1301 	// We have to unlock the terminal buffer while waiting for data from the
1302 	// PTY. We don't have to unlock when we don't need to wait, but we do it
1303 	// anyway, so that TermView won't be starved when trying to synchronize.
1304 	fBuffer->Unlock();
1305 
1306 	// wait for new input from pty
1307 	if (atomic_get(&fReadBufferSize) == 0) {
1308 		status_t status = B_OK;
1309 		while (atomic_get(&fReadBufferSize) == 0 && status == B_OK) {
1310 			do {
1311 				status = acquire_sem(fReaderSem);
1312 			} while (status == B_INTERRUPTED);
1313 
1314 			// eat any sems that were released unconditionally
1315 			int32 semCount;
1316 			if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0)
1317 				acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0);
1318 		}
1319 
1320 		if (status < B_OK) {
1321 			fBuffer->Lock();
1322 			return status;
1323 		}
1324 	}
1325 
1326 	int32 toRead = atomic_get(&fReadBufferSize);
1327 	if (toRead > ESC_PARSER_BUFFER_SIZE)
1328 		toRead = ESC_PARSER_BUFFER_SIZE;
1329 
1330 	for (int32 i = 0; i < toRead; i++) {
1331 		// TODO: This could be optimized using memcpy instead and
1332 		// calculating space left as in the PtyReader().
1333 		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1334 		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1335 	}
1336 
1337 	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1338 
1339 	// If the pty reader thread waits and we have made enough space in the
1340 	// buffer now, let it run again.
1341 	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1342 			&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1343 		release_sem(fReaderLocker);
1344 	}
1345 
1346 	fParserBufferSize = toRead;
1347 	fParserBufferOffset = 0;
1348 
1349 	fBuffer->Lock();
1350 	return B_OK;
1351 }
1352 
1353 
1354 void
_DeviceStatusReport(int n)1355 TermParse::_DeviceStatusReport(int n)
1356 {
1357 	char sbuf[16] ;
1358 	int len;
1359 
1360 	switch (n) {
1361 		case 5:
1362 			{
1363 				// Device status report requested
1364 				// reply with "no malfunction detected"
1365 				const char* toWrite = "\033[0n";
1366 				write(fFd, toWrite, strlen(toWrite));
1367 				break ;
1368 			}
1369 		case 6:
1370 			// Cursor position report requested
1371 			len = snprintf(sbuf, sizeof(sbuf),
1372 				"\033[%" B_PRId32 ";%" B_PRId32 "R",
1373 				fBuffer->Cursor().y + 1,
1374 				fBuffer->Cursor().x + 1);
1375 			write(fFd, sbuf, len);
1376 			break ;
1377 		default:
1378 			return;
1379 	}
1380 }
1381 
1382 
1383 void
_DecReqTermParms(int value)1384 TermParse::_DecReqTermParms(int value)
1385 {
1386 	// Terminal parameters report:
1387 	//   type (2 or 3);
1388 	//   no parity (1);
1389 	//   8 bits per character (1);
1390 	//   transmit speed 38400bps (128);
1391 	//   receive speed 38400bps (128);
1392 	//   bit rate multiplier 16 (1);
1393 	//   no flags (0)
1394 	char parms[] = "\033[?;1;1;128;128;1;0x";
1395 
1396 	if (value < 1)
1397 		parms[2] = '2';
1398 	else if (value == 1)
1399 		parms[2] = '3';
1400 	else
1401 		return;
1402 
1403 	write(fFd, parms, strlen(parms));
1404 }
1405 
1406 
1407 void
_DecPrivateModeSet(int value)1408 TermParse::_DecPrivateModeSet(int value)
1409 {
1410 	switch (value) {
1411 		case 1:
1412 			// Application Cursor Keys (whatever that means).
1413 			// Not supported yet.
1414 			break;
1415 		case 5:
1416 			// Reverse Video (inverses colors for the complete screen
1417 			// -- when followed by normal video, that's shortly flashes the
1418 			// screen).
1419 			// Not supported yet.
1420 			break;
1421 		case 6:
1422 			// Set Origin Mode.
1423 			fBuffer->SetOriginMode(true);
1424 			break;
1425 		case 9:
1426 			// Set Mouse X and Y on button press.
1427 			fBuffer->ReportX10MouseEvent(true);
1428 			break;
1429 		case 12:
1430 			// Start Blinking Cursor.
1431 			fBuffer->SetCursorBlinking(true);
1432 			break;
1433 		case 25:
1434 			// Show Cursor.
1435 			fBuffer->SetCursorHidden(false);
1436 			break;
1437 		case 47:
1438 			// Use Alternate Screen Buffer.
1439 			fBuffer->UseAlternateScreenBuffer(false);
1440 			break;
1441 		case 1000:
1442 			// Send Mouse X & Y on button press and release.
1443 			fBuffer->ReportNormalMouseEvent(true);
1444 			break;
1445 		case 1002:
1446 			// Send Mouse X and Y on button press and release, and on motion
1447 			// when the mouse enter a new cell
1448 			fBuffer->ReportButtonMouseEvent(true);
1449 			break;
1450 		case 1003:
1451 			// Use All Motion Mouse Tracking
1452 			fBuffer->ReportAnyMouseEvent(true);
1453 			break;
1454 		case 1006:
1455 			// Enable extended mouse coordinates with SGR scheme
1456 			fBuffer->EnableExtendedMouseCoordinates(true);
1457 			break;
1458 		case 1034:
1459 			// Interpret "meta" key, sets eighth bit.
1460 			fBuffer->EnableInterpretMetaKey(true);
1461 			break;
1462 		case 1036:
1463 			// Send ESC when Meta modifies a key
1464 			fBuffer->EnableMetaKeySendsEscape(true);
1465 			break;
1466 		case 1039:
1467 			// TODO: Send ESC when Alt modifies a key
1468 			// Not supported yet.
1469 			break;
1470 		case 1049:
1471 			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1472 			// it first.
1473 			fBuffer->SaveCursor();
1474 			fBuffer->UseAlternateScreenBuffer(true);
1475 			break;
1476 		case 2004:
1477 			// Enable bracketed paste mode
1478 			fBuffer->EnableBracketedPasteMode(true);
1479 			break;
1480 	}
1481 }
1482 
1483 
1484 void
_DecPrivateModeReset(int value)1485 TermParse::_DecPrivateModeReset(int value)
1486 {
1487 	switch (value) {
1488 		case 1:
1489 			// Normal Cursor Keys (whatever that means).
1490 			// Not supported yet.
1491 			break;
1492 		case 3:
1493 			// 80 Column Mode.
1494 			// Not supported yet.
1495 			break;
1496 		case 4:
1497 			// Jump (Fast) Scroll.
1498 			// Not supported yet.
1499 			break;
1500 		case 5:
1501 			// Normal Video (Leaves Reverse Video, cf. there).
1502 			// Not supported yet.
1503 			break;
1504 		case 6:
1505 			// Reset Origin Mode.
1506 			fBuffer->SetOriginMode(false);
1507 			break;
1508 		case 9:
1509 			// Disable Mouse X and Y on button press.
1510 			fBuffer->ReportX10MouseEvent(false);
1511 			break;
1512 		case 12:
1513 			// Stop Blinking Cursor.
1514 			fBuffer->SetCursorBlinking(false);
1515 			break;
1516 		case 25:
1517 			// Hide Cursor
1518 			fBuffer->SetCursorHidden(true);
1519 			break;
1520 		case 47:
1521 			// Use Normal Screen Buffer.
1522 			fBuffer->UseNormalScreenBuffer();
1523 			break;
1524 		case 1000:
1525 			// Don't send Mouse X & Y on button press and release.
1526 			fBuffer->ReportNormalMouseEvent(false);
1527 			break;
1528 		case 1002:
1529 			// Don't send Mouse X and Y on button press and release, and on motion
1530 			// when the mouse enter a new cell
1531 			fBuffer->ReportButtonMouseEvent(false);
1532 			break;
1533 		case 1003:
1534 			// Disable All Motion Mouse Tracking.
1535 			fBuffer->ReportAnyMouseEvent(false);
1536 			break;
1537 		case 1006:
1538 			// Disable extended mouse coordinates with SGR scheme
1539 			fBuffer->EnableExtendedMouseCoordinates(false);
1540 			break;
1541 		case 1034:
1542 			// Don't interpret "meta" key.
1543 			fBuffer->EnableInterpretMetaKey(false);
1544 			break;
1545 		case 1036:
1546 			// Don't send ESC when Meta modifies a key
1547 			fBuffer->EnableMetaKeySendsEscape(false);
1548 			break;
1549 		case 1039:
1550 			// TODO: Don't send ESC when Alt modifies a key
1551 			// Not supported yet.
1552 			break;
1553 		case 1049:
1554 			// Use Normal Screen Buffer and restore cursor as in DECRC.
1555 			fBuffer->UseNormalScreenBuffer();
1556 			fBuffer->RestoreCursor();
1557 			break;
1558 		case 2004:
1559 			// Disable bracketed paste mode
1560 			fBuffer->EnableBracketedPasteMode(false);
1561 			break;
1562 	}
1563 }
1564 
1565 
1566 void
_ProcessOperatingSystemControls(uchar * params)1567 TermParse::_ProcessOperatingSystemControls(uchar* params)
1568 {
1569 	int mode = 0;
1570 	for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) {
1571 		mode *= 10;
1572 		mode += c - '0';
1573 	}
1574 
1575 	// eat the separator
1576 	if (*params == ';')
1577 		params++;
1578 
1579 	static uint8 indexes[kTermColorCount];
1580 	static rgb_color colors[kTermColorCount];
1581 
1582 	switch (mode) {
1583 		case 0: // icon name and window title
1584 		case 2: // window title
1585 			fBuffer->SetTitle((const char*)params);
1586 			break;
1587 		case 4: // set colors (0 - 255)
1588 		case 104: // reset colors (0 - 255)
1589 			{
1590 				bool reset = (mode / 100) == 1;
1591 
1592 				// colors can be in "idx1:name1;...;idxN:nameN;" sequence too!
1593 				uint32 count = 0;
1594 				char* p = strtok((char*)params, ";");
1595 				while (p != NULL && count < kTermColorCount) {
1596 					indexes[count] = atoi(p);
1597 
1598 					if (!reset) {
1599 						p = strtok(NULL, ";");
1600 						if (p == NULL)
1601 							break;
1602 
1603 						if (gXColorsTable.LookUpColor(p, &colors[count]) == B_OK)
1604 							count++;
1605 					} else
1606 						count++;
1607 
1608 					p = strtok(NULL, ";");
1609 				};
1610 
1611 				if (count > 0) {
1612 					if (!reset)
1613 						fBuffer->SetColors(indexes, colors, count);
1614 					else
1615 						fBuffer->ResetColors(indexes, count);
1616 				}
1617 			}
1618 			break;
1619 		// set dynamic colors (10 - 19)
1620 		case 10: // text foreground
1621 		case 11: // text background
1622 		case 12: // cursor back
1623 			{
1624 				int32 offset = mode - 10;
1625 				int32 count = 0;
1626 				if (strcmp((char*)params, "?") == 0) {
1627 					fBuffer->GetColor(mode);
1628 					break;
1629 				}
1630 				char* p = strtok((char*)params, ";");
1631 				do {
1632 					if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) {
1633 						// dyna-colors are pos-sensitive - no chance to continue
1634 						break;
1635 					}
1636 
1637 					indexes[count] = 10 + offset + count;
1638 					count++;
1639 					p = strtok(NULL, ";");
1640 
1641 				} while (p != NULL && (offset + count) < 10);
1642 
1643 				if (count > 0) {
1644 					fBuffer->SetColors(indexes, colors, count, true);
1645 				}
1646 			}
1647 			break;
1648 		// reset dynamic colors (10 - 19)
1649 		case 110: // text foreground
1650 		case 111: // text background
1651 		case 112: // cursor back
1652 			{
1653 				indexes[0] = mode;
1654 				fBuffer->ResetColors(indexes, 1, true);
1655 			}
1656 			break;
1657 		default:
1658 		//	printf("%d -> %s\n", mode, params);
1659 			break;
1660 	}
1661 }
1662