Random parser changes (#546)

This commit is contained in:
Jake Wharton
2024-11-25 15:38:44 -05:00
committed by GitHub
parent 4d0f5a3456
commit c912b29ef2
2 changed files with 55 additions and 44 deletions

View File

@ -186,26 +186,22 @@ public class TerminalParser(
private fun parseApc(buffer: ByteArray, start: Int, limit: Int): Event? { private fun parseApc(buffer: ByteArray, start: Int, limit: Int): Event? {
// TODO https://stackoverflow.com/a/71632523/132047 // TODO https://stackoverflow.com/a/71632523/132047
return parseUntilStringTerminator(buffer, start, limit) { stIndex, end -> return parseUntilStringTerminator(buffer, start, limit) { b3Index, stIndex, end ->
val b3Index = start + 2
if (stIndex > b3Index && buffer[b3Index].toInt() == 'G'.code) { if (stIndex > b3Index && buffer[b3Index].toInt() == 'G'.code) {
val delimiter = buffer.indexOf(';'.code.toByte(), b3Index, stIndex) val delimiter = buffer.indexOf(';'.code.toByte(), b3Index, stIndex)
val b5Index = start + 4 val b5Index = start + 4
if (delimiter == -1 || if (delimiter != -1 &&
delimiter <= b5Index || delimiter > b5Index &&
buffer[start + 3].toInt() != 'i'.code || buffer[start + 3].toInt() == 'i'.code &&
buffer[b5Index].toInt() != '='.code buffer[b5Index].toInt() == '='.code
) { ) {
UnknownEvent(buffer.copyOfRange(start, end)) return@parseUntilStringTerminator KittyGraphicsEvent(
} else {
KittyGraphicsEvent(
id = buffer.parseIntDigits(b5Index, delimiter), id = buffer.parseIntDigits(b5Index, delimiter),
message = buffer.decodeToString(delimiter + 1, stIndex), message = buffer.decodeToString(delimiter + 1, stIndex),
) )
} }
} else {
UnknownEvent(buffer.copyOfRange(start, end))
} }
null
} }
} }
@ -275,6 +271,13 @@ public class TerminalParser(
'm'.code, 'm'.code,
'M'.code, 'M'.code,
-> { -> {
if (b3Index == finalIndex) {
// TODO If we are in UTF-8 mode it is at minimum 3 but could be up to 6.
val trailingEnd = end + 3
if (trailingEnd > limit) return null
offset = trailingEnd
break@error
}
if (buffer[b3Index].toInt() != '<'.code) { if (buffer[b3Index].toInt() != '<'.code) {
break@error break@error
} }
@ -411,7 +414,9 @@ public class TerminalParser(
} }
} while (false) } while (false)
return UnknownEvent(buffer.copyOfRange(start, end)) // Use 'offset' not 'end' because some sequences put data after the "final" index. This allows
// parsing of that trailing data to error and still be included in the unknown event.
return UnknownEvent(buffer.copyOfRange(start, offset))
} }
private fun parseCsiLegacy(buffer: ByteArray, start: Int, limit: Int, codepoint: Int): CodepointEvent { private fun parseCsiLegacy(buffer: ByteArray, start: Int, limit: Int, codepoint: Int): CodepointEvent {
@ -421,15 +426,15 @@ public class TerminalParser(
} }
private fun parseDcs(buffer: ByteArray, start: Int, limit: Int): Event? { private fun parseDcs(buffer: ByteArray, start: Int, limit: Int): Event? {
return parseUntilStringTerminator(buffer, start, limit) { stIndex, end -> return parseUntilStringTerminator(buffer, start, limit) { b3Index, stIndex, end ->
val b4Index = start + 3 val b4Index = start + 3
if (stIndex > b4Index && if (stIndex > b4Index &&
buffer[start + 2].toInt() == '>'.code && buffer[b3Index].toInt() == '>'.code &&
buffer[b4Index].toInt() == '|'.code buffer[b4Index].toInt() == '|'.code
) { ) {
TerminalVersionEvent(buffer.decodeToString(start + 4, stIndex)) TerminalVersionEvent(buffer.decodeToString(start + 4, stIndex))
} else { } else {
UnknownEvent(buffer.copyOfRange(start, end)) null
} }
} }
} }
@ -439,14 +444,14 @@ public class TerminalParser(
} }
private fun parsePm(buffer: ByteArray, start: Int, limit: Int): Event? { private fun parsePm(buffer: ByteArray, start: Int, limit: Int): Event? {
return parseUntilStringTerminator(buffer, start, limit) { _, end -> return parseUntilStringTerminator(buffer, start, limit) { _, _, _ ->
UnknownEvent(buffer.copyOfRange(start, end)) null
} }
} }
private fun parseSos(buffer: ByteArray, start: Int, limit: Int): Event? { private fun parseSos(buffer: ByteArray, start: Int, limit: Int): Event? {
return parseUntilStringTerminator(buffer, start, limit) { _, end -> return parseUntilStringTerminator(buffer, start, limit) { _, _, _ ->
UnknownEvent(buffer.copyOfRange(start, end)) null
} }
} }
@ -457,40 +462,45 @@ public class TerminalParser(
offset = end offset = end
val b3Index = start + 2 val b3Index = start + 2
val codepoint = when (buffer[b3Index].toInt()) { error@ do {
'A'.code -> CodepointEvent.Up val codepoint = when (buffer[b3Index].toInt()) {
'B'.code -> CodepointEvent.Down 'A'.code -> CodepointEvent.Up
'C'.code -> CodepointEvent.Right 'B'.code -> CodepointEvent.Down
'D'.code -> CodepointEvent.Left 'C'.code -> CodepointEvent.Right
'F'.code -> CodepointEvent.End 'D'.code -> CodepointEvent.Left
'H'.code -> CodepointEvent.Home 'F'.code -> CodepointEvent.End
'P'.code -> CodepointEvent.F1 'H'.code -> CodepointEvent.Home
'Q'.code -> CodepointEvent.F2 'P'.code -> CodepointEvent.F1
'R'.code -> CodepointEvent.F3 'Q'.code -> CodepointEvent.F2
'S'.code -> CodepointEvent.F3 'R'.code -> CodepointEvent.F3
0x1b -> { 'S'.code -> CodepointEvent.F3
// libvaxis added a guard against this case 0x1b -> {
// https://github.com/rockorager/libvaxis/commit/b68864c3babf2767c15c52911179e8ee9158e1d2 // libvaxis added a guard against this case
offset = b3Index // https://github.com/rockorager/libvaxis/commit/b68864c3babf2767c15c52911179e8ee9158e1d2
return UnknownEvent(buffer.copyOfRange(start, b3Index)) offset = b3Index
break@error
}
else -> break@error
} }
else -> { return CodepointEvent(codepoint)
return UnknownEvent(buffer.copyOfRange(start, end)) } while (false)
}
} // Use 'offset' not 'end' because if end is an escape we back up the offset.
return CodepointEvent(codepoint) return UnknownEvent(buffer.copyOfRange(start, offset))
} }
private inline fun parseUntilStringTerminator( private inline fun parseUntilStringTerminator(
buffer: ByteArray, buffer: ByteArray,
start: Int, start: Int,
limit: Int, limit: Int,
crossinline handler: (stIndex: Int, end: Int) -> Event, crossinline handler: (b3Index: Int, stIndex: Int, end: Int) -> Event?,
): Event? { ): Event? {
// TODO test string with 0x1b inside of it // TODO test string with 0x1b inside of it
// Skip leading discriminator. // Skip leading discriminator.
var searchFrom = start + 2 val b3Index = start + 2
var searchFrom = b3Index
while (true) { while (true) {
val escIndex = buffer.indexOfFirstOrElse( val escIndex = buffer.indexOfFirstOrElse(
@ -506,7 +516,8 @@ public class TerminalParser(
if (buffer[slashIndex] == '\\'.code.toByte()) { if (buffer[slashIndex] == '\\'.code.toByte()) {
val end = escIndex + 2 val end = escIndex + 2
offset = end offset = end
return handler(escIndex, end) return handler(b3Index, escIndex, end)
?: UnknownEvent(buffer.copyOfRange(start, end))
} }
searchFrom = slashIndex searchFrom = slashIndex
} }

View File

@ -13,7 +13,7 @@ public class UnknownEvent(
override fun toString(): String = buildString { override fun toString(): String = buildString {
append("UnknownEvent(") append("UnknownEvent(")
for (byte in bytes) { for (byte in bytes) {
append(byte.toString(16).padStart(2, '0')) append(byte.toUByte().toString(16).padStart(2, '0'))
} }
append(')') append(')')
} }