* Copyright 2001-2024, Haiku, Inc. All rights reserved.
* Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net>
* Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
* Distributed under the terms of the MIT license.
*
* Authors:
* Kian Duffy, myob@users.sourceforge.net
* Simon South, simon@simonsouth.net
* Siarzhuk Zharski, zharik@gmx.li
*/
#include "TermParse.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <Autolock.h>
#include <Beep.h>
#include <Catalog.h>
#include <Locale.h>
#include <Message.h>
#include <UTF8.h>
#include "Colors.h"
#include "TermConst.h"
#include "TerminalBuffer.h"
#include "VTparse.h"
extern int gUTF8GroundTable[];
extern int gISO8859GroundTable[];
extern int gWinCPGroundTable[];
extern int gSJISGroundTable[];
extern int gEscTable[];
extern int gCsiTable[];
extern int gDecTable[];
extern int gScrTable[];
extern int gIgnoreTable[];
extern int gIesTable[];
extern int gEscIgnoreTable[];
extern const char* gLineDrawGraphSet[];
#define DEFAULT -1
#define NPARAM 10 // Max parameters
inline uchar
TermParse::_NextParseChar()
{
if (fParserBufferOffset >= fParserBufferSize) {
status_t error = _ReadParserBuffer();
if (error != B_OK)
throw error;
}
#ifdef USE_DEBUG_SNAPSHOTS
fBuffer->CaptureChar(fParserBuffer[fParserBufferOffset]);
#endif
return fParserBuffer[fParserBufferOffset++];
}
TermParse::TermParse(int fd)
:
fFd(fd),
fParseThread(-1),
fReaderThread(-1),
fReaderSem(-1),
fReaderLocker(-1),
fBufferPosition(0),
fReadBufferSize(0),
fParserBufferSize(0),
fParserBufferOffset(0),
fBuffer(NULL),
fQuitting(true)
{
memset(fReadBuffer, 0, READ_BUF_SIZE);
memset(fParserBuffer, 0, ESC_PARSER_BUFFER_SIZE);
}
TermParse::~TermParse()
{
StopThreads();
}
status_t
TermParse::StartThreads(TerminalBuffer *buffer)
{
if (fBuffer != NULL)
return B_ERROR;
fQuitting = false;
fBuffer = buffer;
status_t status = _InitPtyReader();
if (status < B_OK) {
fBuffer = NULL;
return status;
}
status = _InitTermParse();
if (status < B_OK) {
_StopPtyReader();
fBuffer = NULL;
return status;
}
return B_OK;
}
status_t
TermParse::StopThreads()
{
if (fBuffer == NULL)
return B_ERROR;
fQuitting = true;
_StopPtyReader();
_StopTermParse();
fBuffer = NULL;
return B_OK;
}
status_t
TermParse::_InitTermParse()
{
if (fParseThread >= 0)
return B_ERROR;
fParseThread = spawn_thread(_escparse_thread, "EscParse",
B_DISPLAY_PRIORITY, this);
if (fParseThread < 0)
return fParseThread;
resume_thread(fParseThread);
return B_OK;
}
status_t
TermParse::_InitPtyReader()
{
if (fReaderThread >= 0)
return B_ERROR;
fReaderSem = create_sem(0, "pty_reader_sem");
if (fReaderSem < 0)
return fReaderSem;
fReaderLocker = create_sem(0, "pty_locker_sem");
if (fReaderLocker < 0) {
delete_sem(fReaderSem);
fReaderSem = -1;
return fReaderLocker;
}
fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader",
B_NORMAL_PRIORITY, this);
if (fReaderThread < 0) {
delete_sem(fReaderSem);
fReaderSem = -1;
delete_sem(fReaderLocker);
fReaderLocker = -1;
return fReaderThread;
}
resume_thread(fReaderThread);
return B_OK;
}
void
TermParse::_StopTermParse()
{
if (fParseThread >= 0) {
status_t dummy;
wait_for_thread(fParseThread, &dummy);
fParseThread = -1;
}
}
void
TermParse::_StopPtyReader()
{
if (fReaderSem >= 0) {
delete_sem(fReaderSem);
fReaderSem = -1;
}
if (fReaderLocker >= 0) {
delete_sem(fReaderLocker);
fReaderLocker = -1;
}
if (fReaderThread >= 0) {
suspend_thread(fReaderThread);
status_t status;
wait_for_thread(fReaderThread, &status);
fReaderThread = -1;
}
}
int32
TermParse::PtyReader()
{
int32 bufferSize = 0;
int32 readPos = 0;
while (!fQuitting) {
while (READ_BUF_SIZE - bufferSize < MIN_PTY_BUFFER_SPACE) {
status_t status;
do {
status = acquire_sem(fReaderLocker);
} while (status == B_INTERRUPTED);
if (status < B_OK)
return status;
bufferSize = fReadBufferSize;
}
uchar buf[READ_BUF_SIZE];
ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize);
if (nread <= 0) {
fBuffer->NotifyQuit(errno);
return B_OK;
}
int32 left = READ_BUF_SIZE - readPos;
if (nread >= left) {
memcpy(fReadBuffer + readPos, buf, left);
memcpy(fReadBuffer, buf + left, nread - left);
} else
memcpy(fReadBuffer + readPos, buf, nread);
bufferSize = atomic_add(&fReadBufferSize, nread);
if (bufferSize == 0)
release_sem(fReaderSem);
bufferSize += nread;
readPos = (readPos + nread) % READ_BUF_SIZE;
}
return B_OK;
}
void
TermParse::DumpState(int *groundtable, int *parsestate, uchar c)
{
static const struct {
int *p;
const char *name;
} tables[] = {
#define T(t) \
{ t, #t }
T(gUTF8GroundTable),
T(gISO8859GroundTable),
T(gWinCPGroundTable),
T(gSJISGroundTable),
T(gEscTable),
T(gCsiTable),
T(gDecTable),
T(gScrTable),
T(gIgnoreTable),
T(gIesTable),
T(gEscIgnoreTable),
{ NULL, NULL }
};
int i;
fprintf(stderr, "groundtable: ");
for (i = 0; tables[i].p; i++) {
if (tables[i].p == groundtable)
fprintf(stderr, "%s\t", tables[i].name);
}
fprintf(stderr, "parsestate: ");
for (i = 0; tables[i].p; i++) {
if (tables[i].p == parsestate)
fprintf(stderr, "%s\t", tables[i].name);
}
fprintf(stderr, "char: 0x%02x (%d)\n", c, c);
}
int *
TermParse::_GuessGroundTable(int encoding)
{
switch (encoding) {
case B_ISO1_CONVERSION:
case B_ISO2_CONVERSION:
case B_ISO3_CONVERSION:
case B_ISO4_CONVERSION:
case B_ISO5_CONVERSION:
case B_ISO6_CONVERSION:
case B_ISO7_CONVERSION:
case B_ISO8_CONVERSION:
case B_ISO9_CONVERSION:
case B_ISO10_CONVERSION:
case B_ISO13_CONVERSION:
case B_ISO14_CONVERSION:
case B_ISO15_CONVERSION:
case B_EUC_CONVERSION:
case B_EUC_KR_CONVERSION:
case B_JIS_CONVERSION:
case B_BIG5_CONVERSION:
return gISO8859GroundTable;
case B_KOI8R_CONVERSION:
case B_MS_WINDOWS_1251_CONVERSION:
case B_MS_WINDOWS_CONVERSION:
case B_MAC_ROMAN_CONVERSION:
case B_MS_DOS_866_CONVERSION:
case B_GBK_CONVERSION:
case B_MS_DOS_CONVERSION:
return gWinCPGroundTable;
case B_SJIS_CONVERSION:
return gSJISGroundTable;
case M_UTF8:
default:
break;
}
return gUTF8GroundTable;
}
int32
TermParse::EscParse()
{
int top = 0;
int bottom = 0;
char cbuf[4] = { 0 };
char dstbuf[4] = { 0 };
int currentEncoding = -1;
int param[NPARAM];
int nparam = 1;
for (int i = 0; i < NPARAM; i++)
param[i] = DEFAULT;
int row = 0;
int column = 0;
int *groundtable = gUTF8GroundTable;
int *parsestate = gUTF8GroundTable;
const char** graphSets[4] = { NULL, NULL, NULL, NULL };
int curGL = 0;
int curGR = 0;
BAutolock locker(fBuffer);
while (!fQuitting) {
try {
uchar c = _NextParseChar();
if (currentEncoding != fBuffer->Encoding()) {
groundtable = _GuessGroundTable(fBuffer->Encoding());
parsestate = groundtable;
currentEncoding = fBuffer->Encoding();
}
int32 srcLen = 0;
int32 dstLen = sizeof(dstbuf);
int32 dummyState = 0;
switch (parsestate[c]) {
case CASE_PRINT:
{
int curGS = c < 128 ? curGL : curGR;
const char** curGraphSet = graphSets[curGS];
if (curGraphSet != NULL) {
int offset = c - (c < 128 ? 0x20 : 0xA0);
if (offset >= 0 && offset < 96
&& curGraphSet[offset] != 0) {
fBuffer->InsertChar(curGraphSet[offset]);
break;
}
}
fBuffer->InsertChar((char)c);
break;
}
case CASE_PRINT_GR:
{
switch (currentEncoding) {
case B_EUC_CONVERSION:
case B_EUC_KR_CONVERSION:
case B_JIS_CONVERSION:
case B_BIG5_CONVERSION:
cbuf[srcLen++] = c;
c = _NextParseChar();
cbuf[srcLen++] = c;
break;
case B_GBK_CONVERSION:
cbuf[srcLen++] = c;
do {
c = _NextParseChar();
cbuf[srcLen++] = c;
if (srcLen == 2 && (c < 0x30 || c > 0x39))
break;
} while (srcLen < 4);
break;
default:
cbuf[srcLen++] = c;
break;
}
if (srcLen > 0) {
int encoding = currentEncoding == B_JIS_CONVERSION
? B_EUC_CONVERSION : currentEncoding;
convert_to_utf8(encoding, cbuf, &srcLen,
dstbuf, &dstLen, &dummyState, '?');
fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
}
break;
}
case CASE_LF:
fBuffer->InsertLF();
break;
case CASE_CR:
fBuffer->InsertCR();
break;
case CASE_INDEX:
fBuffer->InsertLF();
parsestate = groundtable;
break;
case CASE_NEXT_LINE:
fBuffer->NextLine();
parsestate = groundtable;
break;
case CASE_SJIS_KANA:
cbuf[srcLen++] = c;
convert_to_utf8(currentEncoding, cbuf, &srcLen,
dstbuf, &dstLen, &dummyState, '?');
fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
break;
case CASE_SJIS_INSTRING:
cbuf[srcLen++] = c;
c = _NextParseChar();
cbuf[srcLen++] = c;
convert_to_utf8(currentEncoding, cbuf, &srcLen,
dstbuf, &dstLen, &dummyState, '?');
fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
break;
case CASE_UTF8_2BYTE:
cbuf[srcLen++] = c;
c = _NextParseChar();
if (groundtable[c] != CASE_UTF8_INSTRING)
break;
cbuf[srcLen++] = c;
fBuffer->InsertChar(UTF8Char(cbuf, srcLen));
break;
case CASE_UTF8_3BYTE:
cbuf[srcLen++] = c;
do {
c = _NextParseChar();
if (groundtable[c] != CASE_UTF8_INSTRING) {
srcLen = 0;
break;
}
cbuf[srcLen++] = c;
} while (srcLen != 3);
if (srcLen > 0)
fBuffer->InsertChar(UTF8Char(cbuf, srcLen));
break;
case CASE_SCS_STATE:
{
int set = -1;
switch (c) {
case '(':
set = 0;
break;
case ')':
case '-':
set = 1;
break;
case '*':
case '.':
set = 2;
break;
case '+':
case '/':
set = 3;
break;
default:
break;
}
if (set > -1) {
char page = _NextParseChar();
switch (page) {
case '0':
graphSets[set] = gLineDrawGraphSet;
break;
default:
graphSets[set] = NULL;
break;
}
}
parsestate = groundtable;
break;
}
case CASE_GROUND_STATE:
parsestate = groundtable;
break;
case CASE_BELL:
beep();
break;
case CASE_BS:
fBuffer->MoveCursorLeft(1);
break;
case CASE_TAB:
fBuffer->InsertTab();
break;
case CASE_ESC:
parsestate = gEscTable;
break;
case CASE_IGNORE_STATE:
parsestate = gIgnoreTable;
break;
case CASE_IGNORE_ESC:
parsestate = gIesTable;
break;
case CASE_IGNORE:
break;
case CASE_LS1:
curGL = 1;
parsestate = groundtable;
break;
case CASE_LS0:
curGL = 0;
parsestate = groundtable;
break;
case CASE_SCR_STATE:
parsestate = gScrTable;
break;
case CASE_ESC_IGNORE:
parsestate = gEscIgnoreTable;
break;
case CASE_ESC_DIGIT:
if ((row = param[nparam - 1]) == DEFAULT)
row = 0;
param[nparam - 1] = 10 * row + (c - '0');
break;
case CASE_ESC_SEMI:
if (nparam < NPARAM)
param[nparam++] = DEFAULT;
break;
case CASE_CSI_SP:
if (nparam < NPARAM)
param[nparam++] = ' ';
break;
case CASE_CSI_EXCL:
if (nparam < NPARAM)
param[nparam++] = '!';
break;
case CASE_DEC_DOL:
if (nparam < NPARAM)
param[nparam++] = '$';
break;
case CASE_DEC_STATE:
parsestate = gDecTable;
break;
case CASE_ICH:
if ((row = param[0]) < 1)
row = 1;
fBuffer->InsertSpace(row);
parsestate = groundtable;
break;
case CASE_CUU:
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorUp(row);
parsestate = groundtable;
break;
case CASE_CUD:
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorDown(row);
parsestate = groundtable;
break;
case CASE_CUF:
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorRight(row);
parsestate = groundtable;
break;
case CASE_CUB:
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorLeft(row);
parsestate = groundtable;
break;
case CASE_CUP:
if ((row = param[0]) < 1)
row = 1;
if (nparam < 2 || (column = param[1]) < 1)
column = 1;
fBuffer->SetCursor(column - 1, row - 1 );
parsestate = groundtable;
break;
case CASE_ED:
switch (param[0]) {
case DEFAULT:
case 0:
fBuffer->EraseBelow();
break;
case 1:
fBuffer->EraseAbove();
break;
case 2:
fBuffer->EraseAll();
break;
case 3:
fBuffer->EraseScrollback();
break;
}
parsestate = groundtable;
break;
case CASE_EL:
switch (param[0]) {
case DEFAULT:
case 0:
fBuffer->DeleteColumns();
break;
case 1:
fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1);
break;
case 2:
fBuffer->DeleteColumnsFrom(0);
break;
}
parsestate = groundtable;
break;
case CASE_IL:
if ((row = param[0]) < 1)
row = 1;
fBuffer->InsertLines(row);
parsestate = groundtable;
break;
case CASE_DL:
if ((row = param[0]) < 1)
row = 1;
fBuffer->DeleteLines(row);
parsestate = groundtable;
break;
case CASE_DCH:
if ((row = param[0]) < 1)
row = 1;
fBuffer->DeleteChars(row);
parsestate = groundtable;
break;
case CASE_SET:
if (param[0] == 4)
fBuffer->SetInsertMode(MODE_INSERT);
parsestate = groundtable;
break;
case CASE_RST:
if (param[0] == 4)
fBuffer->SetInsertMode(MODE_OVER);
parsestate = groundtable;
break;
case CASE_SGR:
{
Attributes attributes = fBuffer->GetAttributes();
for (row = 0; row < nparam; ++row) {
switch (param[row]) {
case DEFAULT:
case 0:
attributes.Reset();
break;
case 1:
case 5:
attributes |= BOLD;
break;
case 4:
if ((row + 1) < nparam) {
row++;
switch (param[row]) {
case 0:
attributes.UnsetUnder();
break;
case 1:
attributes.SetUnder(SINGLE_UNDERLINE);
break;
case 2:
attributes.SetUnder(DOUBLE_UNDERLINE);
break;
case 3:
attributes.SetUnder(CURLY_UNDERLINE);
break;
case 4:
attributes.SetUnder(DOTTED_UNDERLINE);
break;
case 5:
attributes.SetUnder(DASHED_UNDERLINE);
break;
default:
row = nparam;
break;
}
} else
attributes.SetUnder(SINGLE_UNDERLINE);
break;
case 7:
attributes |= INVERSE;
break;
case 8:
attributes |= HIDDEN;
break;
case 21:
attributes.SetUnder(DOUBLE_UNDERLINE);
break;
case 22:
attributes &= ~BOLD;
break;
case 24:
attributes.UnsetUnder();
break;
case 27:
attributes &= ~INVERSE;
break;
case 28:
attributes &= ~HIDDEN;
break;
case 53:
attributes |= OVERLINE;
break;
case 55:
attributes &= ~OVERLINE;
break;
case 90:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
param[row] -= 60;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
attributes.SetIndexedForeground(param[row] - 30);
break;
case 38:
{
if (nparam >= 3 && param[row+1] == 5) {
attributes.SetIndexedForeground(param[row+2]);
row += 2;
} else if (nparam >= 5 && param[row+1] == 2) {
attributes.SetDirectForeground(param[row+2], param[row+3], param[row+4]);
row += 4;
} else {
row = nparam;
}
break;
}
case 39:
attributes.UnsetForeground();
break;
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
param[row] -= 60;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
attributes.SetIndexedBackground(param[row] - 40);
break;
case 48:
{
if (nparam >= 3 && param[row+1] == 5) {
attributes.SetIndexedBackground(param[row+2]);
row += 2;
} else if (nparam >= 5 && param[row+1] == 2) {
attributes.SetDirectBackground(param[row+2], param[row+3], param[row+4]);
row += 4;
} else {
row = nparam;
}
break;
}
case 49:
attributes.UnsetBackground();
break;
case 58:
{
if (nparam >= 3 && param[row+1] == 5) {
attributes.SetIndexedUnderline(param[row+2]);
row += 2;
} else if (nparam >= 5 && param[row+1] == 2) {
attributes.SetDirectUnderline(param[row+2], param[row+3], param[row+4]);
row += 4;
} else {
row = nparam;
}
break;
}
case 59:
attributes.UnsetUnderline();
break;
}
}
fBuffer->SetAttributes(attributes);
parsestate = groundtable;
break;
}
case CASE_CPR:
_DeviceStatusReport(param[0]);
parsestate = groundtable;
break;
case CASE_DA1:
if (param[0] < 1) {
write(fFd, "\033[?6c", 5);
}
parsestate = groundtable;
break;
case CASE_DECSTBM:
if ((top = param[0]) < 1)
top = 1;
if (nparam < 2)
bottom = fBuffer->Height();
else
bottom = param[1];
top--;
bottom--;
if (bottom > top)
fBuffer->SetScrollRegion(top, bottom);
parsestate = groundtable;
break;
case CASE_DECSCUSR_ETC:
if (nparam == 2 && param[1] == ' ') {
bool blinking = (param[0] & 0x01) != 0;
int style = -1;
switch (param[0]) {
case 0:
blinking = true;
case 1:
case 2:
style = BLOCK_CURSOR;
break;
case 3:
case 4:
style = UNDERLINE_CURSOR;
break;
case 5:
case 6:
style = IBEAM_CURSOR;
break;
}
if (style != -1) {
fBuffer->SetCursorStyle(style);
if (blinking)
fBuffer->SetMode(MODE_CURSOR_BLINKING);
else
fBuffer->ResetMode(MODE_CURSOR_BLINKING);
}
}
parsestate = groundtable;
break;
case CASE_DECREQTPARM:
_DecReqTermParms(param[0]);
parsestate = groundtable;
break;
case CASE_DECSET:
for (int i = 0; i < nparam; i++)
_DecPrivateModeSet(param[i]);
parsestate = groundtable;
break;
case CASE_DECRST:
for (int i = 0; i < nparam; i++)
_DecPrivateModeReset(param[i]);
parsestate = groundtable;
break;
case CASE_DECALN:
{
Attributes attr;
fBuffer->FillScreen(UTF8Char('E'), attr);
parsestate = groundtable;
}
break;
case CASE_DECSC:
fBuffer->SaveCursor();
parsestate = groundtable;
break;
case CASE_DECRC:
fBuffer->RestoreCursor();
parsestate = groundtable;
break;
case CASE_HTS:
fBuffer->SetTabStop(fBuffer->Cursor().x);
parsestate = groundtable;
break;
case CASE_TBC:
if (param[0] < 1)
fBuffer->ClearTabStop(fBuffer->Cursor().x);
else if (param[0] == 3)
fBuffer->ClearAllTabStops();
parsestate = groundtable;
break;
case CASE_RI:
fBuffer->InsertRI();
parsestate = groundtable;
break;
case CASE_SS2:
parsestate = groundtable;
break;
case CASE_SS3:
parsestate = groundtable;
break;
case CASE_CSI_STATE:
nparam = 1;
param[0] = DEFAULT;
parsestate = gCsiTable;
break;
case CASE_OSC:
{
uchar params[512];
bool isParsed = false;
int32 skipCount = 0;
for (uint i = 0; !isParsed && i < sizeof(params); i++) {
params[i] = _NextParseChar();
if (skipCount > 0) {
skipCount--;
continue;
}
skipCount = UTF8Char::ByteCount(params[i]) - 1;
if (skipCount > 0)
continue;
switch (params[i]) {
case 0x07:
isParsed = true;
break;
case 0x9c:
isParsed = true;
break;
case '\\':
if (i > 0 && params[i - 1] == 0) {
isParsed = true;
break;
}
default:
if (!isprint(params[i] & 0x7f))
break;
continue;
}
params[i] = '\0';
}
params[sizeof(params) - 1] = '\0';
if (isParsed)
_ProcessOperatingSystemControls(params);
parsestate = groundtable;
break;
}
case CASE_RIS:
break;
case CASE_LS2:
curGL = 2;
parsestate = groundtable;
break;
case CASE_LS3:
curGL = 3;
parsestate = groundtable;
break;
case CASE_LS3R:
curGR = 3;
parsestate = groundtable;
break;
case CASE_LS2R:
curGR = 2;
parsestate = groundtable;
break;
case CASE_LS1R:
curGR = 1;
parsestate = groundtable;
break;
case CASE_VPA:
if ((row = param[0]) < 1)
row = 1;
fBuffer->SetCursorY(row - 1);
parsestate = groundtable;
break;
case CASE_HPA:
if ((column = param[0]) < 1)
column = 1;
fBuffer->SetCursorX(column - 1);
parsestate = groundtable;
break;
case CASE_SU:
if ((row = param[0]) < 1)
row = 1;
fBuffer->ScrollBy(row);
parsestate = groundtable;
break;
case CASE_SD:
if ((row = param[0]) < 1)
row = 1;
fBuffer->ScrollBy(-row);
parsestate = groundtable;
break;
case CASE_ECH:
if ((column = param[0]) < 1)
column = 1;
fBuffer->EraseChars(column);
parsestate = groundtable;
break;
case CASE_CBT:
if ((column = param[0]) < 1)
column = 1;
fBuffer->InsertCursorBackTab(column);
parsestate = groundtable;
break;
case CASE_CFT:
if ((column = param[0]) < 1)
column = 1;
for (int32 i = 0; i < column; ++i)
fBuffer->InsertTab();
parsestate = groundtable;
break;
case CASE_CNL:
if ((row = param[0]) < 1)
row = 1;
fBuffer->SetCursorX(0);
fBuffer->MoveCursorDown(row);
parsestate = groundtable;
break;
case CASE_CPL:
if ((row = param[0]) < 1)
row = 1;
fBuffer->SetCursorX(0);
fBuffer->MoveCursorUp(row);
parsestate = groundtable;
break;
case CASE_REP:
{
int repetitions = param[0];
if (repetitions < 1)
repetitions = 1;
int maxRepetitions = fBuffer->Width() * fBuffer->Height();
if (repetitions > maxRepetitions)
repetitions = maxRepetitions;
for (int i = 0; i < repetitions; i++)
fBuffer->InsertLastChar();
parsestate = groundtable;
break;
}
case CASE_DECRQM:
if (nparam == 2 && param[1] == '$') {
_DecPrivateModeRequest(param[0]);
}
parsestate = groundtable;
break;
case CASE_DECSTR:
if (nparam == 2 && param[1] == '!') {
Attributes attributes;
fBuffer->SetAttributes(attributes);
}
parsestate = groundtable;
break;
default:
break;
}
} catch (...) {
break;
}
}
return B_OK;
}
int32
TermParse::_ptyreader_thread(void *data)
{
return reinterpret_cast<TermParse *>(data)->PtyReader();
}
int32
TermParse::_escparse_thread(void *data)
{
return reinterpret_cast<TermParse *>(data)->EscParse();
}
status_t
TermParse::_ReadParserBuffer()
{
fBuffer->Unlock();
if (atomic_get(&fReadBufferSize) == 0) {
status_t status = B_OK;
while (atomic_get(&fReadBufferSize) == 0 && status == B_OK) {
do {
status = acquire_sem(fReaderSem);
} while (status == B_INTERRUPTED);
int32 semCount;
if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0)
acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0);
}
if (status < B_OK) {
fBuffer->Lock();
return status;
}
}
int32 toRead = atomic_get(&fReadBufferSize);
if (toRead > ESC_PARSER_BUFFER_SIZE)
toRead = ESC_PARSER_BUFFER_SIZE;
for (int32 i = 0; i < toRead; i++) {
fParserBuffer[i] = fReadBuffer[fBufferPosition];
fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
}
int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
release_sem(fReaderLocker);
}
fParserBufferSize = toRead;
fParserBufferOffset = 0;
fBuffer->Lock();
return B_OK;
}
void
TermParse::_DeviceStatusReport(int n)
{
char sbuf[16] ;
int len;
switch (n) {
case 5:
{
const char* toWrite = "\033[0n";
write(fFd, toWrite, strlen(toWrite));
break ;
}
case 6:
len = snprintf(sbuf, sizeof(sbuf),
"\033[%" B_PRId32 ";%" B_PRId32 "R",
fBuffer->Cursor().y + 1,
fBuffer->Cursor().x + 1);
write(fFd, sbuf, len);
break ;
default:
return;
}
}
void
TermParse::_DecReqTermParms(int value)
{
char parms[] = "\033[?;1;1;128;128;1;0x";
if (value < 1)
parms[2] = '2';
else if (value == 1)
parms[2] = '3';
else
return;
write(fFd, parms, strlen(parms));
}
void
TermParse::_DecPrivateModeSet(int value)
{
switch (value) {
case 1:
break;
case 5:
break;
case 6:
fBuffer->SetOriginMode(true);
break;
case 9:
fBuffer->SetMode(MODE_REPORT_X10_MOUSE_EVENT);
break;
case 12:
fBuffer->SetMode(MODE_CURSOR_BLINKING);
break;
case 25:
fBuffer->ResetMode(MODE_CURSOR_HIDDEN);
break;
case 47:
fBuffer->UseAlternateScreenBuffer(false);
break;
case 1000:
fBuffer->SetMode(MODE_REPORT_NORMAL_MOUSE_EVENT);
break;
case 1002:
fBuffer->SetMode(MODE_REPORT_BUTTON_MOUSE_EVENT);
break;
case 1003:
fBuffer->SetMode(MODE_REPORT_ANY_MOUSE_EVENT);
break;
case 1006:
fBuffer->SetMode(MODE_EXTENDED_MOUSE_COORDINATES);
break;
case 1034:
fBuffer->SetMode(MODE_INTERPRET_META_KEY);
break;
case 1036:
fBuffer->SetMode(MODE_META_KEY_SENDS_ESCAPE);
break;
case 1039:
break;
case 1049:
fBuffer->SaveCursor();
fBuffer->UseAlternateScreenBuffer(true);
break;
case 2004:
fBuffer->SetMode(MODE_BRACKETED_PASTE);
break;
}
}
void
TermParse::_DecPrivateModeReset(int value)
{
switch (value) {
case 1:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
fBuffer->SetOriginMode(false);
break;
case 9:
fBuffer->ResetMode(MODE_REPORT_X10_MOUSE_EVENT);
break;
case 12:
fBuffer->ResetMode(MODE_CURSOR_BLINKING);
break;
case 25:
fBuffer->SetMode(MODE_CURSOR_HIDDEN);
break;
case 47:
fBuffer->UseNormalScreenBuffer();
break;
case 1000:
fBuffer->ResetMode(MODE_REPORT_NORMAL_MOUSE_EVENT);
break;
case 1002:
fBuffer->ResetMode(MODE_REPORT_BUTTON_MOUSE_EVENT);
break;
case 1003:
fBuffer->ResetMode(MODE_REPORT_ANY_MOUSE_EVENT);
break;
case 1006:
fBuffer->ResetMode(MODE_EXTENDED_MOUSE_COORDINATES);
break;
case 1034:
fBuffer->ResetMode(MODE_INTERPRET_META_KEY);
break;
case 1036:
fBuffer->ResetMode(MODE_META_KEY_SENDS_ESCAPE);
break;
case 1039:
break;
case 1049:
fBuffer->UseNormalScreenBuffer();
fBuffer->RestoreCursor();
break;
case 2004:
fBuffer->ResetMode(MODE_BRACKETED_PASTE);
break;
}
}
void
TermParse::_DecPrivateModeRequest(int value)
{
BString reply;
switch (value) {
case 12:
reply.SetToFormat("\033[?12;%u$y\033\\",
fBuffer->IsMode(MODE_CURSOR_BLINKING) ? 1 : 2);
break;
case 1006:
reply.SetToFormat("\033[?1006;%u$y\033\\",
fBuffer->IsMode(MODE_EXTENDED_MOUSE_COORDINATES) ? 1 : 2);
break;
case 2004:
reply.SetToFormat("\033[?2004;%u$y\033\\",
fBuffer->IsMode(MODE_BRACKETED_PASTE) ? 1 : 2);
break;
default:
return;
}
_WriteReply(reply);
}
void
TermParse::_ProcessOperatingSystemControls(uchar* params)
{
int mode = 0;
for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) {
mode *= 10;
mode += c - '0';
}
if (*params == ';')
params++;
static uint8 indexes[kTermColorCount];
static rgb_color colors[kTermColorCount];
switch (mode) {
case 0:
case 2:
fBuffer->SetTitle((const char*)params);
break;
case 4:
case 104:
{
bool reset = (mode / 100) == 1;
uint32 count = 0;
char* p = strtok((char*)params, ";");
while (p != NULL && count < kTermColorCount) {
indexes[count] = atoi(p);
if (!reset) {
p = strtok(NULL, ";");
if (p == NULL)
break;
if (gXColorsTable.LookUpColor(p, &colors[count]) == B_OK)
count++;
} else
count++;
p = strtok(NULL, ";");
};
if (count > 0) {
if (!reset)
fBuffer->SetColors(indexes, colors, count);
else
fBuffer->ResetColors(indexes, count);
}
}
break;
case 10:
case 11:
case 12:
{
int32 offset = mode - 10;
int32 count = 0;
if (strcmp((char*)params, "?") == 0) {
fBuffer->GetColor(mode);
break;
}
char* p = strtok((char*)params, ";");
do {
if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) {
break;
}
indexes[count] = 10 + offset + count;
count++;
p = strtok(NULL, ";");
} while (p != NULL && (offset + count) < 10);
if (count > 0) {
fBuffer->SetColors(indexes, colors, count, true);
}
}
break;
case 110:
case 111:
case 112:
{
indexes[0] = mode;
fBuffer->ResetColors(indexes, 1, true);
}
break;
case 8:
{
char* id = NULL;
char* start = (char*)params;
char* end = strpbrk(start, ";:");
for (; end != NULL; start = end + 1) {
end = strpbrk(start, ";:");
if (end == NULL)
break;
if (end - start > 3 && strncmp(start, "id=", 3) == 0)
id = strndup(start + 3, end - start + 3);
if (*end == ';')
break;
}
if (end == NULL)
break;
BString uri(end + 1);
Attributes attributes = fBuffer->GetAttributes();
if (uri.IsEmpty()) {
attributes.SetHyperlink(0);
} else {
attributes.SetHyperlink(fBuffer->PutHyperLink(id, uri));
}
free(id);
fBuffer->SetAttributes(attributes);
break;
}
default:
break;
}
}
void
TermParse::_WriteReply(BString &reply)
{
write(fFd, reply.String(), reply.Length());
}