Files
haiku-beta6/src/apps/terminal/TermParse.cpp
T
Adrien Destugues b2a3f5b114 Terminal: initialize some variables
This is harmless, the sequence of states ensures they would be
initialized when needed anyway, but Coverity decides otherwise.

Fixes #10131 / CID743879.
2014-11-18 15:51:41 +01:00

1542 lines
32 KiB
C++

/*
* Copyright 2001-2013, Haiku, Inc.
* Copyright (c) 2003-4 Kian Duffy <[email protected]>
* Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
* Distributed under the terms of the MIT license.
*
* Authors:
* Kian Duffy, [email protected]
* Siarzhuk Zharski, [email protected]
*/
//! Escape sequence parse and character encoding.
#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[]; /* UTF8 Ground table */
extern int gISO8859GroundTable[]; /* ISO8859 & EUC Ground table */
extern int gWinCPGroundTable[]; /* Windows cp1252, cp1251, koi-8r */
extern int gSJISGroundTable[]; /* Shift-JIS Ground table */
extern int gEscTable[]; /* ESC */
extern int gCsiTable[]; /* ESC [ */
extern int gDecTable[]; /* ESC [ ? */
extern int gScrTable[]; /* ESC # */
extern int gIgnoreTable[]; /* ignore table */
extern int gIesTable[]; /* ignore ESC table */
extern int gEscIgnoreTable[]; /* ESC ignore table */
extern const char* gLineDrawGraphSet[]; /* may be used for G0, G1, G2, G3 */
#define DEFAULT -1
#define NPARAM 10 // Max parameters
//! Get char from pty reader buffer.
inline uchar
TermParse::_NextParseChar()
{
if (fParserBufferOffset >= fParserBufferSize) {
// parser buffer empty
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;
}
//! Initialize and spawn EscParse thread.
status_t
TermParse::_InitTermParse()
{
if (fParseThread >= 0)
return B_ERROR; // we might want to return B_OK instead ?
fParseThread = spawn_thread(_escparse_thread, "EscParse",
B_DISPLAY_PRIORITY, this);
if (fParseThread < 0)
return fParseThread;
resume_thread(fParseThread);
return B_OK;
}
//! Initialize and spawn PtyReader thread.
status_t
TermParse::_InitPtyReader()
{
if (fReaderThread >= 0)
return B_ERROR; // same as above
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;
}
}
//! Get data from pty device.
int32
TermParse::PtyReader()
{
int32 bufferSize = 0;
int32 readPos = 0;
while (!fQuitting) {
// If Pty Buffer nearly full, snooze this thread, and continue.
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;
}
// Read PTY
uchar buf[READ_BUF_SIZE];
ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize);
if (nread <= 0) {
fBuffer->NotifyQuit(errno);
return B_OK;
}
// Copy read string to PtyBuffer.
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;
// default encoding system is UTF8
int *groundtable = gUTF8GroundTable;
int *parsestate = gUTF8GroundTable;
// handle alternative character sets G0 - G4
const char** graphSets[4] = { NULL, NULL, NULL, NULL };
int curGL = 0;
int curGR = 0;
BAutolock locker(fBuffer);
while (!fQuitting) {
try {
uchar c = _NextParseChar();
//DumpState(groundtable, parsestate, c);
if (currentEncoding != fBuffer->Encoding()) {
// Change coding, change parse table.
groundtable = _GuessGroundTable(fBuffer->Encoding());
parsestate = groundtable;
currentEncoding = fBuffer->Encoding();
}
//debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]);
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:
{
/* case iso8859 gr character, or euc */
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 {
// GBK-compatible codepoints are 2-bytes long
c = _NextParseChar();
cbuf[srcLen++] = c;
// GB18030 extends GBK with 4-byte codepoints
// using 2nd byte from range 0x30...0x39
if (srcLen == 2 && (c < 0x30 || c > 0x39))
break;
} while (srcLen < 4);
break;
default: // ISO-8859-1...10 and MacRoman
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_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:
/* exit ignore mode */
parsestate = groundtable;
break;
case CASE_BELL:
beep();
break;
case CASE_BS:
fBuffer->MoveCursorLeft(1);
break;
case CASE_TAB:
fBuffer->InsertTab();
break;
case CASE_ESC:
/* escape */
parsestate = gEscTable;
break;
case CASE_IGNORE_STATE:
/* Ies: ignore anything else */
parsestate = gIgnoreTable;
break;
case CASE_IGNORE_ESC:
/* Ign: escape */
parsestate = gIesTable;
break;
case CASE_IGNORE:
/* Ignore character */
break;
case CASE_LS1:
/* select G1 into GL */
curGL = 1;
parsestate = groundtable;
break;
case CASE_LS0:
/* select G0 into GL */
curGL = 0;
parsestate = groundtable;
break;
case CASE_SCR_STATE: // ESC #
/* enter scr state */
parsestate = gScrTable;
break;
case CASE_ESC_IGNORE:
/* unknown escape sequence */
parsestate = gEscIgnoreTable;
break;
case CASE_ESC_DIGIT: // ESC [ number
/* digit in csi or dec mode */
if ((row = param[nparam - 1]) == DEFAULT)
row = 0;
param[nparam - 1] = 10 * row + (c - '0');
break;
case CASE_ESC_SEMI: // ESC ;
/* semicolon in csi or dec mode */
if (nparam < NPARAM)
param[nparam++] = DEFAULT;
break;
case CASE_CSI_SP: // ESC [N q
// part of change cursor style DECSCUSR
if (nparam < NPARAM)
param[nparam++] = ' ';
break;
case CASE_DEC_STATE:
/* enter dec mode */
parsestate = gDecTable;
break;
case CASE_ICH: // ESC [@ insert charactor
/* ICH */
if ((row = param[0]) < 1)
row = 1;
fBuffer->InsertSpace(row);
parsestate = groundtable;
break;
case CASE_CUU: // ESC [A cursor up, up arrow key.
/* CUU */
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorUp(row);
parsestate = groundtable;
break;
case CASE_CUD: // ESC [B cursor down, down arrow key.
/* CUD */
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorDown(row);
parsestate = groundtable;
break;
case CASE_CUF: // ESC [C cursor forword
/* CUF */
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorRight(row);
parsestate = groundtable;
break;
case CASE_CUB: // ESC [D cursor backword
/* CUB */
if ((row = param[0]) < 1)
row = 1;
fBuffer->MoveCursorLeft(row);
parsestate = groundtable;
break;
case CASE_CUP: // ESC [...H move cursor
/* CUP | HVP */
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: // ESC [ ...J clear screen
/* ED */
switch (param[0]) {
case DEFAULT:
case 0:
fBuffer->EraseBelow();
break;
case 1:
fBuffer->EraseAbove();
break;
case 2:
fBuffer->EraseAll();
break;
}
parsestate = groundtable;
break;
case CASE_EL: // ESC [ ...K delete line
/* 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:
/* IL */
if ((row = param[0]) < 1)
row = 1;
fBuffer->InsertLines(row);
parsestate = groundtable;
break;
case CASE_DL:
/* DL */
if ((row = param[0]) < 1)
row = 1;
fBuffer->DeleteLines(row);
parsestate = groundtable;
break;
case CASE_DCH:
/* DCH */
if ((row = param[0]) < 1)
row = 1;
fBuffer->DeleteChars(row);
parsestate = groundtable;
break;
case CASE_SET:
/* SET */
if (param[0] == 4)
fBuffer->SetInsertMode(MODE_INSERT);
parsestate = groundtable;
break;
case CASE_RST:
/* RST */
if (param[0] == 4)
fBuffer->SetInsertMode(MODE_OVER);
parsestate = groundtable;
break;
case CASE_SGR:
{
/* SGR */
uint32 attributes = fBuffer->GetAttributes();
for (row = 0; row < nparam; ++row) {
switch (param[row]) {
case DEFAULT:
case 0: /* Reset attribute */
attributes = 0;
break;
case 1: /* Bold */
case 5:
attributes |= BOLD;
break;
case 4: /* Underline */
attributes |= UNDERLINE;
break;
case 7: /* Inverse */
attributes |= INVERSE;
break;
case 22: /* Not Bold */
attributes &= ~BOLD;
break;
case 24: /* Not Underline */
attributes &= ~UNDERLINE;
break;
case 27: /* Not Inverse */
attributes &= ~INVERSE;
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 &= ~FORECOLOR;
attributes |= FORECOLORED(param[row] - 30);
attributes |= FORESET;
break;
case 38:
{
int color = -1;
if (nparam == 3 && param[1] == 5)
color = param[2];
else if (nparam == 5 && param[1] == 2)
color = fBuffer->GuessPaletteColor(
param[2], param[3], param[4]);
if (color >= 0) {
attributes &= ~FORECOLOR;
attributes |= FORECOLORED(color);
attributes |= FORESET;
}
row = nparam; // force exit of the parsing
break;
}
case 39:
attributes &= ~FORESET;
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 &= ~BACKCOLOR;
attributes |= BACKCOLORED(param[row] - 40);
attributes |= BACKSET;
break;
case 48:
{
int color = -1;
if (nparam == 3 && param[1] == 5)
color = param[2];
else if (nparam == 5 && param[1] == 2)
color = fBuffer->GuessPaletteColor(
param[2], param[3], param[4]);
if (color >= 0) {
attributes &= ~BACKCOLOR;
attributes |= BACKCOLORED(color);
attributes |= BACKSET;
}
row = nparam; // force exit of the parsing
break;
}
case 49:
attributes &= ~BACKSET;
break;
}
}
fBuffer->SetAttributes(attributes);
parsestate = groundtable;
break;
}
case CASE_CPR:
// Q & D hack by Y.Hayakawa ([email protected])
// 21-JUL-99
_DeviceStatusReport(param[0]);
parsestate = groundtable;
break;
case CASE_DA1:
// DA - report device attributes
if (param[0] < 1) {
// claim to be a VT102
write(fFd, "\033[?6c", 5);
}
parsestate = groundtable;
break;
case CASE_DECSTBM:
/* DECSTBM - set scrolling region */
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:
// DECSCUSR - set cursor style VT520
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, blinking);
}
parsestate = groundtable;
break;
case CASE_DECREQTPARM:
// DEXREQTPARM - request terminal parameters
_DecReqTermParms(param[0]);
parsestate = groundtable;
break;
case CASE_DECSET:
/* DECSET */
for (int i = 0; i < nparam; i++)
_DecPrivateModeSet(param[i]);
parsestate = groundtable;
break;
case CASE_DECRST:
/* DECRST */
for (int i = 0; i < nparam; i++)
_DecPrivateModeReset(param[i]);
parsestate = groundtable;
break;
case CASE_DECALN:
/* DECALN */
fBuffer->FillScreen(UTF8Char('E'), 0);
parsestate = groundtable;
break;
// case CASE_GSETS:
// screen->gsets[scstype] = GSET(c) | cs96;
// parsestate = groundtable;
// break;
case CASE_DECSC:
/* DECSC */
fBuffer->SaveCursor();
parsestate = groundtable;
break;
case CASE_DECRC:
/* DECRC */
fBuffer->RestoreCursor();
parsestate = groundtable;
break;
case CASE_HTS:
/* HTS */
fBuffer->SetTabStop(fBuffer->Cursor().x);
parsestate = groundtable;
break;
case CASE_TBC:
/* TBC */
if (param[0] < 1)
fBuffer->ClearTabStop(fBuffer->Cursor().x);
else if (param[0] == 3)
fBuffer->ClearAllTabStops();
parsestate = groundtable;
break;
case CASE_RI:
/* RI */
fBuffer->InsertRI();
parsestate = groundtable;
break;
case CASE_SS2:
/* SS2 */
parsestate = groundtable;
break;
case CASE_SS3:
/* SS3 */
parsestate = groundtable;
break;
case CASE_CSI_STATE:
/* enter csi state */
nparam = 1;
param[0] = DEFAULT;
parsestate = gCsiTable;
break;
case CASE_OSC:
{
/* Operating System Command: ESC ] */
uchar params[512];
// fill the buffer until BEL, ST or something else.
bool isParsed = false;
int32 skipCount = 0; // take care about UTF-8 characters
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]) {
// BEL
case 0x07:
isParsed = true;
break;
// 8-bit ST
case 0x9c:
isParsed = true;
break;
// 7-bit ST is "ESC \"
case '\\':
// hm... Was \x1b replaced by 0 during parsing?
if (i > 0 && params[i - 1] == 0) {
isParsed = true;
break;
}
default:
if (!isprint(params[i] & 0x7f))
break;
continue;
}
params[i] = '\0';
}
// watchdog for the 'end of buffer' case
params[sizeof(params) - 1] = '\0';
if (isParsed)
_ProcessOperatingSystemControls(params);
parsestate = groundtable;
break;
}
case CASE_RIS: // ESC c ... Reset terminal.
break;
case CASE_LS2:
/* select G2 into GL */
curGL = 2;
parsestate = groundtable;
break;
case CASE_LS3:
/* select G3 into GL */
curGL = 3;
parsestate = groundtable;
break;
case CASE_LS3R:
/* select G3 into GR */
curGR = 3;
parsestate = groundtable;
break;
case CASE_LS2R:
/* select G2 into GR */
curGR = 2;
parsestate = groundtable;
break;
case CASE_LS1R:
/* select G1 into GR */
curGR = 1;
parsestate = groundtable;
break;
case CASE_VPA: // ESC [...d move cursor absolute vertical
/* VPA (CV) */
if ((row = param[0]) < 1)
row = 1;
// note beterm wants it 1-based unlike usual terminals
fBuffer->SetCursorY(row - 1);
parsestate = groundtable;
break;
case CASE_HPA: // ESC [...G move cursor absolute horizontal
/* HPA (CH) */
if ((column = param[0]) < 1)
column = 1;
// note beterm wants it 1-based unlike usual terminals
fBuffer->SetCursorX(column - 1);
parsestate = groundtable;
break;
case CASE_SU: // scroll screen up
if ((row = param[0]) < 1)
row = 1;
fBuffer->ScrollBy(row);
parsestate = groundtable;
break;
case CASE_SD: // scroll screen down
if ((row = param[0]) < 1)
row = 1;
fBuffer->ScrollBy(-row);
parsestate = groundtable;
break;
case CASE_ECH: // erase characters
if ((column = param[0]) < 1)
column = 1;
fBuffer->EraseChars(column);
parsestate = groundtable;
break;
default:
break;
}
} catch (...) {
break;
}
}
return B_OK;
}
/*static*/ int32
TermParse::_ptyreader_thread(void *data)
{
return reinterpret_cast<TermParse *>(data)->PtyReader();
}
/*static*/ int32
TermParse::_escparse_thread(void *data)
{
return reinterpret_cast<TermParse *>(data)->EscParse();
}
status_t
TermParse::_ReadParserBuffer()
{
// We have to unlock the terminal buffer while waiting for data from the
// PTY. We don't have to unlock when we don't need to wait, but we do it
// anyway, so that TermView won't be starved when trying to synchronize.
fBuffer->Unlock();
// wait for new input from pty
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);
// eat any sems that were released unconditionally
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++) {
// TODO: This could be optimized using memcpy instead and
// calculating space left as in the PtyReader().
fParserBuffer[i] = fReadBuffer[fBufferPosition];
fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
}
int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
// If the pty reader thread waits and we have made enough space in the
// buffer now, let it run again.
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:
{
// Device status report requested
// reply with "no malfunction detected"
const char* toWrite = "\033[0n";
write(fFd, toWrite, strlen(toWrite));
break ;
}
case 6:
// Cursor position report requested
len = sprintf(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)
{
// Terminal parameters report:
// type (2 or 3);
// no parity (1);
// 8 bits per character (1);
// transmit speed 38400bps (128);
// receive speed 38400bps (128);
// bit rate multiplier 16 (1);
// no flags (0)
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:
// Application Cursor Keys (whatever that means).
// Not supported yet.
break;
case 5:
// Reverse Video (inverses colors for the complete screen
// -- when followed by normal video, that's shortly flashes the
// screen).
// Not supported yet.
break;
case 6:
// Set Origin Mode.
fBuffer->SetOriginMode(true);
break;
case 9:
// Set Mouse X and Y on button press.
fBuffer->ReportX10MouseEvent(true);
break;
case 12:
// Start Blinking Cursor.
fBuffer->SetCursorBlinking(true);
break;
case 25:
// Show Cursor.
fBuffer->SetCursorHidden(false);
break;
case 47:
// Use Alternate Screen Buffer.
fBuffer->UseAlternateScreenBuffer(false);
break;
case 1000:
// Send Mouse X & Y on button press and release.
fBuffer->ReportNormalMouseEvent(true);
break;
case 1002:
// Send Mouse X and Y on button press and release, and on motion
// when the mouse enter a new cell
fBuffer->ReportButtonMouseEvent(true);
break;
case 1003:
// Use All Motion Mouse Tracking
fBuffer->ReportAnyMouseEvent(true);
break;
case 1034:
// TODO: Interprete "meta" key, sets eighth bit.
// Not supported yet.
break;
case 1036:
// TODO: Send ESC when Meta modifies a key
// Not supported yet.
break;
case 1039:
// TODO: Send ESC when Alt modifies a key
// Not supported yet.
break;
case 1049:
// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
// it first.
fBuffer->SaveCursor();
fBuffer->UseAlternateScreenBuffer(true);
break;
}
}
void
TermParse::_DecPrivateModeReset(int value)
{
switch (value) {
case 1:
// Normal Cursor Keys (whatever that means).
// Not supported yet.
break;
case 3:
// 80 Column Mode.
// Not supported yet.
break;
case 4:
// Jump (Fast) Scroll.
// Not supported yet.
break;
case 5:
// Normal Video (Leaves Reverse Video, cf. there).
// Not supported yet.
break;
case 6:
// Reset Origin Mode.
fBuffer->SetOriginMode(false);
break;
case 9:
// Disable Mouse X and Y on button press.
fBuffer->ReportX10MouseEvent(false);
break;
case 12:
// Stop Blinking Cursor.
fBuffer->SetCursorBlinking(false);
break;
case 25:
// Hide Cursor
fBuffer->SetCursorHidden(true);
break;
case 47:
// Use Normal Screen Buffer.
fBuffer->UseNormalScreenBuffer();
break;
case 1000:
// Don't send Mouse X & Y on button press and release.
fBuffer->ReportNormalMouseEvent(false);
break;
case 1002:
// Don't send Mouse X and Y on button press and release, and on motion
// when the mouse enter a new cell
fBuffer->ReportButtonMouseEvent(false);
break;
case 1003:
// Disable All Motion Mouse Tracking.
fBuffer->ReportAnyMouseEvent(false);
break;
case 1034:
// Don't interprete "meta" key.
// Not supported yet.
break;
case 1036:
// TODO: Don't send ESC when Meta modifies a key
// Not supported yet.
break;
case 1039:
// TODO: Don't send ESC when Alt modifies a key
// Not supported yet.
break;
case 1049:
// Use Normal Screen Buffer and restore cursor as in DECRC.
fBuffer->UseNormalScreenBuffer();
fBuffer->RestoreCursor();
break;
}
}
void
TermParse::_ProcessOperatingSystemControls(uchar* params)
{
int mode = 0;
for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) {
mode *= 10;
mode += c - '0';
}
// eat the separator
if (*params == ';')
params++;
static uint8 indexes[kTermColorCount];
static rgb_color colors[kTermColorCount];
switch (mode) {
case 0: // icon name and window title
case 2: // window title
fBuffer->SetTitle((const char*)params);
break;
case 4: // set colors (0 - 255)
case 104: // reset colors (0 - 255)
{
bool reset = (mode / 100) == 1;
// colors can be in "idx1:name1;...;idxN:nameN;" sequence too!
uint32 count = 0;
char* p = strtok((char*)params, ";");
do {
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, ";");
} while (p != NULL && count < kTermColorCount);
if (count > 0) {
if (!reset)
fBuffer->SetColors(indexes, colors, count);
else
fBuffer->ResetColors(indexes, count);
}
}
break;
// set dynamic colors (10 - 19)
case 10: // text foreground
case 11: // text background
{
int32 offset = mode - 10;
int32 count = 0;
char* p = strtok((char*)params, ";");
do {
if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) {
// dyna-colors are pos-sensitive - no chance to continue
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;
// reset dynamic colors (10 - 19)
case 110: // text foreground
case 111: // text background
{
indexes[0] = mode;
fBuffer->ResetColors(indexes, 1, true);
}
break;
default:
// printf("%d -> %s\n", mode, params);
break;
}
}