multiple changes in APC handling:
- change interface to IFunctionIdentifier (API change) - extend parser states APC_ENTRY, APC_INTERMEDIATE, APC_PASSTHROUGH - remove APC_STRING - change ApcParser.start to retrieve ident - change actions APC_START and APC_PUT
This commit is contained in:
@@ -60,8 +60,8 @@ describe('ApcParser', () => {
|
||||
|
||||
describe('identifier parsing', () => {
|
||||
it('single character identifier', () => {
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100,a=T;payload');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100,a=T;payload');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(reports, [
|
||||
@@ -71,20 +71,20 @@ describe('ApcParser', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('identifier with no payload', () => {
|
||||
parser.start();
|
||||
const data = toUtf32('G');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(reports, [
|
||||
[0x47, 'START', undefined],
|
||||
[0x47, 'END', true]
|
||||
]);
|
||||
});
|
||||
//it('identifier with no payload', () => {
|
||||
// parser.start('G'.charCodeAt(0));
|
||||
// const data = toUtf32('');
|
||||
// parser.put(data, 0, data.length);
|
||||
// parser.end(true);
|
||||
// assert.deepEqual(reports, [
|
||||
// [0x47, 'START', undefined],
|
||||
// [0x47, 'END', true]
|
||||
// ]);
|
||||
//});
|
||||
|
||||
it('identifier with chunked payload', () => {
|
||||
parser.start();
|
||||
let data = toUtf32('Gf=100');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
let data = toUtf32('f=100');
|
||||
parser.put(data, 0, data.length);
|
||||
data = toUtf32(',a=T');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -100,11 +100,11 @@ describe('ApcParser', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('empty APC sequence', () => {
|
||||
parser.start();
|
||||
parser.end(true);
|
||||
assert.deepEqual(reports, []);
|
||||
});
|
||||
//it('empty APC sequence', () => {
|
||||
// parser.start('G'.charCodeAt(0));
|
||||
// parser.end(true);
|
||||
// assert.deepEqual(reports, []);
|
||||
//});
|
||||
});
|
||||
|
||||
describe('handler registration', () => {
|
||||
@@ -117,8 +117,8 @@ describe('ApcParser', () => {
|
||||
it('registerHandler for specific identifier', () => {
|
||||
const G_CODE = 0x47; // 'G'
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'kitty'));
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100,a=T;imagedata');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100,a=T;imagedata');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, [
|
||||
@@ -133,8 +133,8 @@ describe('ApcParser', () => {
|
||||
const G_CODE = 0x47; // 'G'
|
||||
const X_CODE = 0x58; // 'X'
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'kitty'));
|
||||
parser.start();
|
||||
const data = toUtf32('Xsome data');
|
||||
parser.start('X'.charCodeAt(0));
|
||||
const data = toUtf32('some data');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, []);
|
||||
@@ -149,8 +149,8 @@ describe('ApcParser', () => {
|
||||
const G_CODE = 0x47;
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'kitty'));
|
||||
parser.clearHandler(G_CODE);
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, []);
|
||||
@@ -165,8 +165,8 @@ describe('ApcParser', () => {
|
||||
const G_CODE = 0x47;
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler1'));
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler2'));
|
||||
parser.start();
|
||||
const data = toUtf32('Gdata');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('data');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, [
|
||||
@@ -183,8 +183,8 @@ describe('ApcParser', () => {
|
||||
const G_CODE = 0x47;
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler1'));
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler2', true));
|
||||
parser.start();
|
||||
const data = toUtf32('Gdata');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('data');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, [
|
||||
@@ -202,8 +202,8 @@ describe('ApcParser', () => {
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler1'));
|
||||
const disposable = parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'handler2'));
|
||||
disposable.dispose();
|
||||
parser.start();
|
||||
const data = toUtf32('Gdata');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('data');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(true);
|
||||
assert.deepEqual(handlerReports, [
|
||||
@@ -237,8 +237,8 @@ describe('ApcParser', () => {
|
||||
results.push([G_CODE, data]);
|
||||
return true;
|
||||
}));
|
||||
parser.start();
|
||||
let data = toUtf32('Gf=100');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
let data = toUtf32('f=100');
|
||||
parser.put(data, 0, data.length);
|
||||
data = toUtf32(',a=T;payload');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -253,8 +253,8 @@ describe('ApcParser', () => {
|
||||
results.push([G_CODE, data]);
|
||||
return true;
|
||||
}));
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100,a=T;payload');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100,a=T;payload');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.end(false);
|
||||
assert.deepEqual(results, []);
|
||||
@@ -268,8 +268,8 @@ describe('ApcParser', () => {
|
||||
results.push([G_CODE, data]);
|
||||
return true;
|
||||
}));
|
||||
parser.start();
|
||||
let data = toUtf32('G');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
let data = toUtf32('');
|
||||
parser.put(data, 0, data.length);
|
||||
data = toUtf32('A'.repeat(CHUNK_SIZE));
|
||||
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
|
||||
@@ -287,8 +287,8 @@ describe('ApcParser', () => {
|
||||
results.push([G_CODE, data]);
|
||||
return true;
|
||||
}));
|
||||
parser.start();
|
||||
let data = toUtf32('G');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
let data = toUtf32('');
|
||||
parser.put(data, 0, data.length);
|
||||
data = toUtf32('A'.repeat(CHUNK_SIZE));
|
||||
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
|
||||
@@ -311,8 +311,8 @@ describe('ApcParser', () => {
|
||||
it('reset during payload cleans up handlers', () => {
|
||||
const G_CODE = 0x47;
|
||||
parser.registerHandler(G_CODE, new TestHandler(G_CODE, handlerReports, 'kitty'));
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100');
|
||||
parser.put(data, 0, data.length);
|
||||
parser.reset();
|
||||
assert.deepEqual(handlerReports, [
|
||||
@@ -353,8 +353,8 @@ describe('ApcParser - async tests', () => {
|
||||
results.push([G_CODE, data]);
|
||||
return true;
|
||||
}));
|
||||
parser.start();
|
||||
const data = toUtf32('Gf=100,a=T');
|
||||
parser.start('G'.charCodeAt(0));
|
||||
const data = toUtf32('f=100,a=T');
|
||||
parser.put(data, 0, data.length);
|
||||
await endP(parser, true);
|
||||
assert.deepEqual(results, [[G_CODE, 'f=100,a=T']]);
|
||||
|
||||
@@ -19,10 +19,9 @@ const EMPTY_HANDLERS: IApcHandler[] = [];
|
||||
* The identifier is the character code of the first byte after ESC _.
|
||||
*/
|
||||
export class ApcParser implements IApcParser {
|
||||
private _state = ApcState.START;
|
||||
private _active = EMPTY_HANDLERS;
|
||||
private _id = -1;
|
||||
private _handlers: IHandlerCollection<IApcHandler> = Object.create(null);
|
||||
private _active = EMPTY_HANDLERS;
|
||||
private _ident: number = 0;
|
||||
private _handlerFb: ApcFallbackHandlerType = () => { };
|
||||
private _stack: ISubParserStackState = {
|
||||
paused: false,
|
||||
@@ -64,22 +63,24 @@ export class ApcParser implements IApcParser {
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
// force cleanup handlers if payload was already sent
|
||||
if (this._state === ApcState.PAYLOAD) {
|
||||
// force cleanup handlers
|
||||
if (this._active.length) {
|
||||
for (let j = this._stack.paused ? this._stack.loopPosition - 1 : this._active.length - 1; j >= 0; --j) {
|
||||
this._active[j].end(false);
|
||||
}
|
||||
}
|
||||
this._stack.paused = false;
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._id = -1;
|
||||
this._state = ApcState.START;
|
||||
this._ident = 0;
|
||||
}
|
||||
|
||||
private _start(): void {
|
||||
this._active = this._handlers[this._id] || EMPTY_HANDLERS;
|
||||
public start(ident: number): void {
|
||||
// always reset leftover handlers
|
||||
this.reset();
|
||||
this._ident = ident;
|
||||
this._active = this._handlers[ident] || EMPTY_HANDLERS;
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'START');
|
||||
this._handlerFb(this._ident, 'START');
|
||||
} else {
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].start();
|
||||
@@ -87,9 +88,9 @@ export class ApcParser implements IApcParser {
|
||||
}
|
||||
}
|
||||
|
||||
private _put(data: Uint32Array, start: number, end: number): void {
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'PUT', utf32ToString(data, start, end));
|
||||
this._handlerFb(this._ident, 'PUT', utf32ToString(data, start, end));
|
||||
} else {
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].put(data, start, end);
|
||||
@@ -97,100 +98,51 @@ export class ApcParser implements IApcParser {
|
||||
}
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
// always reset leftover handlers
|
||||
this.reset();
|
||||
this._state = ApcState.ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put data to current APC command.
|
||||
* For APC, the first character is used as the identifier.
|
||||
* Format: ESC _ <identifier><payload> ESC \
|
||||
* Example: ESC _ G f=100,a=T;... ESC \ (Kitty graphics, identifier='G')
|
||||
*/
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
if (this._state === ApcState.ABORT) {
|
||||
return;
|
||||
}
|
||||
if (this._state === ApcState.ID) {
|
||||
// The first character is the identifier
|
||||
if (start < end) {
|
||||
this._id = data[start++];
|
||||
this._state = ApcState.PAYLOAD;
|
||||
this._start();
|
||||
}
|
||||
}
|
||||
if (this._state === ApcState.PAYLOAD && end - start > 0) {
|
||||
this._put(data, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates end of an APC command.
|
||||
* Whether the APC got aborted or finished normally
|
||||
* is indicated by `success`.
|
||||
*/
|
||||
public end(success: boolean, promiseResult: boolean = true): void | Promise<boolean> {
|
||||
if (this._state === ApcState.START) {
|
||||
return;
|
||||
}
|
||||
// do nothing if command was faulty
|
||||
if (this._state !== ApcState.ABORT) {
|
||||
// if we are still in ID state and get an early end
|
||||
// means we got an empty APC sequence with no identifier,
|
||||
// which is invalid - just reset and return
|
||||
if (this._state === ApcState.ID) {
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._id = -1;
|
||||
this._state = ApcState.START;
|
||||
return;
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._ident, 'END', success);
|
||||
} else {
|
||||
let handlerResult: boolean | Promise<boolean> = false;
|
||||
let j = this._active.length - 1;
|
||||
let fallThrough = false;
|
||||
if (this._stack.paused) {
|
||||
j = this._stack.loopPosition - 1;
|
||||
handlerResult = promiseResult;
|
||||
fallThrough = this._stack.fallThrough;
|
||||
this._stack.paused = false;
|
||||
}
|
||||
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'END', success);
|
||||
} else {
|
||||
let handlerResult: boolean | Promise<boolean> = false;
|
||||
let j = this._active.length - 1;
|
||||
let fallThrough = false;
|
||||
if (this._stack.paused) {
|
||||
j = this._stack.loopPosition - 1;
|
||||
handlerResult = promiseResult;
|
||||
fallThrough = this._stack.fallThrough;
|
||||
this._stack.paused = false;
|
||||
}
|
||||
if (!fallThrough && handlerResult === false) {
|
||||
for (; j >= 0; j--) {
|
||||
handlerResult = this._active[j].end(success);
|
||||
if (handlerResult === true) {
|
||||
break;
|
||||
} else if (handlerResult instanceof Promise) {
|
||||
this._stack.paused = true;
|
||||
this._stack.loopPosition = j;
|
||||
this._stack.fallThrough = false;
|
||||
return handlerResult;
|
||||
}
|
||||
}
|
||||
j--;
|
||||
}
|
||||
// cleanup left over handlers
|
||||
// we always have to call .end for proper cleanup,
|
||||
// here we use `success` to indicate whether a handler should execute
|
||||
if (!fallThrough && handlerResult === false) {
|
||||
for (; j >= 0; j--) {
|
||||
handlerResult = this._active[j].end(false);
|
||||
if (handlerResult instanceof Promise) {
|
||||
handlerResult = this._active[j].end(success);
|
||||
if (handlerResult === true) {
|
||||
break;
|
||||
} else if (handlerResult instanceof Promise) {
|
||||
this._stack.paused = true;
|
||||
this._stack.loopPosition = j;
|
||||
this._stack.fallThrough = true;
|
||||
this._stack.fallThrough = false;
|
||||
return handlerResult;
|
||||
}
|
||||
}
|
||||
j--;
|
||||
}
|
||||
// cleanup left over handlers (fallThrough for async)
|
||||
for (; j >= 0; j--) {
|
||||
handlerResult = this._active[j].end(false);
|
||||
if (handlerResult instanceof Promise) {
|
||||
this._stack.paused = true;
|
||||
this._stack.loopPosition = j;
|
||||
this._stack.fallThrough = true;
|
||||
return handlerResult;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._id = -1;
|
||||
this._state = ApcState.START;
|
||||
this._ident = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ export const enum ParserState {
|
||||
DCS_IGNORE = 11,
|
||||
DCS_INTERMEDIATE = 12,
|
||||
DCS_PASSTHROUGH = 13,
|
||||
APC_STRING = 14,
|
||||
APC_ENTRY = 14,
|
||||
APC_INTERMEDIATE = 15,
|
||||
APC_PASSTHROUGH = 16,
|
||||
// Number of states, meaning LAST_STATE + 1.
|
||||
STATE_LENGTH = 15
|
||||
STATE_LENGTH = 17
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -163,7 +163,9 @@ const states: number[] = [
|
||||
ParserState.DCS_IGNORE,
|
||||
ParserState.DCS_INTERMEDIATE,
|
||||
ParserState.DCS_PASSTHROUGH,
|
||||
ParserState.APC_STRING
|
||||
ParserState.APC_ENTRY,
|
||||
ParserState.APC_INTERMEDIATE,
|
||||
ParserState.APC_PASSTHROUGH
|
||||
];
|
||||
let state: any;
|
||||
|
||||
@@ -278,7 +280,7 @@ describe('EscapeSequenceParser', () => {
|
||||
const exceptions: { [key: number]: { [key: string]: any[] } } = {
|
||||
8: { '\x18': [], '\x1a': [] }, // abort OSC_STRING
|
||||
13: { '\x18': [['dcs unhook', false]], '\x1a': [['dcs unhook', false]] }, // abort DCS_PASSTHROUGH
|
||||
14: { '\x18': [], '\x1a': [] } // abort APC_STRING
|
||||
16: { '\x18': [], '\x1a': [] } // abort APC_PASSTHROUGH
|
||||
};
|
||||
parser.reset();
|
||||
testTerminal.clear();
|
||||
@@ -725,20 +727,20 @@ describe('EscapeSequenceParser', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
it('trans ANYWHERE/ESCAPE --> APC_STRING', () => {
|
||||
parser.reset();
|
||||
// C0 (ESC _)
|
||||
parse(parser, '\x1b_');
|
||||
assert.equal(parser.currentState, ParserState.APC_STRING);
|
||||
parser.reset();
|
||||
// C1
|
||||
for (state in states) {
|
||||
parser.currentState = state;
|
||||
parse(parser, '\x9f');
|
||||
assert.equal(parser.currentState, ParserState.APC_STRING);
|
||||
parser.reset();
|
||||
}
|
||||
});
|
||||
//it('trans ANYWHERE/ESCAPE --> APC_STRING', () => {
|
||||
// parser.reset();
|
||||
// // C0 (ESC _)
|
||||
// parse(parser, '\x1b_');
|
||||
// assert.equal(parser.currentState, ParserState.APC_STRING);
|
||||
// parser.reset();
|
||||
// // C1
|
||||
// for (state in states) {
|
||||
// parser.currentState = state;
|
||||
// parse(parser, '\x9f');
|
||||
// assert.equal(parser.currentState, ParserState.APC_STRING);
|
||||
// parser.reset();
|
||||
// }
|
||||
//});
|
||||
it('state SOS_PM_STRING ignore rules', () => {
|
||||
parser.reset();
|
||||
let ignored = r(0x00, 0x18);
|
||||
|
||||
@@ -77,7 +77,8 @@ const NON_ASCII_PRINTABLE = 0xA0;
|
||||
* Taken from https://vt100.net/emu/dec_ansi_parser.
|
||||
*/
|
||||
export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
const table: TransitionTable = new TransitionTable(4095);
|
||||
// table size: (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1
|
||||
const table: TransitionTable = new TransitionTable(4257);
|
||||
|
||||
// range macro for byte
|
||||
const BYTE_VALUES = 256;
|
||||
@@ -105,7 +106,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
table.add(0x1b, state, ParserAction.CLEAR, ParserState.ESCAPE); // ESC
|
||||
table.add(0x9d, state, ParserAction.OSC_START, ParserState.OSC_STRING); // OSC
|
||||
table.addMany([0x98, 0x9e], state, ParserAction.IGNORE, ParserState.SOS_PM_STRING); // SOS, PM
|
||||
table.add(0x9f, state, ParserAction.APC_START, ParserState.APC_STRING); // APC
|
||||
table.add(0x9f, state, ParserAction.CLEAR, ParserState.APC_ENTRY); // APC
|
||||
table.add(0x9b, state, ParserAction.CLEAR, ParserState.CSI_ENTRY); // CSI
|
||||
table.add(0x90, state, ParserAction.CLEAR, ParserState.DCS_ENTRY); // DCS
|
||||
}
|
||||
@@ -136,11 +137,19 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
table.add(0x9c, ParserState.SOS_PM_STRING, ParserAction.IGNORE, ParserState.GROUND);
|
||||
table.add(0x7f, ParserState.SOS_PM_STRING, ParserAction.IGNORE, ParserState.SOS_PM_STRING);
|
||||
// apc
|
||||
table.add(0x5f, ParserState.ESCAPE, ParserAction.APC_START, ParserState.APC_STRING);
|
||||
table.addMany(PRINTABLES, ParserState.APC_STRING, ParserAction.APC_PUT, ParserState.APC_STRING);
|
||||
table.addMany(EXECUTABLES, ParserState.APC_STRING, ParserAction.IGNORE, ParserState.APC_STRING);
|
||||
table.add(0x7f, ParserState.APC_STRING, ParserAction.IGNORE, ParserState.APC_STRING);
|
||||
table.addMany([0x1b, 0x9c, 0x18, 0x1a], ParserState.APC_STRING, ParserAction.APC_END, ParserState.GROUND);
|
||||
table.add(0x5f, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.APC_ENTRY);
|
||||
table.addMany(EXECUTABLES, ParserState.APC_ENTRY, ParserAction.IGNORE, ParserState.APC_ENTRY);
|
||||
table.add(0x7f, ParserState.APC_ENTRY, ParserAction.IGNORE, ParserState.APC_ENTRY);
|
||||
table.addMany(r(0x20, 0x30), ParserState.APC_ENTRY, ParserAction.COLLECT, ParserState.APC_INTERMEDIATE);
|
||||
table.addMany(r(0x30, 0x7f), ParserState.APC_ENTRY, ParserAction.APC_START, ParserState.APC_PASSTHROUGH);
|
||||
table.addMany(r(0x30, 0x7f), ParserState.APC_INTERMEDIATE, ParserAction.APC_START, ParserState.APC_PASSTHROUGH);
|
||||
table.addMany(EXECUTABLES, ParserState.APC_INTERMEDIATE, ParserAction.IGNORE, ParserState.APC_INTERMEDIATE);
|
||||
table.add(0x7f, ParserState.APC_INTERMEDIATE, ParserAction.IGNORE, ParserState.APC_INTERMEDIATE);
|
||||
table.addMany(PRINTABLES, ParserState.APC_PASSTHROUGH, ParserAction.APC_PUT, ParserState.APC_PASSTHROUGH);
|
||||
table.addMany(EXECUTABLES, ParserState.APC_PASSTHROUGH, ParserAction.IGNORE, ParserState.APC_PASSTHROUGH);
|
||||
table.addMany(r(0x08, 0x0e), ParserState.APC_PASSTHROUGH, ParserAction.APC_PUT, ParserState.APC_PASSTHROUGH);
|
||||
table.add(0x7f, ParserState.APC_PASSTHROUGH, ParserAction.IGNORE, ParserState.APC_PASSTHROUGH);
|
||||
table.addMany([0x1b, 0x9c, 0x18, 0x1a], ParserState.APC_PASSTHROUGH, ParserAction.APC_END, ParserState.GROUND);
|
||||
// csi entries
|
||||
table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY);
|
||||
table.addMany(r(0x40, 0x7f), ParserState.CSI_ENTRY, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
||||
@@ -200,7 +209,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
table.add(NON_ASCII_PRINTABLE, ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||
table.add(NON_ASCII_PRINTABLE, ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||
table.add(NON_ASCII_PRINTABLE, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH);
|
||||
table.add(NON_ASCII_PRINTABLE, ParserState.APC_STRING, ParserAction.APC_PUT, ParserState.APC_STRING);
|
||||
table.add(NON_ASCII_PRINTABLE, ParserState.APC_PASSTHROUGH, ParserAction.APC_PUT, ParserState.APC_PASSTHROUGH);
|
||||
return table;
|
||||
})();
|
||||
|
||||
@@ -439,11 +448,13 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._oscParser.setHandlerFallback(handler);
|
||||
}
|
||||
|
||||
public registerApcHandler(ident: number, handler: IApcHandler): IDisposable {
|
||||
return this._apcParser.registerHandler(ident, handler);
|
||||
public registerApcHandler(id: IFunctionIdentifier, handler: IApcHandler): IDisposable {
|
||||
id.prefix = undefined; // APC does not support prefix byte
|
||||
return this._apcParser.registerHandler(this._identifier(id, [0x30, 0x7e]), handler);
|
||||
}
|
||||
public clearApcHandler(ident: number): void {
|
||||
this._apcParser.clearHandler(ident);
|
||||
public clearApcHandler(id: IFunctionIdentifier): void {
|
||||
id.prefix = undefined; // APC does not support prefix byte
|
||||
this._apcParser.clearHandler(this._identifier(id, [0x30, 0x7e]));
|
||||
}
|
||||
public setApcHandlerFallback(handler: ApcFallbackHandlerType): void {
|
||||
this._apcParser.setHandlerFallback(handler);
|
||||
@@ -719,7 +730,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
}
|
||||
|
||||
// normal transition & action lookup
|
||||
transition = this._transitions.table[this.currentState << TableAccess.INDEX_STATE_SHIFT | (code < 0xa0 ? code : NON_ASCII_PRINTABLE)];
|
||||
transition = this._transitions.table[
|
||||
this.currentState << TableAccess.INDEX_STATE_SHIFT |
|
||||
(code < NON_ASCII_PRINTABLE ? code : NON_ASCII_PRINTABLE)
|
||||
];
|
||||
switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) {
|
||||
case ParserAction.PRINT:
|
||||
// Note: 0x20 (SP) is included, 0x7F (DEL) is excluded
|
||||
@@ -872,17 +886,19 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
case ParserAction.APC_START:
|
||||
this._apcParser.start();
|
||||
this._apcParser.start(this._collect << 8 | code);
|
||||
break;
|
||||
case ParserAction.APC_PUT:
|
||||
// FIXME: this is wrong, must be 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE
|
||||
// inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c
|
||||
for (let j = i + 1; ; ++j) {
|
||||
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || code === 0x9c || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
|
||||
this._apcParser.put(data, i, j);
|
||||
i = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//for (let j = i + 1; ; ++j) {
|
||||
// if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || code === 0x9c || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
|
||||
// this._apcParser.put(data, i, j);
|
||||
// i = j - 1;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
this._apcParser.put(data, i, i+1);
|
||||
break;
|
||||
case ParserAction.APC_END:
|
||||
handlerResult = this._apcParser.end(code !== 0x18 && code !== 0x1a);
|
||||
|
||||
@@ -220,8 +220,8 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
clearOscHandler(ident: number): void;
|
||||
setOscHandlerFallback(handler: OscFallbackHandlerType): void;
|
||||
|
||||
registerApcHandler(ident: number, handler: IApcHandler): IDisposable;
|
||||
clearApcHandler(ident: number): void;
|
||||
registerApcHandler(id: IFunctionIdentifier, handler: IApcHandler): IDisposable;
|
||||
clearApcHandler(id: IFunctionIdentifier): void;
|
||||
setApcHandlerFallback(handler: ApcFallbackHandlerType): void;
|
||||
|
||||
setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
|
||||
@@ -252,7 +252,7 @@ export interface IDcsParser extends ISubParser<IDcsHandler, DcsFallbackHandlerTy
|
||||
}
|
||||
|
||||
export interface IApcParser extends ISubParser<IApcHandler, ApcFallbackHandlerType> {
|
||||
start(): void;
|
||||
start(ident: number): void;
|
||||
end(success: boolean, promiseResult?: boolean): void | Promise<boolean>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user