@@ -26,6 +26,7 @@ enum ParserState : UInt8 {
2626 case csiIgnore
2727 case sosPmApcString
2828 case oscString
29+ case apcString
2930 case dcsEntry
3031 case dcsParam
3132 case dcsIgnore
@@ -42,6 +43,7 @@ class ParsingState {
4243 var print : Int
4344 var dcs : Int
4445 var osc : cstring
46+ var apc : cstring
4547 var collect : cstring
4648 var parameters : [ Int32 ]
4749 var abort : Bool
@@ -54,6 +56,7 @@ class ParsingState {
5456 print = 0
5557 dcs = 0
5658 osc = [ ]
59+ apc = [ ]
5760 collect = [ ]
5861 parameters = [ ]
5962 abort = false
@@ -170,7 +173,8 @@ public class EscapeSequenceParser {
170173 table. add ( code: 0x9c , state: state, action: . ignore, next: . ground) // ST as terminator
171174 table. add ( code: 0x1b , state: state, action: . clear, next: . escape) // ESC
172175 table. add ( code: 0x9d , state: state, action: . oscStart, next: . oscString) // OSC
173- table. add ( codes: [ 0x98 , 0x9e , 0x9f ] , state: state, action: . ignore, next: . sosPmApcString)
176+ table. add ( codes: [ 0x98 , 0x9e ] , state: state, action: . ignore, next: . sosPmApcString)
177+ table. add ( code: 0x9f , state: state, action: . oscStart, next: . apcString)
174178 table. add ( code: 0x9b , state: state, action: . clear, next: . csiEntry) // CSI
175179 table. add ( code: 0x90 , state: state, action: . clear, next: . dcsEntry) // DCS
176180 }
@@ -179,6 +183,7 @@ public class EscapeSequenceParser {
179183 table. add ( codes: executables, state: . escape, action: . execute, next: . escape)
180184 table. add ( code: 0x7f , state: . escape, action: . ignore, next: . escape)
181185 table. add ( codes: executables, state: . oscString, action: . ignore, next: . oscString)
186+ table. add ( codes: executables, state: . apcString, action: . ignore, next: . apcString)
182187 table. add ( codes: executables, state: . csiEntry, action: . execute, next: . csiEntry)
183188 table. add ( code: 0x7f , state: . csiEntry, action: . ignore, next: . csiEntry)
184189 table. add ( codes: executables, state: . csiParam, action: . execute, next: . csiParam)
@@ -194,8 +199,14 @@ public class EscapeSequenceParser {
194199 table. add ( code: 0x7f , state: . oscString, action: . oscPut, next: . oscString)
195200 table. add ( codes: [ 0x9c , 0x1b , 0x18 , 0x1a , 0x07 ] , state: . oscString, action: . oscEnd, next: . ground)
196201 table. add ( codes: r ( low: 0x1c , high: 0x20 ) , state: . oscString, action: . ignore, next: . oscString)
197- // sos/pm/apc does nothing
198- table. add ( codes: [ 0x58 , 0x5e , 0x5f ] , state: . escape, action: . ignore, next: . sosPmApcString)
202+ // apc
203+ table. add ( code: 0x5f , state: . escape, action: . oscStart, next: . apcString)
204+ table. add ( codes: printables, state: . apcString, action: . oscPut, next: . apcString)
205+ table. add ( code: 0x7f , state: . apcString, action: . oscPut, next: . apcString)
206+ table. add ( codes: [ 0x9c , 0x1b , 0x18 , 0x1a , 0x07 ] , state: . apcString, action: . oscEnd, next: . ground)
207+ table. add ( codes: r ( low: 0x1c , high: 0x20 ) , state: . apcString, action: . ignore, next: . apcString)
208+ // sos/pm does nothing
209+ table. add ( codes: [ 0x58 , 0x5e ] , state: . escape, action: . ignore, next: . sosPmApcString)
199210 table. add ( codes: printables, state: . sosPmApcString, action: . ignore, next: . sosPmApcString)
200211 table. add ( codes: executables, state: . sosPmApcString, action: . ignore, next: . sosPmApcString)
201212 table. add ( code: 0x9c , state: . sosPmApcString, action: . ignore, next: . ground)
@@ -263,6 +274,7 @@ public class EscapeSequenceParser {
263274 table. add ( code: 0x7f , state: . dcsPassthrough, action: . ignore, next: . dcsPassthrough)
264275 table. add ( codes: [ 0x1b , 0x9c ] , state: . dcsPassthrough, action: . dcsUnhook, next: . ground)
265276 table. add ( code: NonAsciiPrintable, state: . oscString, action: . oscPut, next: . oscString)
277+ table. add ( code: NonAsciiPrintable, state: . apcString, action: . oscPut, next: . apcString)
266278 return table
267279 }
268280
@@ -277,6 +289,10 @@ public class EscapeSequenceParser {
277289 /// receive both the OSC code as the first parameter, along with a byte array containing
278290 /// the payload for the OSC message.
279291 public typealias OscHandlerFallback = ( Int , ArraySlice < UInt8 > ) -> ( )
292+
293+ /// Signature for an APC handler, it will receive the byte array containing the data to this APC sequence
294+ public typealias ApcHandler = ( ArraySlice < UInt8 > ) -> ( )
295+ public typealias ApcHandlerFallback = ( UInt8 , ArraySlice < UInt8 > ) -> ( )
280296
281297 typealias DscHandlerFallback = ( UInt8 , [ Int ] ) -> ( )
282298
@@ -301,6 +317,7 @@ public class EscapeSequenceParser {
301317 /// }
302318 /// ```
303319 public var oscHandlers : [ Int : OscHandler ] = [ : ]
320+ public var apcHandlers : [ UInt8 : ApcHandler ] = [ : ]
304321 var executeHandlers : [ UInt8 : ExecuteHandler ] = [ : ]
305322 var escHandlers : [ cstring : EscHandler ] = [ : ]
306323 var dcsHandlers : [ cstring : DcsHandler ] = [ : ]
@@ -313,6 +330,7 @@ public class EscapeSequenceParser {
313330
314331 // buffers over several calls
315332 var _osc : cstring
333+ var _apc : cstring
316334 var _pars : [ Int ]
317335 var _parsTxt : [ UInt8 ]
318336 var _collect : cstring
@@ -325,6 +343,7 @@ public class EscapeSequenceParser {
325343 {
326344 table = EscapeSequenceParser . buildVt500TransitionTable ( )
327345 _osc = [ ]
346+ _apc = [ ]
328347 _pars = [ 0 ]
329348 _parsTxt = [ ]
330349 _collect = [ ]
@@ -340,6 +359,11 @@ public class EscapeSequenceParser {
340359 escHandlers [ Array ( flag. utf8) ] = callback
341360 }
342361
362+ func setApcHandler ( _ flag: String , _ callback: @escaping ApcHandler )
363+ {
364+ apcHandlers [ flag. first!. asciiValue!] = callback
365+ }
366+
343367 func setCsiHandler ( _ flag: String , _ callback: @escaping CsiHandler )
344368 {
345369 csiHandlers [ flag. first!. asciiValue!] = callback
@@ -361,12 +385,16 @@ public class EscapeSequenceParser {
361385
362386 var oscHandlerFallback : OscHandlerFallback = { code, data -> ( ) in
363387
388+ }
389+ var apcHandlerFallback : ApcHandlerFallback = { code, data -> ( ) in
390+
364391 }
365392
366393 func reset ( )
367394 {
368395 currentState = initialState
369396 _osc = [ ]
397+ _apc = [ ]
370398 _pars = [ 0 ]
371399 _collect = [ ]
372400 activeDcsHandler = nil
@@ -397,6 +425,7 @@ public class EscapeSequenceParser {
397425 var print = - 1
398426 var dcs = - 1
399427 var osc = self . _osc
428+ var apc = self . _apc
400429 var collect = self . _collect
401430 var pars = self . _pars
402431 var parsTxt = self . _parsTxt
@@ -493,6 +522,7 @@ public class EscapeSequenceParser {
493522 state. print = print
494523 state. dcs = dcs
495524 state. osc = osc
525+ state. apc = apc
496526 state. collect = collect
497527 let inject = errorHandler ( state)
498528 if inject. abort {
@@ -533,6 +563,7 @@ public class EscapeSequenceParser {
533563 print = - 1
534564 }
535565 osc = [ ]
566+ apc = [ ]
536567 pars = [ 0 ]
537568 parsTxt = [ ]
538569 collect = [ ]
@@ -559,6 +590,7 @@ public class EscapeSequenceParser {
559590 transition |= ParserState . escape. rawValue
560591 }
561592 osc = [ ]
593+ apc = [ ]
562594 pars = [ 0 ]
563595 parsTxt = [ ]
564596 collect = [ ]
@@ -569,44 +601,66 @@ public class EscapeSequenceParser {
569601 printHandler ( data [ print..< i] )
570602 print = - 1
571603 }
572- osc = [ ]
604+ let nextState = ParserState ( rawValue: transition & 15 ) !
605+ if nextState == . apcString {
606+ apc = [ ]
607+ } else {
608+ osc = [ ]
609+ }
573610 case . oscPut:
574611 var j = i
575612 while j < end {
576613 let c = data [ j]
577614 if c == ControlCodes . BEL || c == ControlCodes . CAN || c == ControlCodes . ESC {
578615 break
579616 } else if c >= 0x20 {
580- osc. append ( c)
617+ if currentState == . apcString {
618+ apc. append ( c)
619+ } else {
620+ osc. append ( c)
621+ }
581622 }
582623 j += 1
583624 }
584625 i = j - 1
585626 case . oscEnd:
586- if osc. count != 0 && code != ControlCodes . CAN && code != ControlCodes . SUB {
587- // NOTE: OSC subparsing is not part of the original parser
588- // we do basic identifier parsing here to offer a jump table for OSC as well
589- var oscCode : Int
590- var content : ArraySlice < UInt8 >
591- let semiColonAscii = 59 // ';'
592-
593- if let idx = osc. firstIndex ( of: UInt8 ( semiColonAscii) ) {
594- oscCode = EscapeSequenceParser . parseInt ( osc [ 0 ..< idx] )
595- content = osc [ ( idx+ 1 ) ... ]
596- } else {
597- oscCode = EscapeSequenceParser . parseInt ( osc [ 0 ... ] )
598- content = [ ]
627+ if currentState == . apcString {
628+ if apc. count != 0 && code != ControlCodes . CAN && code != ControlCodes . SUB {
629+ let command = apc [ apc. startIndex]
630+ let content = apc. count > 1 ? apc [ ( apc. startIndex+ 1 ) ... ] : ArraySlice < UInt8 > ( )
631+ if let handler = apcHandlers [ command] {
632+ handler ( content)
633+ } else {
634+ apcHandlerFallback ( command, content)
635+ }
599636 }
600- if let handler = oscHandlers [ oscCode] {
601- handler ( content)
602- } else {
603- oscHandlerFallback ( oscCode, content)
637+ } else {
638+ if osc. count != 0 && code != ControlCodes . CAN && code != ControlCodes . SUB {
639+ // NOTE: OSC subparsing is not part of the original parser
640+ // we do basic identifier parsing here to offer a jump table for OSC as well
641+ var oscCode : Int
642+ var content : ArraySlice < UInt8 >
643+ let semiColonAscii = 59 // ';'
644+
645+ if let idx = osc. firstIndex ( of: UInt8 ( semiColonAscii) ) {
646+ oscCode = EscapeSequenceParser . parseInt ( osc [ 0 ..< idx] )
647+ content = osc [ ( idx+ 1 ) ... ]
648+ } else {
649+ oscCode = EscapeSequenceParser . parseInt ( osc [ 0 ... ] )
650+ content = [ ]
651+ }
652+ if let handler = oscHandlers [ oscCode] {
653+ handler ( content)
654+ } else {
655+ oscHandlerFallback ( oscCode, content)
656+ }
604657 }
605658 }
606659 if code == 0x1b {
607660 transition |= ParserState . escape. rawValue
608661 }
609662 osc = [ ]
663+ apc = [ ]
610664 pars = [ 0 ]
611665 parsTxt = [ ]
612666 collect = [ ]
@@ -624,6 +678,7 @@ public class EscapeSequenceParser {
624678 }
625679 // save non pushable buffers
626680 _osc = osc
681+ _apc = apc
627682 _collect = collect
628683 _pars = pars
629684 _parsTxt = parsTxt
0 commit comments