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