@@ -13,6 +13,34 @@ class ControlCodeParser extends EventEmitter implements ReadableStreamInterface
1313 private $ closed = false ;
1414 private $ buffer = '' ;
1515
16+ /**
17+ * we know about the following C1 types (7 bit only)
18+ *
19+ * followed by "[" means it's CSI (Control Sequence Introducer)
20+ * followed by "]" means it's OSC (Operating System Controls)
21+ * followed by "_" means it's APC (Application Program-Control)
22+ * followed by "P" means it's DPS (Device-Control string)
23+ * followed by "^" means it's PM (Privacy Message)
24+ *
25+ * Each of these will be parsed until the sequence ends and then emitted
26+ * under their respective name.
27+ *
28+ * All other C1 types will be emitted under the "c1" name without any
29+ * further processing.
30+ *
31+ * C1 types in 8 bit are currently not supported, as they require special
32+ * care with regards to whether UTF-8 mode is enabled. So far this has
33+ * turned out to be a non-issue because most terminal emulators *accept*
34+ * boths formats, but usually *send* in 7 bit mode exclusively.
35+ */
36+ private $ types = array (
37+ '[ ' => 'csi ' ,
38+ '] ' => 'osc ' ,
39+ '_ ' => 'apc ' ,
40+ 'P ' => 'dps ' ,
41+ '^ ' => 'pm ' ,
42+ );
43+
1644 public function __construct (ReadableStreamInterface $ input )
1745 {
1846 $ this ->input = $ input ;
@@ -95,11 +123,20 @@ public function handleData($data)
95123 break ;
96124 }
97125
98- $ found = false ;
126+ // if this is an unknown type, just emit as "c1" without further parsing
127+ if (!isset ($ this ->types [$ this ->buffer [1 ]])) {
128+ $ data = substr ($ this ->buffer , 0 , 2 );
129+ $ this ->buffer = (string )substr ($ this ->buffer , 2 );
99130
100- if ($ this ->buffer [1 ] === '[ ' ) {
101- // followed by "[" means it's CSI
131+ $ this ->emit ('c1 ' , array ($ data ));
132+ continue ;
133+ }
134+
135+ // this is known type, check for the sequence end
136+ $ type = $ this ->types [$ this ->buffer [1 ]];
137+ $ found = false ;
102138
139+ if ($ type === 'csi ' ) {
103140 // CSI is now at the start of the buffer, search final character
104141 for ($ i = 2 ; isset ($ this ->buffer [$ i ]); ++$ i ) {
105142 $ code = ord ($ this ->buffer [$ i ]);
@@ -109,39 +146,32 @@ public function handleData($data)
109146 $ data = substr ($ this ->buffer , 0 , $ i + 1 );
110147 $ this ->buffer = (string )substr ($ this ->buffer , $ i + 1 );
111148
112- $ this ->emit (' csi ' , array ($ data ));
149+ $ this ->emit ($ type , array ($ data ));
113150 $ found = true ;
114151 break ;
115152 }
116153 }
117- } elseif ($ this ->buffer [1 ] === '] ' ) {
118- // followed by "]" means it's OSC (Operating System Controls)
119-
120- // terminated by ST or BEL (whichever comes first)
154+ } else {
155+ // all other types are terminated by ST
156+ // only OSC can also be terminted by BEL (whichever comes first)
121157 $ st = strpos ($ this ->buffer , "\x1B\\" );
122- $ bel = strpos ($ this ->buffer , "\x07" );
158+ $ bel = ( $ type === ' osc ' ) ? strpos ($ this ->buffer , "\x07" ) : false ;
123159
124160 if ($ st !== false && ($ bel === false || $ bel > $ st )) {
125161 // ST comes before BEL or no BEL found
126162 $ data = substr ($ this ->buffer , 0 , $ st + 2 );
127163 $ this ->buffer = (string )substr ($ this ->buffer , $ st + 2 );
128164
129- $ this ->emit (' osc ' , array ($ data ));
165+ $ this ->emit ($ type , array ($ data ));
130166 $ found = true ;
131167 } elseif ($ bel !== false ) {
132168 // BEL comes before ST or no ST found
133169 $ data = substr ($ this ->buffer , 0 , $ bel + 1 );
134170 $ this ->buffer = (string )substr ($ this ->buffer , $ bel + 1 );
135171
136- $ this ->emit (' osc ' , array ($ data ));
172+ $ this ->emit ($ type , array ($ data ));
137173 $ found = true ;
138174 }
139- } else {
140- $ data = substr ($ this ->buffer , 0 , 2 );
141- $ this ->buffer = (string )substr ($ this ->buffer , 2 );
142-
143- $ this ->emit ('data ' , array ($ data ));
144- continue ;
145175 }
146176
147177 // no final character found => wait for next data chunk
0 commit comments