• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 7171266103

11 Dec 2023 05:41PM UTC coverage: 74.638% (+0.1%) from 74.499%
7171266103

push

github

Arusekk
term: hotfix

4564 of 7217 branches covered (0.0%)

12834 of 17195 relevant lines covered (74.64%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.19
/pwnlib/term/key.py
1
from __future__ import absolute_import
1✔
2
from __future__ import division
1✔
3

4
import errno
1✔
5
import os
1✔
6
import select
1✔
7
import six
1✔
8
import string
1✔
9
import sys
1✔
10

11
from pwnlib.term import keyconsts as kc
1✔
12
from pwnlib.term import termcap
1✔
13
from pwnlib.term import term
1✔
14

15
__all__ = ['getch', 'getraw', 'get', 'unget']
1✔
16

17
# When set, convert keypad codes into regular key presses, e.g. "+" instead of
18
# `<kp plus>`
19
FLAG_CONVERTKP = True
1✔
20

21
try:    _fd = sys.stdin.fileno()
1✔
22
except Exception: _fd = os.open(os.devnull, os.O_RDONLY)
×
23

24
def getch(timeout = 0):
1✔
25
    term.setupterm()
1✔
26
    while True:
1✔
27
        try:
1✔
28
            rfds, _wfds, _xfds = select.select([_fd], [], [], timeout)
1✔
29
            if rfds:
1✔
30
                with term.rlock:
1!
31
                    rfds, _wfds, _xfds = select.select([_fd], [], [], 0)
1✔
32
                    if not rfds: continue
1!
33
                    c = os.read(_fd, 1)
1✔
34
                return ord(c) if c else None
1✔
35
            else:
36
                return None
1✔
37
        except select.error as e:
×
38
            if e.args[0] == errno.EINTR:
×
39
                continue
×
40
            raise
×
41

42
def getraw(timeout = None):
1✔
43
    '''Get list of raw key codes corresponding to zero or more key presses'''
44
    cs = []
1✔
45
    c = getch(timeout)
1✔
46
    while c is not None: # timeout
1✔
47
        cs.append(c)
1✔
48
        if c is None: # EOF
1!
49
            break
×
50
        c = getch()
1✔
51
    return cs
1✔
52

53
class Matcher:
1✔
54
    def __init__(self, desc):
1✔
55
        self._desc = desc
1✔
56
        desc = desc.split('-')
1✔
57
        mods = desc[:-1]
1✔
58
        k = desc[-1]
1✔
59
        if k == '<space>':
1!
60
            k = ' '
×
61
        m = kc.MOD_NONE
1✔
62
        if 'S' in mods:
1!
63
            m |= kc.MOD_SHIFT
×
64
        if 'M' in mods:
1✔
65
            m |= kc.MOD_ALT
1✔
66
        if 'C' in mods:
1✔
67
            m |= kc.MOD_CTRL
1✔
68
        if   len(k) == 1:
1✔
69
            t = kc.TYPE_UNICODE
1✔
70
            c = k
1✔
71
            h = ord(k)
1✔
72
        elif k[0] == '<' and k in kc.KEY_NAMES_REVERSE:
1!
73
            t = kc.TYPE_KEYSYM
1✔
74
            c = kc.KEY_NAMES_REVERSE[k]
1✔
75
            h = c
1✔
76
        elif k[:2] == '<f' and k[-1] == '>' and k[2:-1].isdigit():
×
77
            t = kc.TYPE_FUNCTION
×
78
            c = int(k[2:-1])
×
79
            h = c
×
80
        else:
81
            raise ValueError('bad key description "%s"' % k)
×
82
        self._type = t
1✔
83
        self._code = c
1✔
84
        self._mods = m
1✔
85
        self._hash = h | (m << 6) | (t << 7)
1✔
86

87
    def __call__(self, k):
1✔
88
        if isinstance(k, Key):
1!
89
            return all([k.type == self._type,
1✔
90
                        k.code == self._code,
91
                        k.mods == self._mods,
92
                        ])
93

94
    def __eq__(self, other):
1✔
95
        if   isinstance(other, Matcher):
1!
96
            return all([other._type == self._type,
1✔
97
                        other._code == self._code,
98
                        other._mods == self._mods,
99
                        ])
100
        elif isinstance(other, Key):
×
101
            return self.__call__(other)
×
102
        else:
103
            return False
×
104

105
    def __neq__(self, other):
1✔
106
        return not self == other
×
107

108
    def __hash__(self):
1✔
109
        return self._hash
1✔
110

111
    def __str__(self):
1✔
112
        return self._desc
×
113

114
class Key:
1✔
115
    def __init__(self, type, code = None, mods = kc.MOD_NONE):
1✔
116
        self.type = type
1✔
117
        self.code = code
1✔
118
        self.mods = mods
1✔
119
        self._str = None
1✔
120

121
    def __str__(self):
1✔
122
        if self._str:
1!
123
            return self._str
×
124
        if   self.type == kc.TYPE_UNICODE:
1!
125
            if self.code == ' ':
1!
126
                s = '<space>'
×
127
            else:
128
                s = self.code
1✔
129
        elif self.type == kc.TYPE_KEYSYM:
×
130
            s = kc.KEY_NAMES.get(self.code, '<SYMNAME-%d>' % self.code)
×
131
        elif self.type == kc.TYPE_FUNCTION:
×
132
            s = '<f%d>' % self.code
×
133
        elif self.type == kc.TYPE_POSITION:
×
134
            s = 'Position(%d, %d)' % self.code
×
135
        elif self.type == kc.TYPE_EOF:
×
136
            s = 'EOF'
×
137
        else:
138
            s = '<UNKNOWN>'
×
139
        if self.mods & kc.MOD_SHIFT:
1!
140
            s = 'S-' + s
×
141
        if self.mods & kc.MOD_ALT:
1!
142
            s = 'M-' + s
×
143
        if self.mods & kc.MOD_CTRL:
1!
144
            s = 'C-' + s
×
145
        self._str = s
1✔
146
        return s
1✔
147

148
    def __repr__(self):
1✔
149
        return self.__str__()
×
150

151
    def __eq__(self, other):
1✔
152
        if   isinstance(other, (six.text_type, six.binary_type)):
1!
153
            return Matcher(other)(self)
1✔
154
        elif isinstance(other, Matcher):
×
155
            return other(self)
×
156
        elif isinstance(other, Key):
×
157
            return all([self.type == other.type,
×
158
                        self.code == other.code,
159
                        self.mods == other.mods,
160
                        ])
161
        else:
162
            return False
×
163

164
_cbuf = []
1✔
165
_kbuf = []
1✔
166

167
def _read(timeout = 0):
1✔
168
    _cbuf.extend(getraw(timeout))
1✔
169

170
def _peek():
1✔
171
    if _cbuf:
1✔
172
        return _peek_ti() or _peek_csi() or _peek_simple()
1✔
173

174
def get(timeout = None):
1✔
175
    if _kbuf:
1!
176
        return _kbuf.pop(0)
×
177
    k = _peek()
1✔
178
    if k:
1✔
179
        return k
1✔
180
    _read(timeout)
1✔
181
    return _peek()
1✔
182

183
def unget(k):
1✔
184
    _kbuf.append(k)
×
185

186
# terminfo
187
def _name_to_key(fname):
1✔
188
    if   fname in kc.FUNCSYMS:
1✔
189
        k = Key(kc.TYPE_KEYSYM, *kc.FUNCSYMS[fname])
1✔
190
    elif fname[0] == 'f' and fname[1:].isdigit():
1✔
191
        k = Key(kc.TYPE_FUNCTION, int(fname[1:]))
1✔
192
    elif fname[0] == 's':
1✔
193
        k = _name_to_key(fname[1:])
1✔
194
        if k:
1✔
195
            k.mods |= kc.MOD_SHIFT
1✔
196
    else:
197
        return None
1✔
198
    return k
1✔
199

200
_ti_table = None
1✔
201

202
def _peek_ti():
1✔
203
    global _cbuf
204
    if _ti_table is None:
1✔
205
        _init_ti_table()
1✔
206
    # XXX: Faster lookup, plox
207
    for seq, key in _ti_table:
1✔
208
        if _cbuf[:len(seq)] == seq:
1!
209
            _cbuf = _cbuf[len(seq):]
×
210
            return key
×
211

212
def _init_ti_table():
1✔
213
    global _ti_table
214
    _ti_table = []
1✔
215
    for fname, name in zip(kc.STRFNAMES, kc.STRNAMES):
1✔
216
        seq = termcap.get(name)
1✔
217
        if not seq:
1✔
218
            continue
1✔
219
        k = _name_to_key(fname)
1✔
220
        if k:
1✔
221
            _ti_table.append((list(bytearray(seq)), k))
1✔
222

223
# csi
224
def _parse_csi(offset):
1✔
225
    i = offset
1✔
226
    while i < len(_cbuf):
1!
227
        c = _cbuf[i]
1✔
228
        if c >= 0x40 and c < 0x80:
1✔
229
            break
1✔
230
        i += 1
1✔
231
    if i >= len(_cbuf):
1!
232
        return
×
233
    end = i
1✔
234
    cmd = [c, None, None]
1✔
235

236
    i = offset
1✔
237
    in_num = False
1✔
238
    args = []
1✔
239
    if _cbuf[i] >= ord('<') and _cbuf[i] <= ord('?'):
1!
240
        cmd[1] = _cbuf[i]
×
241
        i += 1
×
242
    while i < end:
1✔
243
        c = _cbuf[i]
1✔
244
        if   c >= ord('0') and c <= ord('9'):
1✔
245
            if not in_num:
1!
246
                args.append(c - ord('0'))
1✔
247
                in_num = True
1✔
248
            else:
249
                args[-1] = args[-1] * 10 + c - ord('0')
×
250
        elif c == ord(';'):
1!
251
            if not in_num:
1!
252
                args.append(None)
×
253
            in_num = False
1✔
254
            if len(args) > 16:
1!
255
                break
×
256
        elif c >= 0x20 and c <= 0x2f:
×
257
            cmd[2] = c
×
258
            break
×
259

260
        i += 1
1✔
261

262
    return cmd, args, end + 1
1✔
263

264
def _csi_func(cmd, args):
1✔
265
    k = Key(kc.TYPE_UNKNOWN)
×
266
    if len(args) > 1 and args[1]:
×
267
        k.mods |= args[1] - 1
×
268

269
    if   args[0] == 0x1b and len(args) == 3:
×
270
        k.type = kc.TYPE_KEYSYM
×
271
        k.code = args[2]
×
272
        return k
×
273
    elif args[0] in _csi_funcs:
×
274
        f = _csi_funcs[args[0]]
×
275
        k.type = f[0]
×
276
        k.code = f[1]
×
277
        return k
×
278

279
def _csi_ss3(cmd, args):
1✔
280
    t, c = _csi_ss3s[chr(cmd[0])]
1✔
281
    k = Key(t, c)
1✔
282
    if len(args) > 1 and args[1]:
1✔
283
        k.mods |= args[1] - 1
1✔
284
    return k
1✔
285

286
def _csi_u(cmd, args):
1✔
287
    k = Key(kc.TYPE_UNICODE, six.unichr(args[0]))
×
288
    if len(args) > 1 and args[1]:
×
289
        k.mods |= args[1] - 1
×
290
    return k
×
291

292
def _csi_R(cmd, args):
1✔
293
    if cmd[0] == ord('R') and cmd[1] == ord('?'):
×
294
        if len(args) < 2:
×
295
            return
×
296
        return Key(kc.TYPE_POSITION, (args[1], args[0]))
×
297
    else:
298
        return _csi_ss3(cmd, args)
×
299

300
_csi_handlers = {
1✔
301
    '~' : _csi_func,
302
    'u' : _csi_u,
303
    'R' : _csi_R,
304
    }
305

306
_csi_ss3s = {
1✔
307
    'A': (kc.TYPE_KEYSYM, kc.KEY_UP),
308
    'B': (kc.TYPE_KEYSYM, kc.KEY_DOWN),
309
    'C': (kc.TYPE_KEYSYM, kc.KEY_RIGHT),
310
    'D': (kc.TYPE_KEYSYM, kc.KEY_LEFT),
311
    'E': (kc.TYPE_KEYSYM, kc.KEY_BEGIN),
312
    'F': (kc.TYPE_KEYSYM, kc.KEY_END),
313
    'H': (kc.TYPE_KEYSYM, kc.KEY_HOME),
314
    'P': (kc.TYPE_FUNCTION, 1),
315
    'Q': (kc.TYPE_FUNCTION, 2),
316
    'R': (kc.TYPE_FUNCTION, 3),
317
    'S': (kc.TYPE_FUNCTION, 4),
318
    'Z': (kc.TYPE_KEYSYM, kc.KEY_TAB),
319
}
320

321
_csi_ss3kp = {
1✔
322
    'M': (kc.TYPE_KEYSYM, kc.KEY_KPENTER , None),
323
    'X': (kc.TYPE_KEYSYM, kc.KEY_KPEQUALS, '='),
324
    'j': (kc.TYPE_KEYSYM, kc.KEY_KPMULT  , '*'),
325
    'k': (kc.TYPE_KEYSYM, kc.KEY_KPPLUS  , '+'),
326
    'l': (kc.TYPE_KEYSYM, kc.KEY_KPCOMMA , ','),
327
    'm': (kc.TYPE_KEYSYM, kc.KEY_KPMINUS , '-'),
328
    'n': (kc.TYPE_KEYSYM, kc.KEY_KPPERIOD, '.'),
329
    'o': (kc.TYPE_KEYSYM, kc.KEY_KPDIV   , '/'),
330
    'p': (kc.TYPE_KEYSYM, kc.KEY_KP0     , '0'),
331
    'q': (kc.TYPE_KEYSYM, kc.KEY_KP1     , '1'),
332
    'r': (kc.TYPE_KEYSYM, kc.KEY_KP2     , '2'),
333
    's': (kc.TYPE_KEYSYM, kc.KEY_KP3     , '3'),
334
    't': (kc.TYPE_KEYSYM, kc.KEY_KP4     , '4'),
335
    'u': (kc.TYPE_KEYSYM, kc.KEY_KP5     , '5'),
336
    'v': (kc.TYPE_KEYSYM, kc.KEY_KP6     , '6'),
337
    'w': (kc.TYPE_KEYSYM, kc.KEY_KP7     , '7'),
338
    'x': (kc.TYPE_KEYSYM, kc.KEY_KP8     , '8'),
339
    'y': (kc.TYPE_KEYSYM, kc.KEY_KP9     , '9'),
340
}
341

342
_csi_funcs = {
1✔
343
    1 : (kc.TYPE_KEYSYM, kc.KEY_FIND),
344
    2 : (kc.TYPE_KEYSYM, kc.KEY_INSERT),
345
    3 : (kc.TYPE_KEYSYM, kc.KEY_DELETE),
346
    4 : (kc.TYPE_KEYSYM, kc.KEY_SELECT),
347
    5 : (kc.TYPE_KEYSYM, kc.KEY_PAGEUP),
348
    6 : (kc.TYPE_KEYSYM, kc.KEY_PAGEDOWN),
349
    7 : (kc.TYPE_KEYSYM, kc.KEY_HOME),
350
    8 : (kc.TYPE_KEYSYM, kc.KEY_END),
351
    11: (kc.TYPE_FUNCTION, 1),
352
    12: (kc.TYPE_FUNCTION, 2),
353
    13: (kc.TYPE_FUNCTION, 3),
354
    14: (kc.TYPE_FUNCTION, 4),
355
    15: (kc.TYPE_FUNCTION, 5),
356
    17: (kc.TYPE_FUNCTION, 6),
357
    18: (kc.TYPE_FUNCTION, 7),
358
    19: (kc.TYPE_FUNCTION, 8),
359
    20: (kc.TYPE_FUNCTION, 9),
360
    21: (kc.TYPE_FUNCTION, 10),
361
    23: (kc.TYPE_FUNCTION, 11),
362
    24: (kc.TYPE_FUNCTION, 12),
363
    25: (kc.TYPE_FUNCTION, 13),
364
    26: (kc.TYPE_FUNCTION, 14),
365
    28: (kc.TYPE_FUNCTION, 15),
366
    29: (kc.TYPE_FUNCTION, 16),
367
    31: (kc.TYPE_FUNCTION, 17),
368
    32: (kc.TYPE_FUNCTION, 18),
369
    33: (kc.TYPE_FUNCTION, 19),
370
    34: (kc.TYPE_FUNCTION, 20),
371
    }
372

373
def _peekkey_csi(offset):
1✔
374
    global _cbuf
375
    ret = _parse_csi(offset)
1✔
376
    if not ret:
1!
377
        _cbuf = _cbuf[offset:]
×
378
        return Key(kc.TYPE_UNICODE, u'[', kc.MOD_ALT)
×
379
    cmd, args, numb = ret
1✔
380
    # print(cmd, args, '\r')
381
    _cbuf = _cbuf[numb:]
1✔
382
    k = None
1✔
383
    if   chr(cmd[0]) in _csi_handlers:
1!
384
        k = _csi_handlers[chr(cmd[0])](cmd, args)
×
385
    elif chr(cmd[0]) in _csi_ss3s:
1!
386
        k = _csi_ss3(cmd, args)
1✔
387
        if k and chr(cmd[0]) == 'Z':
1!
388
            k.mods |= kc.MOD_SHIFT
×
389

390
    if k:
1!
391
        return k
1✔
392
    else:
393
        return Key(kc.TYPE_UNKNOWN_CSI, (cmd, args))
×
394

395
def _peekkey_ss3(offset):
1✔
396
    global _cbuf
397
    if len(_cbuf) <= offset:
×
398
        return Key(kc.TYPE_UNICODE, u'O', kc.MOD_ALT)
×
399
    cmd = _cbuf[offset]
×
400
    if cmd < 0x40 or cmd >= 0x80:
×
401
        return
×
402
    _cbuf = _cbuf[offset:]
×
403

404
    if chr(cmd) in _csi_ss3s:
×
405
        return Key(*_csi_ss3s[chr(cmd)])
×
406

407
    if chr(cmd) in _csi_ss3kp:
×
408
        t, c, a = _csi_ss3kp[chr(cmd)]
×
409
        if FLAG_CONVERTKP and a:
×
410
            return Key(kc.TYPE_UNICODE, a)
×
411
        else:
412
            return Key(t, c)
×
413

414
def _peek_csi():
1✔
415
    global _cbuf
416
    # print('csi', _cbuf, '\r')
417
    c0 = _cbuf[0]
1✔
418
    if   c0 == 0x1b and len(_cbuf) >= 2:
1✔
419
        c1 = _cbuf[1]
1✔
420
        if   c1 == ord('['):
1!
421
            return _peekkey_csi(2)
1✔
422
        elif c1 == ord('O'):
×
423
            return _peekkey_ss3(2)
×
424
    elif c0 == 0x8f:
1!
425
        return _peekkey_ss3(1)
×
426
    elif c0 == 0x9b:
1!
427
        return _peekkey_csi(1)
×
428

429
def _peek_simple():
1✔
430
    global _cbuf
431
    # print('simple', _cbuf, '\r')
432
    if not _cbuf:
1!
433
        return
×
434
    c0 = _cbuf.pop(0)
1✔
435
    if   c0 is None:
1!
436
        _cbuf = []
×
437
        return Key(kc.TYPE_EOF)
×
438
    elif c0 == 0x1b:
1!
439
        if _cbuf:
×
440
            k = _peek()
×
441
            # print(k)
442
            if k:
×
443
                # need to deep copy or we risk modifying keys in ti table
444
                return Key(k.type, k.code, k.mods | kc.MOD_ALT)
×
445
        else:
446
            return Key(kc.TYPE_KEYSYM, kc.KEY_ESCAPE)
×
447
    elif c0 < 0xa0:
1!
448
        if   c0 < 0x20:
1✔
449
            if   c0 == 8:
1!
450
                k = Key(kc.TYPE_KEYSYM, kc.KEY_BACKSPACE)
×
451
            elif c0 == 9:
1!
452
                k = Key(kc.TYPE_KEYSYM, kc.KEY_TAB)
×
453
            elif c0 in (10, 13):
1!
454
                k = Key(kc.TYPE_KEYSYM, kc.KEY_ENTER)
1✔
455
            else:
456
                k = Key(kc.TYPE_UNICODE)
×
457
                if   c0 == 0:
×
458
                    k.code = u' '
×
459
                elif chr(c0 + 0x40) in string.ascii_uppercase:
×
460
                    k.code = six.unichr(c0 + 0x60)
×
461
                else:
462
                    k.code = six.unichr(c0 + 0x40)
×
463
                k.mods |= kc.MOD_CTRL
×
464
        elif c0 == 0x7f:
1!
465
            # print('del\r')
466
            k = Key(kc.TYPE_KEYSYM, kc.KEY_DEL)
×
467
        elif c0 >= 0x20 and c0 < 0x80:
1!
468
            k = Key(kc.TYPE_UNICODE, six.unichr(c0))
1✔
469
        else:
470
            k = Key(kc.TYPE_UNICODE, six.unichr(c0 - 0x40), kc.MOD_CTRL | kc.MOD_ALT)
×
471
    else: # utf8
472
        n = 0
×
473
        if   c0 & 0b11100000 == 0b11000000:
×
474
            n = 2
×
475
        elif c0 & 0b11110000 == 0b11100000:
×
476
            n = 3
×
477
        elif c0 & 0b11111000 == 0b11110000:
×
478
            n = 4
×
479
        elif c0 & 0b11111100 == 0b11111000:
×
480
            n = 5
×
481
        elif c0 & 0b11111110 == 0b11111100:
×
482
            n = 6
×
483
        if n:
×
484
            c = [c0] + _cbuf[:n - 1]
×
485
            k = Key(kc.TYPE_UNICODE, bytearray(c).decode('utf8'))
×
486
            _cbuf = _cbuf[n - 1:]
×
487
        else:
488
            k = Key(kc.TYPE_UNKNOWN, _cbuf)
×
489
            _cbuf = []
×
490
    return k
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc