| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Parser for escape sequences sent by terminals to clients. | ||
| 2 | // Copyright © Craig Barnes. | ||
| 3 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 4 | // See also: | ||
| 5 | // • https://invisible-island.net/xterm/ctlseqs/ctlseqs.html | ||
| 6 | // • https://sw.kovidgoyal.net/kitty/keyboard-protocol/ | ||
| 7 | // • ECMA-48 §5.4 (https://ecma-international.org/publications-and-standards/standards/ecma-48/) | ||
| 8 | // • https://vt100.net/emu/dec_ansi_parser | ||
| 9 | |||
| 10 | #include "parse.h" | ||
| 11 | #include "query.h" | ||
| 12 | #include "util/ascii.h" | ||
| 13 | #include "util/debug.h" | ||
| 14 | #include "util/log.h" | ||
| 15 | #include "util/unicode.h" | ||
| 16 | |||
| 17 | typedef enum { | ||
| 18 | BYTE_CONTROL, // 0x00..0x1F | ||
| 19 | BYTE_INTERMEDIATE, // 0x20..0x2F | ||
| 20 | BYTE_PARAMETER, // 0x30..0x3F | ||
| 21 | BYTE_FINAL, // 0x40..0x6F | ||
| 22 | BYTE_FINAL_PRIVATE, // 0x70..0x7E | ||
| 23 | BYTE_DELETE, // 0x7F | ||
| 24 | BYTE_OTHER, // 0x80..0xFF | ||
| 25 | } Ecma48ByteType; | ||
| 26 | |||
| 27 | // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-functional-keys | ||
| 28 | 503 | static KeyCode decode_key_from_final_byte(uint8_t byte) | |
| 29 | { | ||
| 30 |
14/14✓ Branch 0 (2→3) taken 43 times.
✓ Branch 1 (2→4) taken 43 times.
✓ Branch 2 (2→5) taken 43 times.
✓ Branch 3 (2→6) taken 43 times.
✓ Branch 4 (2→7) taken 43 times.
✓ Branch 5 (2→8) taken 46 times.
✓ Branch 6 (2→9) taken 1 times.
✓ Branch 7 (2→10) taken 1 times.
✓ Branch 8 (2→11) taken 42 times.
✓ Branch 9 (2→12) taken 42 times.
✓ Branch 10 (2→13) taken 42 times.
✓ Branch 11 (2→14) taken 42 times.
✓ Branch 12 (2→15) taken 25 times.
✓ Branch 13 (2→16) taken 47 times.
|
503 | switch (byte) { |
| 31 | case 'A': return KEY_UP; | ||
| 32 | 43 | case 'B': return KEY_DOWN; | |
| 33 | 43 | case 'C': return KEY_RIGHT; | |
| 34 | 43 | case 'D': return KEY_LEFT; | |
| 35 | 43 | case 'E': return KEY_BEGIN; // (keypad '5') | |
| 36 | 43 | case 'F': return KEY_END; | |
| 37 | 46 | case 'H': return KEY_HOME; | |
| 38 | 1 | case 'L': return KEY_INSERT; | |
| 39 | 1 | case 'M': return KEY_ENTER; | |
| 40 | 42 | case 'P': return KEY_F1; | |
| 41 | 42 | case 'Q': return KEY_F2; | |
| 42 | 42 | case 'R': return KEY_F3; | |
| 43 | 42 | case 'S': return KEY_F4; | |
| 44 | } | ||
| 45 | 25 | return KEY_IGNORE; | |
| 46 | } | ||
| 47 | |||
| 48 | // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-functional-keys | ||
| 49 | // https://gitlab.com/craigbarnes/dte/-/commit/f540904cfdbb04b4cafdff0d7b15e3fd188395d4 | ||
| 50 | // https://gitlab.com/craigbarnes/dte/-/issues/121 | ||
| 51 | 995 | static KeyCode decode_key_from_param(uint32_t param) | |
| 52 | { | ||
| 53 |
28/28✓ Branch 0 (2→3) taken 41 times.
✓ Branch 1 (2→4) taken 41 times.
✓ Branch 2 (2→5) taken 1 times.
✓ Branch 3 (2→6) taken 41 times.
✓ Branch 4 (2→7) taken 44 times.
✓ Branch 5 (2→8) taken 1 times.
✓ Branch 6 (2→9) taken 41 times.
✓ Branch 7 (2→10) taken 41 times.
✓ Branch 8 (2→11) taken 41 times.
✓ Branch 9 (2→12) taken 41 times.
✓ Branch 10 (2→13) taken 41 times.
✓ Branch 11 (2→14) taken 41 times.
✓ Branch 12 (2→15) taken 41 times.
✓ Branch 13 (2→16) taken 41 times.
✓ Branch 14 (2→17) taken 41 times.
✓ Branch 15 (2→18) taken 41 times.
✓ Branch 16 (2→19) taken 41 times.
✓ Branch 17 (2→20) taken 41 times.
✓ Branch 18 (2→21) taken 41 times.
✓ Branch 19 (2→22) taken 41 times.
✓ Branch 20 (2→23) taken 41 times.
✓ Branch 21 (2→24) taken 41 times.
✓ Branch 22 (2→25) taken 41 times.
✓ Branch 23 (2→26) taken 40 times.
✓ Branch 24 (2→27) taken 40 times.
✓ Branch 25 (2→28) taken 41 times.
✓ Branch 26 (2→29) taken 6 times.
✓ Branch 27 (2→30) taken 2 times.
|
995 | switch (param) { |
| 54 | case 1: return KEY_HOME; | ||
| 55 | 41 | case 2: return KEY_INSERT; | |
| 56 | 41 | case 3: return KEY_DELETE; | |
| 57 | 1 | case 4: return KEY_END; | |
| 58 | 41 | case 5: return KEY_PAGE_UP; | |
| 59 | 44 | case 6: return KEY_PAGE_DOWN; | |
| 60 | case 7: return KEY_HOME; | ||
| 61 | 1 | case 8: return KEY_END; | |
| 62 | 41 | case 11: return KEY_F1; | |
| 63 | 41 | case 12: return KEY_F2; | |
| 64 | 41 | case 13: return KEY_F3; | |
| 65 | 41 | case 14: return KEY_F4; | |
| 66 | 41 | case 15: return KEY_F5; | |
| 67 | 41 | case 17: return KEY_F6; | |
| 68 | 41 | case 18: return KEY_F7; | |
| 69 | 41 | case 19: return KEY_F8; | |
| 70 | 41 | case 20: return KEY_F9; | |
| 71 | 41 | case 21: return KEY_F10; | |
| 72 | 41 | case 23: return KEY_F11; | |
| 73 | 41 | case 24: return KEY_F12; | |
| 74 | 41 | case 25: return KEY_F13; | |
| 75 | 41 | case 26: return KEY_F14; | |
| 76 | 41 | case 28: return KEY_F15; | |
| 77 | 41 | case 29: return KEY_F16; | |
| 78 | 41 | case 31: return KEY_F17; | |
| 79 | 40 | case 32: return KEY_F18; | |
| 80 | 40 | case 33: return KEY_F19; | |
| 81 | 41 | case 34: return KEY_F20; | |
| 82 | } | ||
| 83 | 6 | return KEY_IGNORE; | |
| 84 | } | ||
| 85 | |||
| 86 | // See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#modifiers | ||
| 87 | // ---------------------------- | ||
| 88 | // Shift 0b1 (1) | ||
| 89 | // Alt 0b10 (2) | ||
| 90 | // Ctrl 0b100 (4) | ||
| 91 | // Super 0b1000 (8) | ||
| 92 | // Hyper 0b10000 (16) | ||
| 93 | // Meta 0b100000 (32) | ||
| 94 | // Capslock 0b1000000 (64) | ||
| 95 | // Numlock 0b10000000 (128) | ||
| 96 | // ---------------------------- | ||
| 97 | 1666 | static KeyCode decode_modifiers(uint32_t n) | |
| 98 | { | ||
| 99 | 1666 | n--; | |
| 100 |
2/2✓ Branch 0 (2→3) taken 1455 times.
✓ Branch 1 (2→4) taken 211 times.
|
1666 | if (unlikely(n > 255)) { |
| 101 | return KEY_IGNORE; | ||
| 102 | } | ||
| 103 | |||
| 104 | 1455 | static_assert(1 == MOD_SHIFT >> KEYCODE_MODIFIER_OFFSET); | |
| 105 | 1455 | static_assert(2 == MOD_META >> KEYCODE_MODIFIER_OFFSET); | |
| 106 | 1455 | static_assert(4 == MOD_CTRL >> KEYCODE_MODIFIER_OFFSET); | |
| 107 | 1455 | static_assert(8 == MOD_SUPER >> KEYCODE_MODIFIER_OFFSET); | |
| 108 | 1455 | static_assert(16 == MOD_HYPER >> KEYCODE_MODIFIER_OFFSET); | |
| 109 | 1455 | static_assert(31 == MOD_MASK >> KEYCODE_MODIFIER_OFFSET); | |
| 110 | |||
| 111 | // Decode Meta and/or Alt as MOD_META and ignore Capslock/Numlock | ||
| 112 | 1455 | KeyCode mods = (n & 31) | ((n & 32) >> 4); | |
| 113 | 1455 | return mods << KEYCODE_MODIFIER_OFFSET; | |
| 114 | } | ||
| 115 | |||
| 116 | // Normalize KeyCode values originating from `CSI u` sequences. | ||
| 117 | // Note that, unlike normalize_csi_27_tilde_keycode(), the `mods` | ||
| 118 | // parameter is only passed in for context and not expected to be | ||
| 119 | // included in the returned value. | ||
| 120 | 78 | static KeyCode normalize_csi_u_keycode(KeyCode mods, KeyCode key) | |
| 121 | { | ||
| 122 | // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions | ||
| 123 |
49/51✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 1 times.
✗ Branch 2 (2→5) not taken.
✓ Branch 3 (2→6) taken 3 times.
✓ Branch 4 (2→7) taken 1 times.
✓ Branch 5 (2→8) taken 2 times.
✓ Branch 6 (2→9) taken 1 times.
✓ Branch 7 (2→10) taken 1 times.
✓ Branch 8 (2→11) taken 1 times.
✓ Branch 9 (2→12) taken 1 times.
✓ Branch 10 (2→13) taken 1 times.
✓ Branch 11 (2→14) taken 1 times.
✓ Branch 12 (2→15) taken 1 times.
✓ Branch 13 (2→16) taken 1 times.
✓ Branch 14 (2→17) taken 1 times.
✓ Branch 15 (2→18) taken 1 times.
✓ Branch 16 (2→19) taken 1 times.
✓ Branch 17 (2→20) taken 1 times.
✓ Branch 18 (2→21) taken 1 times.
✓ Branch 19 (2→22) taken 1 times.
✓ Branch 20 (2→23) taken 1 times.
✓ Branch 21 (2→24) taken 1 times.
✓ Branch 22 (2→25) taken 1 times.
✓ Branch 23 (2→26) taken 1 times.
✓ Branch 24 (2→27) taken 1 times.
✓ Branch 25 (2→28) taken 1 times.
✓ Branch 26 (2→29) taken 1 times.
✓ Branch 27 (2→30) taken 1 times.
✓ Branch 28 (2→31) taken 1 times.
✓ Branch 29 (2→32) taken 1 times.
✓ Branch 30 (2→33) taken 1 times.
✓ Branch 31 (2→34) taken 1 times.
✓ Branch 32 (2→35) taken 1 times.
✓ Branch 33 (2→36) taken 1 times.
✓ Branch 34 (2→37) taken 1 times.
✓ Branch 35 (2→38) taken 1 times.
✓ Branch 36 (2→39) taken 1 times.
✓ Branch 37 (2→40) taken 1 times.
✓ Branch 38 (2→41) taken 1 times.
✓ Branch 39 (2→42) taken 1 times.
✓ Branch 40 (2→43) taken 1 times.
✓ Branch 41 (2→44) taken 1 times.
✓ Branch 42 (2→45) taken 1 times.
✓ Branch 43 (2→46) taken 1 times.
✓ Branch 44 (2→47) taken 1 times.
✓ Branch 45 (2→48) taken 1 times.
✓ Branch 46 (2→49) taken 1 times.
✓ Branch 47 (2→50) taken 1 times.
✓ Branch 48 (2→51) taken 1 times.
✓ Branch 49 (2→52) taken 27 times.
✗ Branch 50 (2→56) not taken.
|
78 | switch (key) { |
| 124 | case '\b': return KEY_BACKSPACE; // BS; Kitty never emits this, but (buggy) WezTerm does | ||
| 125 | 1 | case '\r': return KEY_ENTER; | |
| 126 | 1 | case '\t': return KEY_TAB; | |
| 127 | ✗ | case '\n': return KEY_ENTER; // Kitty never emits this | |
| 128 | 3 | case 27: return KEY_ESCAPE; // ESC | |
| 129 | case 127: return KEY_BACKSPACE; // DEL | ||
| 130 | 1 | case 57359: return KEY_SCROLL_LOCK; | |
| 131 | 2 | case 57361: return KEY_PRINT_SCREEN; | |
| 132 | 1 | case 57362: return KEY_PAUSE; | |
| 133 | 1 | case 57363: return KEY_MENU; | |
| 134 | 1 | case 57376: return KEY_F13; | |
| 135 | 1 | case 57377: return KEY_F14; | |
| 136 | 1 | case 57378: return KEY_F15; | |
| 137 | 1 | case 57379: return KEY_F16; | |
| 138 | 1 | case 57380: return KEY_F17; | |
| 139 | 1 | case 57381: return KEY_F18; | |
| 140 | 1 | case 57382: return KEY_F19; | |
| 141 | 1 | case 57383: return KEY_F20; | |
| 142 | 1 | case 57384: return KEY_F21; | |
| 143 | 1 | case 57385: return KEY_F22; | |
| 144 | 1 | case 57386: return KEY_F23; | |
| 145 | 1 | case 57387: return KEY_F24; | |
| 146 | // Keypad: | ||
| 147 | 1 | case 57399: return '0'; | |
| 148 | 1 | case 57400: return '1'; | |
| 149 | 1 | case 57401: return '2'; | |
| 150 | 1 | case 57402: return '3'; | |
| 151 | 1 | case 57403: return '4'; | |
| 152 | 1 | case 57404: return '5'; | |
| 153 | 1 | case 57405: return '6'; | |
| 154 | 1 | case 57406: return '7'; | |
| 155 | 1 | case 57407: return '8'; | |
| 156 | 1 | case 57408: return '9'; | |
| 157 | 1 | case 57409: return '.'; | |
| 158 | 1 | case 57410: return '/'; | |
| 159 | 1 | case 57411: return '*'; | |
| 160 | 1 | case 57412: return '-'; | |
| 161 | 1 | case 57413: return '+'; | |
| 162 | 1 | case 57414: return KEY_ENTER; | |
| 163 | 1 | case 57415: return '='; | |
| 164 | 1 | case 57416: return KEY_IGNORE; // KP_SEPARATOR | |
| 165 | 1 | case 57417: return KEY_LEFT; | |
| 166 | 1 | case 57418: return KEY_RIGHT; | |
| 167 | 1 | case 57419: return KEY_UP; | |
| 168 | 1 | case 57420: return KEY_DOWN; | |
| 169 | 1 | case 57421: return KEY_PAGE_UP; | |
| 170 | 1 | case 57422: return KEY_PAGE_DOWN; | |
| 171 | 1 | case 57423: return KEY_HOME; | |
| 172 | 1 | case 57424: return KEY_END; | |
| 173 | 1 | case 57425: return KEY_INSERT; | |
| 174 | 1 | case 57426: return KEY_DELETE; | |
| 175 | 1 | case 57427: return KEY_BEGIN; | |
| 176 | } | ||
| 177 | |||
| 178 |
2/2✓ Branch 0 (52→53) taken 15 times.
✓ Branch 1 (52→56) taken 12 times.
|
27 | if (unlikely(key < 32 || (key >= 57344 && key <= 63743))) { |
| 179 | // Ignore values (not already handled above) that correspond | ||
| 180 | // to C0 controls or the Kitty private use range | ||
| 181 | return KEY_IGNORE; | ||
| 182 | } | ||
| 183 | |||
| 184 |
3/4✓ Branch 0 (53→54) taken 1 times.
✓ Branch 1 (53→56) taken 14 times.
✗ Branch 2 (54→55) not taken.
✓ Branch 3 (54→56) taken 1 times.
|
15 | if (u_is_ascii_upper(key) && (mods & MOD_CTRL)) { |
| 185 | // This was originally done for the sake of iTerm2's `CSI u` mode, | ||
| 186 | // which could be activated with `CSI > 1 u` but didn't fully | ||
| 187 | // conform to Kitty's key encoding scheme. This mode has seemingly | ||
| 188 | // been replaced with more complete support for the Kitty protocol | ||
| 189 | // and explicit support for activating the old mode has been also | ||
| 190 | // been removed from dte. However, this code is retained for now, | ||
| 191 | // since the `CSI u` encoding pre-dates the Kitty keyboard protocol | ||
| 192 | // and it's not clear yet whether removing it would regress support | ||
| 193 | // in less modern terminals and/or improve correctness for exotic | ||
| 194 | // keyboard layouts in modern (Kitty protocol supporting) terminals. | ||
| 195 | // See also: | ||
| 196 | // • https://gitlab.com/craigbarnes/dte/-/issues/130#note_870592688 | ||
| 197 | // • https://gitlab.com/craigbarnes/dte/-/issues/130#note_864512674 | ||
| 198 | // • https://gitlab.com/gnachman/iterm2/-/issues/10017 | ||
| 199 | // • https://gitlab.com/gnachman/iterm2/-/commit/9cd0241afd0655024153c8730d5b3ed1fe41faf7 | ||
| 200 | // • https://gitlab.com/gnachman/iterm2/-/commit/9cd0241afd0655024153c8730d5b3ed1fe41faf7#1d96fc7f79950509a8bc22bc59a1a82a438c890d_0_17 | ||
| 201 | // • https://gitlab.com/gnachman/iterm2/-/issues/7440#note_129599012 | ||
| 202 | // • https://sw.kovidgoyal.net/kitty/keyboard-protocol/#bugs-in-fixterms:~:text=Incorrectly%20encoding%20shifted%20keys%20when%20shift%20modifier%20is%20used | ||
| 203 | ✗ | return ascii_tolower(key); | |
| 204 | } | ||
| 205 | |||
| 206 | return key; | ||
| 207 | } | ||
| 208 | |||
| 209 | // Normalize KeyCode values originating from xterm-style "modifyOtherKeys" | ||
| 210 | // sequences (CSI 27 ; <modifiers> ; <key> ~) | ||
| 211 | 18 | static KeyCode normalize_csi_27_tilde_keycode(KeyCode mods, KeyCode key) | |
| 212 | { | ||
| 213 |
6/7✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 2 times.
✓ Branch 2 (2→5) taken 1 times.
✗ Branch 3 (2→6) not taken.
✓ Branch 4 (2→7) taken 3 times.
✓ Branch 5 (2→8) taken 3 times.
✓ Branch 6 (2→9) taken 6 times.
|
18 | switch (key) { |
| 214 | // https://codeberg.org/dnkl/foot/pulls/791#issuecomment-279784 | ||
| 215 | 3 | case '\b': return mods | KEY_BACKSPACE; | |
| 216 | 2 | case '\r': return mods | KEY_ENTER; | |
| 217 | 1 | case '\t': return mods | KEY_TAB; | |
| 218 | ✗ | case '\n': return mods | KEY_ENTER; | |
| 219 | 3 | case 27: return mods | KEY_ESCAPE; // ESC | |
| 220 | 3 | case 127: return mods | KEY_BACKSPACE; // DEL | |
| 221 | } | ||
| 222 | |||
| 223 |
1/2✓ Branch 0 (9→10) taken 6 times.
✗ Branch 1 (9→14) not taken.
|
6 | if (unlikely(key < 32)) { |
| 224 | return KEY_IGNORE; | ||
| 225 | } | ||
| 226 | |||
| 227 |
2/2✓ Branch 0 (10→11) taken 2 times.
✓ Branch 1 (10→13) taken 4 times.
|
6 | if (u_is_ascii_upper(key)) { |
| 228 |
1/2✓ Branch 0 (11→12) taken 2 times.
✗ Branch 1 (11→14) not taken.
|
2 | if ((mods & ~MOD_SHIFT) == 0) { |
| 229 | // [A-Z] and Shift+[A-Z] should be encoded as just [A-Z] | ||
| 230 | return key; | ||
| 231 | } | ||
| 232 | // [A-Z] with any other combination of modifiers should be | ||
| 233 | // converted to lowercase and have the MOD_SHIFT bit set. | ||
| 234 | // This is done in a "blanket" fashion and covers sequences | ||
| 235 | // that xterm never emits, because some terminals (e.g. tmux) | ||
| 236 | // emulate the protocol imperfectly. | ||
| 237 | 2 | return mods | MOD_SHIFT | key | 0x20; | |
| 238 | } | ||
| 239 | |||
| 240 | 4 | return mods | key; | |
| 241 | } | ||
| 242 | |||
| 243 | 73 | static ssize_t parse_ss3(const char *buf, size_t length, size_t i, KeyCode *k) | |
| 244 | { | ||
| 245 |
2/2✓ Branch 0 (2→3) taken 36 times.
✓ Branch 1 (2→13) taken 37 times.
|
73 | if (unlikely(i >= length)) { |
| 246 | return TPARSE_PARTIAL_MATCH; | ||
| 247 | } | ||
| 248 | |||
| 249 | 36 | const char ch = buf[i++]; | |
| 250 | 36 | KeyCode key = decode_key_from_final_byte(ch); | |
| 251 |
2/2✓ Branch 0 (3→4) taken 12 times.
✓ Branch 1 (3→5) taken 24 times.
|
36 | if (key != KEY_IGNORE) { |
| 252 | 12 | *k = key; | |
| 253 |
2/2✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 23 times.
|
24 | } else if (ch == 'X') { |
| 254 | 1 | *k = '='; | |
| 255 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 22 times.
|
23 | } else if (ch == ' ') { |
| 256 | 1 | *k = KEY_SPACE; | |
| 257 |
2/2✓ Branch 0 (9→10) taken 17 times.
✓ Branch 1 (9→11) taken 5 times.
|
22 | } else if ((ch >= 'j' && ch <= 'y') || ch == 'I') { |
| 258 | 17 | *k = ch - 64; | |
| 259 | } else { | ||
| 260 | 5 | *k = KEY_IGNORE; | |
| 261 | } | ||
| 262 | |||
| 263 | 36 | return i; | |
| 264 | } | ||
| 265 | |||
| 266 | 1959 | static Ecma48ByteType get_byte_type(unsigned char byte) | |
| 267 | { | ||
| 268 | 1959 | enum { | |
| 269 | C = BYTE_CONTROL, | ||
| 270 | I = BYTE_INTERMEDIATE, | ||
| 271 | P = BYTE_PARAMETER, | ||
| 272 | F = BYTE_FINAL, | ||
| 273 | f = BYTE_FINAL_PRIVATE, | ||
| 274 | x = BYTE_OTHER, | ||
| 275 | }; | ||
| 276 | |||
| 277 | // ECMA-48 divides bytes ("bit combinations") into rows of 16 columns. | ||
| 278 | // The byte classifications mostly fall into their own rows: | ||
| 279 | 1959 | static const uint8_t rows[16] = { | |
| 280 | C, C, I, P, F, F, F, f, | ||
| 281 | x, x, x, x, x, x, x, x | ||
| 282 | }; | ||
| 283 | |||
| 284 | // ... with the exception of byte 127 (DEL), which falls into rows[7] | ||
| 285 | // but isn't a private final byte like the others in that row: | ||
| 286 | 1959 | static_assert(BYTE_FINAL_PRIVATE + 1 == BYTE_DELETE); | |
| 287 | 1959 | unsigned int del_offset = (byte == 127); | |
| 288 | 1959 | return rows[byte >> 4] + del_offset; | |
| 289 | } | ||
| 290 | |||
| 291 | #define UNHANDLED(var, ...) unhandled(var, __LINE__, __VA_ARGS__) | ||
| 292 | |||
| 293 | COLD PRINTF(3) | ||
| 294 | 398 | static void unhandled(bool *var, int line, const char *fmt, ...) | |
| 295 | { | ||
| 296 |
2/2✓ Branch 0 (2→3) taken 92 times.
✓ Branch 1 (2→5) taken 306 times.
|
398 | if (*var) { |
| 297 | // Only log the first error in a sequence | ||
| 298 | return; | ||
| 299 | } | ||
| 300 | |||
| 301 | 92 | *var = true; | |
| 302 | 92 | if (DEBUG_LOGGING_ENABLED) { | |
| 303 | 92 | va_list ap; | |
| 304 | 92 | va_start(ap, fmt); | |
| 305 | 92 | log_msgv(LOG_LEVEL_DEBUG, __FILE__, line, fmt, ap); | |
| 306 | 92 | va_end(ap); | |
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | 7476 | size_t term_parse_csi_params(const char *buf, size_t len, size_t i, TermControlParams *csi) | |
| 311 | { | ||
| 312 | 7476 | size_t nparams = 0; | |
| 313 | 7476 | size_t nr_intermediate = 0; | |
| 314 | 7476 | size_t sub = 0; | |
| 315 | 7476 | size_t digits = 0; | |
| 316 | 7476 | uint32_t num = 0; | |
| 317 | 7476 | bool have_subparams = false; | |
| 318 | 7476 | bool err = false; | |
| 319 | |||
| 320 |
2/2✓ Branch 0 (36→3) taken 23664 times.
✓ Branch 1 (36→37) taken 5637 times.
|
29301 | while (i < len) { |
| 321 | 23664 | const char ch = buf[i++]; | |
| 322 |
4/4✓ Branch 0 (3→4) taken 17162 times.
✓ Branch 1 (3→7) taken 4509 times.
✓ Branch 2 (3→11) taken 34 times.
✓ Branch 3 (3→15) taken 1959 times.
|
23664 | switch (ch) { |
| 323 | 17162 | case '0': case '1': case '2': case '3': case '4': | |
| 324 | case '5': case '6': case '7': case '8': case '9': | ||
| 325 | 17162 | num = (num * 10) + (ch - '0'); | |
| 326 |
2/2✓ Branch 0 (4→5) taken 370 times.
✓ Branch 1 (4→6) taken 16792 times.
|
17162 | if (unlikely(num > UNICODE_MAX_VALID_CODEPOINT)) { |
| 327 | 370 | UNHANDLED(&err, "CSI param overflow"); | |
| 328 | } | ||
| 329 | 17162 | digits++; | |
| 330 | 17162 | continue; | |
| 331 | 4509 | case ';': | |
| 332 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 4509 times.
|
4509 | if (unlikely(nparams >= ARRAYLEN(csi->params))) { |
| 333 | ✗ | UNHANDLED(&err, "too many params in CSI sequence"); | |
| 334 | ✗ | continue; | |
| 335 | } | ||
| 336 | 4509 | csi->nsub[nparams] = sub + 1; | |
| 337 | 4509 | csi->params[nparams++][sub] = num; | |
| 338 | 4509 | num = 0; | |
| 339 | 4509 | digits = 0; | |
| 340 | 4509 | sub = 0; | |
| 341 | 4509 | continue; | |
| 342 | 34 | case ':': | |
| 343 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→14) taken 34 times.
|
34 | if (unlikely(sub >= ARRAYLEN(csi->params[0]))) { |
| 344 | ✗ | UNHANDLED(&err, "too many sub-params in CSI sequence"); | |
| 345 | ✗ | continue; | |
| 346 | } | ||
| 347 | 34 | csi->params[nparams][sub++] = num; | |
| 348 | 34 | num = 0; | |
| 349 | 34 | digits = 0; | |
| 350 | 34 | have_subparams = true; | |
| 351 | 34 | continue; | |
| 352 | } | ||
| 353 | |||
| 354 |
5/7✓ Branch 0 (15→16) taken 100 times.
✓ Branch 1 (15→20) taken 1834 times.
✓ Branch 2 (15→25) taken 12 times.
✓ Branch 3 (15→27) taken 9 times.
✓ Branch 4 (15→31) taken 4 times.
✗ Branch 5 (15→32) not taken.
✗ Branch 6 (15→34) not taken.
|
1959 | switch (get_byte_type(ch)) { |
| 355 | 100 | case BYTE_INTERMEDIATE: | |
| 356 |
2/2✓ Branch 0 (16→17) taken 11 times.
✓ Branch 1 (16→18) taken 89 times.
|
100 | if (unlikely(nr_intermediate >= ARRAYLEN(csi->intermediate))) { |
| 357 | 11 | UNHANDLED(&err, "too many intermediate bytes in CSI sequence"); | |
| 358 | } else { | ||
| 359 | 89 | csi->intermediate[nr_intermediate++] = ch; | |
| 360 | } | ||
| 361 | 100 | continue; | |
| 362 | 1834 | case BYTE_FINAL: | |
| 363 | case BYTE_FINAL_PRIVATE: | ||
| 364 | 1834 | csi->final_byte = ch; | |
| 365 |
2/2✓ Branch 0 (20→21) taken 1817 times.
✓ Branch 1 (20→24) taken 17 times.
|
1834 | if (digits > 0) { |
| 366 |
1/2✗ Branch 0 (21→22) not taken.
✓ Branch 1 (21→23) taken 1817 times.
|
1817 | if (unlikely( |
| 367 | nparams >= ARRAYLEN(csi->params) | ||
| 368 | || sub >= ARRAYLEN(csi->params[0]) | ||
| 369 | )) { | ||
| 370 | ✗ | UNHANDLED(&err, "too many params/sub-params in CSI sequence"); | |
| 371 | } else { | ||
| 372 | 1817 | csi->nsub[nparams] = sub + 1; | |
| 373 | 1817 | csi->params[nparams++][sub] = num; | |
| 374 | } | ||
| 375 | } | ||
| 376 | 1834 | goto exit_loop; | |
| 377 | 12 | case BYTE_PARAMETER: | |
| 378 | // ECMA-48 §5.4.2: "bit combinations 03/12 to 03/15 are | ||
| 379 | // reserved for future standardization except when used | ||
| 380 | // as the first bit combination of the parameter string." | ||
| 381 | // (03/12 to 03/15 == '<' to '?') | ||
| 382 | 12 | UNHANDLED(&err, "unhandled CSI param byte: '%c'", ch); | |
| 383 | 12 | continue; | |
| 384 | 9 | case BYTE_CONTROL: | |
| 385 |
3/3✓ Branch 0 (27→28) taken 2 times.
✓ Branch 1 (27→29) taken 3 times.
✓ Branch 2 (27→31) taken 4 times.
|
9 | switch (ch) { |
| 386 | 2 | case 0x1B: // ESC | |
| 387 | // Don't consume ESC; it's the start of another sequence | ||
| 388 | 2 | i--; | |
| 389 | // Fallthrough | ||
| 390 | 5 | case 0x18: // CAN | |
| 391 | case 0x1A: // SUB | ||
| 392 | 5 | UNHANDLED(&err, "CSI sequence cancelled by 0x%02hhx", ch); | |
| 393 | 5 | csi->final_byte = ch; | |
| 394 | 5 | goto exit_loop; | |
| 395 | } | ||
| 396 | // Fallthrough | ||
| 397 | case BYTE_DELETE: | ||
| 398 | 8 | continue; | |
| 399 | ✗ | case BYTE_OTHER: | |
| 400 | ✗ | UNHANDLED(&err, "unhandled byte in CSI sequence: 0x%02hhx", ch); | |
| 401 | ✗ | continue; | |
| 402 | } | ||
| 403 | |||
| 404 | − | BUG("unhandled byte type"); | |
| 405 | err = true; | ||
| 406 | } | ||
| 407 | |||
| 408 | 5637 | exit_loop: | |
| 409 | 7476 | csi->nparams = nparams; | |
| 410 | 7476 | csi->nr_intermediate = nr_intermediate; | |
| 411 | 7476 | csi->have_subparams = have_subparams; | |
| 412 | 7476 | csi->unhandled_bytes = err; | |
| 413 | 7476 | return i; | |
| 414 | } | ||
| 415 | |||
| 416 | 7471 | static ssize_t parse_csi(const char *buf, size_t len, size_t i, KeyCode *k) | |
| 417 | { | ||
| 418 | 7471 | TermControlParams csi = {.nparams = 0}; | |
| 419 |
6/6✓ Branch 0 (2→3) taken 6419 times.
✓ Branch 1 (2→6) taken 1052 times.
✓ Branch 2 (3→4) taken 267 times.
✓ Branch 3 (3→6) taken 6152 times.
✓ Branch 4 (4→5) taken 253 times.
✓ Branch 5 (4→6) taken 14 times.
|
7471 | bool maybe_query_reply = (i < len && buf[i] >= '<' && buf[i] <= '?'); |
| 420 | 253 | uint8_t param_prefix = maybe_query_reply ? buf[i] : 0; | |
| 421 | 7471 | i = term_parse_csi_params(buf, len, i + (maybe_query_reply ? 1 : 0), &csi); | |
| 422 | |||
| 423 |
2/2✓ Branch 0 (7→8) taken 5637 times.
✓ Branch 1 (7→10) taken 1834 times.
|
7471 | if (unlikely(csi.final_byte == 0)) { |
| 424 | 5637 | BUG_ON(i < len); | |
| 425 | return TPARSE_PARTIAL_MATCH; | ||
| 426 | } | ||
| 427 |
2/2✓ Branch 0 (10→11) taken 17 times.
✓ Branch 1 (10→12) taken 1817 times.
|
1834 | if (unlikely(csi.unhandled_bytes)) { |
| 428 | 17 | goto ignore; | |
| 429 | } | ||
| 430 | |||
| 431 |
2/2✓ Branch 0 (12→13) taken 38 times.
✓ Branch 1 (12→15) taken 1779 times.
|
1817 | if (maybe_query_reply) { |
| 432 | 38 | *k = parse_csi_query_reply(&csi, param_prefix); | |
| 433 | 38 | return i; | |
| 434 | } | ||
| 435 | |||
| 436 |
2/2✓ Branch 0 (15→16) taken 4 times.
✓ Branch 1 (15→17) taken 1775 times.
|
1779 | if (unlikely(csi.nr_intermediate)) { |
| 437 | 4 | goto ignore; | |
| 438 | } | ||
| 439 | |||
| 440 | /* | ||
| 441 | * This handles the basic CSI u ("fixterms") encoding and also the | ||
| 442 | * extended kitty keyboard encoding. | ||
| 443 | * | ||
| 444 | * • https://www.leonerd.org.uk/hacks/fixterms/ | ||
| 445 | * • https://sw.kovidgoyal.net/kitty/keyboard-protocol/ | ||
| 446 | * • https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:formatOtherKeys | ||
| 447 | * | ||
| 448 | * kitty params: key:unshifted-key:base-layout-key ; mods:event-type ; text | ||
| 449 | */ | ||
| 450 | 1775 | KeyCode key, mods = 0; | |
| 451 |
2/2✓ Branch 0 (17→18) taken 80 times.
✓ Branch 1 (17→33) taken 1695 times.
|
1775 | if (csi.final_byte == 'u') { |
| 452 |
3/6✓ Branch 0 (18→19) taken 80 times.
✗ Branch 1 (18→21) not taken.
✓ Branch 2 (19→20) taken 80 times.
✗ Branch 3 (19→21) not taken.
✗ Branch 4 (20→21) not taken.
✓ Branch 5 (20→22) taken 80 times.
|
80 | if (unlikely(csi.nsub[0] > 3 || csi.nsub[1] > 2 || csi.nsub[2] > 1)) { |
| 453 | // Don't allow unknown sub-params | ||
| 454 | ✗ | goto ignore; | |
| 455 | } | ||
| 456 | // Use the "base layout key", if present | ||
| 457 |
2/2✓ Branch 0 (22→23) taken 79 times.
✓ Branch 1 (22→24) taken 1 times.
|
80 | key = csi.params[0][csi.nsub[0] == 3 ? 2 : 0]; |
| 458 |
2/3✓ Branch 0 (24→25) taken 22 times.
✓ Branch 1 (24→29) taken 58 times.
✗ Branch 2 (24→32) not taken.
|
80 | switch (csi.nparams) { |
| 459 | 22 | case 3: | |
| 460 | case 2: | ||
| 461 |
2/2✓ Branch 0 (25→26) taken 1 times.
✓ Branch 1 (25→27) taken 21 times.
|
22 | if (unlikely(csi.params[1][1] > 2)) { |
| 462 | // Key release event | ||
| 463 | 1 | goto ignore; | |
| 464 | } | ||
| 465 | 21 | mods = decode_modifiers(csi.params[1][0]); | |
| 466 |
2/2✓ Branch 0 (27→28) taken 1 times.
✓ Branch 1 (27→29) taken 20 times.
|
21 | if (unlikely(mods == KEY_IGNORE)) { |
| 467 | 1 | goto ignore; | |
| 468 | } | ||
| 469 | // Fallthrough | ||
| 470 | case 1: | ||
| 471 | 78 | key = normalize_csi_u_keycode(mods, key); | |
| 472 |
2/2✓ Branch 0 (29→30) taken 13 times.
✓ Branch 1 (29→31) taken 65 times.
|
78 | if (unlikely(key == KEY_IGNORE)) { |
| 473 | 13 | goto ignore; | |
| 474 | } | ||
| 475 | 65 | *k = mods | key; | |
| 476 | 65 | return i; | |
| 477 | } | ||
| 478 | ✗ | goto ignore; | |
| 479 | } | ||
| 480 | |||
| 481 |
1/2✗ Branch 0 (33→34) not taken.
✓ Branch 1 (33→35) taken 1695 times.
|
1695 | if (unlikely(csi.have_subparams)) { |
| 482 | ✗ | goto ignore; | |
| 483 | } | ||
| 484 | |||
| 485 |
3/4✓ Branch 0 (35→36) taken 1157 times.
✗ Branch 1 (35→51) not taken.
✓ Branch 2 (35→53) taken 2 times.
✓ Branch 3 (35→56) taken 536 times.
|
1695 | switch (csi.final_byte) { |
| 486 | 1157 | case '~': | |
| 487 |
3/4✓ Branch 0 (36→37) taken 18 times.
✓ Branch 1 (36→42) taken 1107 times.
✓ Branch 2 (36→44) taken 32 times.
✗ Branch 3 (36→50) not taken.
|
1157 | switch (csi.nparams) { |
| 488 | 18 | case 3: | |
| 489 |
1/2✗ Branch 0 (37→38) not taken.
✓ Branch 1 (37→39) taken 18 times.
|
18 | if (unlikely(csi.params[0][0] != 27)) { |
| 490 | ✗ | goto ignore; | |
| 491 | } | ||
| 492 | 18 | mods = decode_modifiers(csi.params[1][0]); | |
| 493 |
1/2✗ Branch 0 (39→40) not taken.
✓ Branch 1 (39→41) taken 18 times.
|
18 | if (unlikely(mods == KEY_IGNORE)) { |
| 494 | ✗ | goto ignore; | |
| 495 | } | ||
| 496 | // xterm-style modifyOtherKeys encoding | ||
| 497 | 18 | key = csi.params[2][0]; | |
| 498 | 18 | *k = normalize_csi_27_tilde_keycode(mods, key); | |
| 499 | 18 | return i; | |
| 500 | 1107 | case 2: | |
| 501 | 1107 | mods = decode_modifiers(csi.params[1][0]); | |
| 502 |
2/2✓ Branch 0 (42→43) taken 144 times.
✓ Branch 1 (42→44) taken 963 times.
|
1107 | if (unlikely(mods == KEY_IGNORE)) { |
| 503 | 144 | goto ignore; | |
| 504 | } | ||
| 505 | // Fallthrough | ||
| 506 | case 1: | ||
| 507 | 995 | key = decode_key_from_param(csi.params[0][0]); | |
| 508 |
2/2✓ Branch 0 (44→45) taken 6 times.
✓ Branch 1 (44→49) taken 989 times.
|
995 | if (key == KEY_IGNORE) { |
| 509 |
1/4✗ Branch 0 (45→46) not taken.
✓ Branch 1 (45→48) taken 6 times.
✗ Branch 2 (46→47) not taken.
✗ Branch 3 (46→48) not taken.
|
6 | if (csi.params[0][0] == 200 && mods == 0) { |
| 510 | ✗ | *k = KEYCODE_BRACKETED_PASTE; | |
| 511 | ✗ | return i; | |
| 512 | } | ||
| 513 | 6 | goto ignore; | |
| 514 | } | ||
| 515 | 989 | *k = mods | key; | |
| 516 | 989 | return i; | |
| 517 | } | ||
| 518 | ✗ | goto ignore; | |
| 519 | ✗ | case 't': | |
| 520 | ✗ | *k = parse_xtwinops_query_reply(&csi); | |
| 521 | ✗ | return i; | |
| 522 | 2 | case 'Z': | |
| 523 |
2/2✓ Branch 0 (53→54) taken 1 times.
✓ Branch 1 (53→55) taken 1 times.
|
2 | if (unlikely(csi.nparams != 0)) { |
| 524 | 1 | goto ignore; | |
| 525 | } | ||
| 526 | 1 | *k = MOD_SHIFT | KEY_TAB; | |
| 527 | 1 | return i; | |
| 528 | } | ||
| 529 | |||
| 530 |
3/3✓ Branch 0 (56→57) taken 520 times.
✓ Branch 1 (56→60) taken 13 times.
✓ Branch 2 (56→63) taken 3 times.
|
536 | switch (csi.nparams) { |
| 531 | 520 | case 2: | |
| 532 | 520 | mods = decode_modifiers(csi.params[1][0]); | |
| 533 |
3/4✓ Branch 0 (57→58) taken 454 times.
✓ Branch 1 (57→59) taken 66 times.
✗ Branch 2 (58→59) not taken.
✓ Branch 3 (58→60) taken 454 times.
|
520 | if (unlikely(mods == KEY_IGNORE || csi.params[0][0] != 1)) { |
| 534 | 66 | goto ignore; | |
| 535 | } | ||
| 536 | // Fallthrough | ||
| 537 | case 0: | ||
| 538 | 467 | key = decode_key_from_final_byte(csi.final_byte); | |
| 539 |
2/2✓ Branch 0 (60→61) taken 1 times.
✓ Branch 1 (60→62) taken 466 times.
|
467 | if (unlikely(key == KEY_IGNORE)) { |
| 540 | 1 | goto ignore; | |
| 541 | } | ||
| 542 | 466 | *k = mods | key; | |
| 543 | 466 | return i; | |
| 544 | } | ||
| 545 | |||
| 546 | 257 | ignore: | |
| 547 | 257 | *k = KEY_IGNORE; | |
| 548 | 257 | return i; | |
| 549 | } | ||
| 550 | |||
| 551 | 29 | static ssize_t parse_osc(const char *buf, size_t len, size_t i, KeyCode *k) | |
| 552 | { | ||
| 553 | 29 | char data[4096]; | |
| 554 |
2/2✓ Branch 0 (13→3) taken 87 times.
✓ Branch 1 (13→14) taken 25 times.
|
112 | for (size_t pos = 0; i < len; ) { |
| 555 | 87 | unsigned char ch = buf[i++]; | |
| 556 |
2/2✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→10) taken 83 times.
|
87 | if (unlikely(ch < 0x20)) { |
| 557 |
2/4✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 2 times.
✓ Branch 2 (4→7) taken 2 times.
✗ Branch 3 (4→9) not taken.
|
4 | switch (ch) { |
| 558 | ✗ | case 0x18: // CAN | |
| 559 | case 0x1A: // SUB | ||
| 560 | ✗ | *k = KEY_IGNORE; | |
| 561 | ✗ | return i; | |
| 562 | 2 | case 0x1B: // ESC (https://vt100.net/emu/dec_ansi_parser#STESC) | |
| 563 | 2 | i--; | |
| 564 | // Fallthrough | ||
| 565 | 4 | case 0x07: // BEL | |
| 566 | 4 | *k = parse_osc_query_reply(data, pos, pos >= sizeof(data)); | |
| 567 | 4 | return i; | |
| 568 | } | ||
| 569 | ✗ | continue; | |
| 570 | } | ||
| 571 | // Collect 0x20..0xFF (UTF-8 allowed) | ||
| 572 |
1/2✓ Branch 0 (10→11) taken 83 times.
✗ Branch 1 (10→12) not taken.
|
83 | if (likely(pos < sizeof(data))) { |
| 573 | 83 | data[pos++] = ch; | |
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | // Unterminated sequence (possibly truncated) | ||
| 578 | return TPARSE_PARTIAL_MATCH; | ||
| 579 | } | ||
| 580 | |||
| 581 | 200 | static ssize_t parse_dcs(const char *buf, size_t len, size_t i, KeyCode *k) | |
| 582 | { | ||
| 583 | 200 | char data[4096]; | |
| 584 |
2/2✓ Branch 0 (12→3) taken 1653 times.
✓ Branch 1 (12→13) taken 185 times.
|
1838 | for (size_t pos = 0; i < len; ) { |
| 585 | 1653 | unsigned char ch = buf[i++]; | |
| 586 |
2/2✓ Branch 0 (3→4) taken 15 times.
✓ Branch 1 (3→9) taken 1638 times.
|
1653 | if (unlikely(ch < 0x20 || ch == 0x7F)) { |
| 587 |
1/3✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 15 times.
✗ Branch 2 (4→8) not taken.
|
15 | switch (ch) { |
| 588 | ✗ | case 0x18: // CAN | |
| 589 | case 0x1A: // SUB | ||
| 590 | ✗ | *k = KEY_IGNORE; | |
| 591 | ✗ | return i; | |
| 592 | 15 | case 0x1B: // ESC (https://vt100.net/emu/dec_ansi_parser#STESC) | |
| 593 | 15 | *k = parse_dcs_query_reply(data, pos, pos >= sizeof(data)); | |
| 594 | 15 | return i - 1; | |
| 595 | } | ||
| 596 | ✗ | continue; | |
| 597 | } | ||
| 598 | // Collect 0x20..0xFF (excluding 0x7F) | ||
| 599 |
1/2✓ Branch 0 (9→10) taken 1638 times.
✗ Branch 1 (9→11) not taken.
|
1638 | if (likely(pos < sizeof(data))) { |
| 600 | 1638 | data[pos++] = ch; | |
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | // Unterminated sequence (possibly truncated) | ||
| 605 | return TPARSE_PARTIAL_MATCH; | ||
| 606 | } | ||
| 607 | |||
| 608 | /* | ||
| 609 | * Some terminals emit output resembling CSI/SS3 sequences without properly | ||
| 610 | * conforming to ECMA-48 §5.4, so we use an approach that accommodates | ||
| 611 | * terminal-specific special cases. This more or less precludes the use of | ||
| 612 | * a state machine (like e.g. the "dec_ansi_parser" mentioned at the top of | ||
| 613 | * this file). There's no real standard for "terminal to host" communications, | ||
| 614 | * although conforming to ECMA-48 §5.4 has been a de facto standard since the | ||
| 615 | * DEC VTs, with only a few (unnecessary and most likely not deliberate) | ||
| 616 | * exceptions in some emulators. | ||
| 617 | * | ||
| 618 | * See also: rxvt.c and linux.c | ||
| 619 | */ | ||
| 620 | 8935 | ssize_t term_parse_sequence(const char *buf, size_t length, KeyCode *k) | |
| 621 | { | ||
| 622 |
4/4✓ Branch 0 (2→3) taken 8934 times.
✓ Branch 1 (2→11) taken 1 times.
✓ Branch 2 (3→4) taken 8933 times.
✓ Branch 3 (3→11) taken 1 times.
|
8935 | if (unlikely(length == 0 || buf[0] != '\033')) { |
| 623 | return 0; | ||
| 624 |
2/2✓ Branch 0 (4→5) taken 7775 times.
✓ Branch 1 (4→11) taken 1158 times.
|
8933 | } else if (unlikely(length == 1)) { |
| 625 | return TPARSE_PARTIAL_MATCH; | ||
| 626 | } | ||
| 627 | |||
| 628 |
6/6✓ Branch 0 (5→6) taken 73 times.
✓ Branch 1 (5→7) taken 200 times.
✓ Branch 2 (5→8) taken 7471 times.
✓ Branch 3 (5→9) taken 29 times.
✓ Branch 4 (5→10) taken 1 times.
✓ Branch 5 (5→11) taken 1 times.
|
7775 | switch (buf[1]) { |
| 629 | 73 | case 'O': return parse_ss3(buf, length, 2, k); | |
| 630 | 200 | case 'P': return parse_dcs(buf, length, 2, k); | |
| 631 | 7471 | case '[': return parse_csi(buf, length, 2, k); | |
| 632 | 29 | case ']': return parse_osc(buf, length, 2, k); | |
| 633 | // String Terminator (https://vt100.net/emu/dec_ansi_parser#STESC) | ||
| 634 | 1 | case '\\': *k = KEY_IGNORE; return 2; | |
| 635 | } | ||
| 636 | |||
| 637 | return 0; | ||
| 638 | } | ||
| 639 |