Coverage Report

Created: 2025-07-11 06:20

/src/tmux/key-string.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <wchar.h>
25
26
#include "tmux.h"
27
28
static key_code key_string_search_table(const char *);
29
static key_code key_string_get_modifiers(const char **);
30
31
static const struct {
32
  const char     *string;
33
  key_code  key;
34
} key_string_table[] = {
35
  /* Function keys. */
36
  { "F1",   KEYC_F1|KEYC_IMPLIED_META },
37
  { "F2",   KEYC_F2|KEYC_IMPLIED_META },
38
  { "F3",   KEYC_F3|KEYC_IMPLIED_META },
39
  { "F4",   KEYC_F4|KEYC_IMPLIED_META },
40
  { "F5",   KEYC_F5|KEYC_IMPLIED_META },
41
  { "F6",   KEYC_F6|KEYC_IMPLIED_META },
42
  { "F7",   KEYC_F7|KEYC_IMPLIED_META },
43
  { "F8",   KEYC_F8|KEYC_IMPLIED_META },
44
  { "F9",   KEYC_F9|KEYC_IMPLIED_META },
45
  { "F10",  KEYC_F10|KEYC_IMPLIED_META },
46
  { "F11",  KEYC_F11|KEYC_IMPLIED_META },
47
  { "F12",  KEYC_F12|KEYC_IMPLIED_META },
48
  { "IC",   KEYC_IC|KEYC_IMPLIED_META },
49
  { "Insert", KEYC_IC|KEYC_IMPLIED_META },
50
  { "DC",   KEYC_DC|KEYC_IMPLIED_META },
51
  { "Delete", KEYC_DC|KEYC_IMPLIED_META },
52
  { "Home", KEYC_HOME|KEYC_IMPLIED_META },
53
  { "End",  KEYC_END|KEYC_IMPLIED_META },
54
  { "NPage",  KEYC_NPAGE|KEYC_IMPLIED_META },
55
  { "PageDown", KEYC_NPAGE|KEYC_IMPLIED_META },
56
  { "PgDn", KEYC_NPAGE|KEYC_IMPLIED_META },
57
  { "PPage",  KEYC_PPAGE|KEYC_IMPLIED_META },
58
  { "PageUp", KEYC_PPAGE|KEYC_IMPLIED_META },
59
  { "PgUp", KEYC_PPAGE|KEYC_IMPLIED_META },
60
  { "BTab", KEYC_BTAB },
61
  { "Space",  ' ' },
62
  { "BSpace", KEYC_BSPACE },
63
64
  /*
65
   * C0 control characters, with the exception of Tab, Enter,
66
   * and Esc, should never appear as keys. We still render them,
67
   * so to be able to spot them in logs in case of an abnormality.
68
   */
69
  { "[NUL]",  C0_NUL },
70
  { "[SOH]",  C0_SOH },
71
  { "[STX]",  C0_STX },
72
  { "[ETX]",  C0_ETX },
73
  { "[EOT]",  C0_EOT },
74
  { "[ENQ]",  C0_ENQ },
75
  { "[ASC]",  C0_ASC },
76
  { "[BEL]",  C0_BEL },
77
  { "[BS]", C0_BS },
78
  { "Tab",  C0_HT },
79
  { "[LF]", C0_LF },
80
  { "[VT]", C0_VT },
81
  { "[FF]", C0_FF },
82
  { "Enter",  C0_CR },
83
  { "[SO]", C0_SO },
84
  { "[SI]", C0_SI },
85
  { "[DLE]",  C0_DLE },
86
  { "[DC1]",  C0_DC1 },
87
  { "[DC2]",  C0_DC2 },
88
  { "[DC3]",  C0_DC3 },
89
  { "[DC4]",  C0_DC4 },
90
  { "[NAK]",  C0_NAK },
91
  { "[SYN]",  C0_SYN },
92
  { "[ETB]",  C0_ETB },
93
  { "[CAN]",  C0_CAN },
94
  { "[EM]", C0_EM },
95
  { "[SUB]",  C0_SUB },
96
  { "Escape", C0_ESC },
97
  { "[FS]", C0_FS },
98
  { "[GS]", C0_GS },
99
  { "[RS]", C0_RS },
100
  { "[US]", C0_US },
101
102
  /* Arrow keys. */
103
  { "Up",   KEYC_UP|KEYC_CURSOR|KEYC_IMPLIED_META },
104
  { "Down", KEYC_DOWN|KEYC_CURSOR|KEYC_IMPLIED_META },
105
  { "Left", KEYC_LEFT|KEYC_CURSOR|KEYC_IMPLIED_META },
106
  { "Right",  KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META },
107
108
  /* Numeric keypad. */
109
  { "KP/",  KEYC_KP_SLASH|KEYC_KEYPAD },
110
  { "KP*",  KEYC_KP_STAR|KEYC_KEYPAD },
111
  { "KP-",  KEYC_KP_MINUS|KEYC_KEYPAD },
112
  { "KP7",  KEYC_KP_SEVEN|KEYC_KEYPAD },
113
  { "KP8",  KEYC_KP_EIGHT|KEYC_KEYPAD },
114
  { "KP9",  KEYC_KP_NINE|KEYC_KEYPAD },
115
  { "KP+",  KEYC_KP_PLUS|KEYC_KEYPAD },
116
  { "KP4",  KEYC_KP_FOUR|KEYC_KEYPAD },
117
  { "KP5",  KEYC_KP_FIVE|KEYC_KEYPAD },
118
  { "KP6",  KEYC_KP_SIX|KEYC_KEYPAD },
119
  { "KP1",  KEYC_KP_ONE|KEYC_KEYPAD },
120
  { "KP2",  KEYC_KP_TWO|KEYC_KEYPAD },
121
  { "KP3",  KEYC_KP_THREE|KEYC_KEYPAD },
122
  { "KPEnter",  KEYC_KP_ENTER|KEYC_KEYPAD },
123
  { "KP0",  KEYC_KP_ZERO|KEYC_KEYPAD },
124
  { "KP.",  KEYC_KP_PERIOD|KEYC_KEYPAD },
125
126
  /* Mouse keys. */
127
  KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1),
128
  KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2),
129
  KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3),
130
  KEYC_MOUSE_STRING(MOUSEDOWN6, MouseDown6),
131
  KEYC_MOUSE_STRING(MOUSEDOWN7, MouseDown7),
132
  KEYC_MOUSE_STRING(MOUSEDOWN8, MouseDown8),
133
  KEYC_MOUSE_STRING(MOUSEDOWN9, MouseDown9),
134
  KEYC_MOUSE_STRING(MOUSEDOWN10, MouseDown10),
135
  KEYC_MOUSE_STRING(MOUSEDOWN11, MouseDown11),
136
  KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1),
137
  KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2),
138
  KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3),
139
  KEYC_MOUSE_STRING(MOUSEUP6, MouseUp6),
140
  KEYC_MOUSE_STRING(MOUSEUP7, MouseUp7),
141
  KEYC_MOUSE_STRING(MOUSEUP8, MouseUp8),
142
  KEYC_MOUSE_STRING(MOUSEUP9, MouseUp9),
143
  KEYC_MOUSE_STRING(MOUSEUP10, MouseUp10),
144
  KEYC_MOUSE_STRING(MOUSEUP11, MouseUp11),
145
  KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1),
146
  KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2),
147
  KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3),
148
  KEYC_MOUSE_STRING(MOUSEDRAG6, MouseDrag6),
149
  KEYC_MOUSE_STRING(MOUSEDRAG7, MouseDrag7),
150
  KEYC_MOUSE_STRING(MOUSEDRAG8, MouseDrag8),
151
  KEYC_MOUSE_STRING(MOUSEDRAG9, MouseDrag9),
152
  KEYC_MOUSE_STRING(MOUSEDRAG10, MouseDrag10),
153
  KEYC_MOUSE_STRING(MOUSEDRAG11, MouseDrag11),
154
  KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1),
155
  KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2),
156
  KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3),
157
  KEYC_MOUSE_STRING(MOUSEDRAGEND6, MouseDragEnd6),
158
  KEYC_MOUSE_STRING(MOUSEDRAGEND7, MouseDragEnd7),
159
  KEYC_MOUSE_STRING(MOUSEDRAGEND8, MouseDragEnd8),
160
  KEYC_MOUSE_STRING(MOUSEDRAGEND9, MouseDragEnd9),
161
  KEYC_MOUSE_STRING(MOUSEDRAGEND10, MouseDragEnd10),
162
  KEYC_MOUSE_STRING(MOUSEDRAGEND11, MouseDragEnd11),
163
  KEYC_MOUSE_STRING(WHEELUP, WheelUp),
164
  KEYC_MOUSE_STRING(WHEELDOWN, WheelDown),
165
  KEYC_MOUSE_STRING(SECONDCLICK1, SecondClick1),
166
  KEYC_MOUSE_STRING(SECONDCLICK2, SecondClick2),
167
  KEYC_MOUSE_STRING(SECONDCLICK3, SecondClick3),
168
  KEYC_MOUSE_STRING(SECONDCLICK6, SecondClick6),
169
  KEYC_MOUSE_STRING(SECONDCLICK7, SecondClick7),
170
  KEYC_MOUSE_STRING(SECONDCLICK8, SecondClick8),
171
  KEYC_MOUSE_STRING(SECONDCLICK9, SecondClick9),
172
  KEYC_MOUSE_STRING(SECONDCLICK10, SecondClick10),
173
  KEYC_MOUSE_STRING(SECONDCLICK11, SecondClick11),
174
  KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1),
175
  KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2),
176
  KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3),
177
  KEYC_MOUSE_STRING(DOUBLECLICK6, DoubleClick6),
178
  KEYC_MOUSE_STRING(DOUBLECLICK7, DoubleClick7),
179
  KEYC_MOUSE_STRING(DOUBLECLICK8, DoubleClick8),
180
  KEYC_MOUSE_STRING(DOUBLECLICK9, DoubleClick9),
181
  KEYC_MOUSE_STRING(DOUBLECLICK10, DoubleClick10),
182
  KEYC_MOUSE_STRING(DOUBLECLICK11, DoubleClick11),
183
  KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1),
184
  KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2),
185
  KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3),
186
  KEYC_MOUSE_STRING(TRIPLECLICK6, TripleClick6),
187
  KEYC_MOUSE_STRING(TRIPLECLICK7, TripleClick7),
188
  KEYC_MOUSE_STRING(TRIPLECLICK8, TripleClick8),
189
  KEYC_MOUSE_STRING(TRIPLECLICK9, TripleClick9),
190
  KEYC_MOUSE_STRING(TRIPLECLICK10, TripleClick10),
191
  KEYC_MOUSE_STRING(TRIPLECLICK11, TripleClick11)
192
};
193
194
/* Find key string in table. */
195
static key_code
196
key_string_search_table(const char *string)
197
0
{
198
0
  u_int i, user;
199
200
0
  for (i = 0; i < nitems(key_string_table); i++) {
201
0
    if (strcasecmp(string, key_string_table[i].string) == 0)
202
0
      return (key_string_table[i].key);
203
0
  }
204
205
0
  if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER)
206
0
    return (KEYC_USER + user);
207
208
0
  return (KEYC_UNKNOWN);
209
0
}
210
211
/* Find modifiers. */
212
static key_code
213
key_string_get_modifiers(const char **string)
214
0
{
215
0
  key_code  modifiers;
216
217
0
  modifiers = 0;
218
0
  while (((*string)[0] != '\0') && (*string)[1] == '-') {
219
0
    switch ((*string)[0]) {
220
0
    case 'C':
221
0
    case 'c':
222
0
      modifiers |= KEYC_CTRL;
223
0
      break;
224
0
    case 'M':
225
0
    case 'm':
226
0
      modifiers |= KEYC_META;
227
0
      break;
228
0
    case 'S':
229
0
    case 's':
230
0
      modifiers |= KEYC_SHIFT;
231
0
      break;
232
0
    default:
233
0
      *string = NULL;
234
0
      return (0);
235
0
    }
236
0
    *string += 2;
237
0
  }
238
0
  return (modifiers);
239
0
}
240
241
/* Lookup a string and convert to a key value. */
242
key_code
243
key_string_lookup_string(const char *string)
244
0
{
245
0
  key_code     key, modifiers = 0;
246
0
  u_int      u, i;
247
0
  struct utf8_data   ud, *udp;
248
0
  enum utf8_state    more;
249
0
  utf8_char    uc;
250
0
  char       m[MB_LEN_MAX + 1];
251
0
  int      mlen;
252
253
  /* Is this no key or any key? */
254
0
  if (strcasecmp(string, "None") == 0)
255
0
    return (KEYC_NONE);
256
0
  if (strcasecmp(string, "Any") == 0)
257
0
    return (KEYC_ANY);
258
259
  /* Is this a hexadecimal value? */
260
0
  if (string[0] == '0' && string[1] == 'x') {
261
0
    if (sscanf(string + 2, "%x", &u) != 1)
262
0
      return (KEYC_UNKNOWN);
263
0
    if (u < 32)
264
0
      return (u);
265
0
    mlen = wctomb(m, u);
266
0
    if (mlen <= 0 || mlen > MB_LEN_MAX)
267
0
      return (KEYC_UNKNOWN);
268
0
    m[mlen] = '\0';
269
270
0
    udp = utf8_fromcstr(m);
271
0
    if (udp == NULL ||
272
0
        udp[0].size == 0 ||
273
0
        udp[1].size != 0 ||
274
0
        utf8_from_data(&udp[0], &uc) != UTF8_DONE) {
275
0
      free(udp);
276
0
      return (KEYC_UNKNOWN);
277
0
    }
278
0
    free(udp);
279
0
    return (uc);
280
0
  }
281
282
  /* Check for short Ctrl key. */
283
0
  if (string[0] == '^' && string[1] != '\0') {
284
0
    if (string[2] == '\0')
285
0
      return (tolower((u_char)string[1])|KEYC_CTRL);
286
0
    modifiers |= KEYC_CTRL;
287
0
    string++;
288
0
  }
289
290
  /* Check for modifiers. */
291
0
  modifiers |= key_string_get_modifiers(&string);
292
0
  if (string == NULL || string[0] == '\0')
293
0
    return (KEYC_UNKNOWN);
294
295
  /* Is this a standard ASCII key? */
296
0
  if (string[1] == '\0' && (u_char)string[0] <= 127) {
297
0
    key = (u_char)string[0];
298
0
    if (key < 32)
299
0
      return (KEYC_UNKNOWN);
300
0
  } else {
301
    /* Try as a UTF-8 key. */
302
0
    if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) {
303
0
      if (strlen(string) != ud.size)
304
0
        return (KEYC_UNKNOWN);
305
0
      for (i = 1; i < ud.size; i++)
306
0
        more = utf8_append(&ud, (u_char)string[i]);
307
0
      if (more != UTF8_DONE)
308
0
        return (KEYC_UNKNOWN);
309
0
      if (utf8_from_data(&ud, &uc) != UTF8_DONE)
310
0
        return (KEYC_UNKNOWN);
311
0
      return (uc|modifiers);
312
0
    }
313
314
    /* Otherwise look the key up in the table. */
315
0
    key = key_string_search_table(string);
316
0
    if (key == KEYC_UNKNOWN)
317
0
      return (KEYC_UNKNOWN);
318
0
    if (~modifiers & KEYC_META)
319
0
      key &= ~KEYC_IMPLIED_META;
320
0
  }
321
322
0
  return (key|modifiers);
323
0
}
324
325
/* Convert a key code into string format, with prefix if necessary. */
326
const char *
327
key_string_lookup_key(key_code key, int with_flags)
328
0
{
329
0
  key_code     saved = key;
330
0
  static char    out[64];
331
0
  char       tmp[8];
332
0
  const char    *s;
333
0
  u_int      i;
334
0
  struct utf8_data   ud;
335
0
  size_t       off;
336
337
0
  *out = '\0';
338
339
  /* Literal keys are themselves. */
340
0
  if (key & KEYC_LITERAL) {
341
0
    snprintf(out, sizeof out, "%c", (int)(key & 0xff));
342
0
    goto out;
343
0
  }
344
345
  /* Fill in the modifiers. */
346
0
  if (key & KEYC_CTRL)
347
0
    strlcat(out, "C-", sizeof out);
348
0
  if (key & KEYC_META)
349
0
    strlcat(out, "M-", sizeof out);
350
0
  if (key & KEYC_SHIFT)
351
0
    strlcat(out, "S-", sizeof out);
352
0
  key &= KEYC_MASK_KEY;
353
354
  /* Handle no key. */
355
0
  if (key == KEYC_NONE) {
356
0
    s = "None";
357
0
    goto append;
358
0
  }
359
360
  /* Handle special keys. */
361
0
  if (key == KEYC_UNKNOWN) {
362
0
    s = "Unknown";
363
0
    goto append;
364
0
  }
365
0
  if (key == KEYC_ANY) {
366
0
    s = "Any";
367
0
    goto append;
368
0
  }
369
0
  if (key == KEYC_FOCUS_IN) {
370
0
    s = "FocusIn";
371
0
    goto append;
372
0
  }
373
0
  if (key == KEYC_FOCUS_OUT) {
374
0
    s = "FocusOut";
375
0
    goto append;
376
0
  }
377
0
  if (key == KEYC_PASTE_START) {
378
0
    s = "PasteStart";
379
0
    goto append;
380
0
  }
381
0
  if (key == KEYC_PASTE_END) {
382
0
    s = "PasteEnd";
383
0
    goto append;
384
0
  }
385
0
  if (key == KEYC_MOUSE) {
386
0
    s = "Mouse";
387
0
    goto append;
388
0
  }
389
0
  if (key == KEYC_DRAGGING) {
390
0
    s = "Dragging";
391
0
    goto append;
392
0
  }
393
0
  if (key == KEYC_MOUSEMOVE_PANE) {
394
0
    s = "MouseMovePane";
395
0
    goto append;
396
0
  }
397
0
  if (key == KEYC_MOUSEMOVE_STATUS) {
398
0
    s = "MouseMoveStatus";
399
0
    goto append;
400
0
  }
401
0
  if (key == KEYC_MOUSEMOVE_STATUS_LEFT) {
402
0
    s = "MouseMoveStatusLeft";
403
0
    goto append;
404
0
  }
405
0
  if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) {
406
0
    s = "MouseMoveStatusRight";
407
0
    goto append;
408
0
  }
409
0
  if (key == KEYC_MOUSEMOVE_BORDER) {
410
0
    s = "MouseMoveBorder";
411
0
    goto append;
412
0
  }
413
0
  if (key >= KEYC_USER && key < KEYC_USER_END) {
414
0
    snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
415
0
    strlcat(out, tmp, sizeof out);
416
0
    goto out;
417
0
  }
418
419
  /* Try the key against the string table. */
420
0
  for (i = 0; i < nitems(key_string_table); i++) {
421
0
    if (key == (key_string_table[i].key & KEYC_MASK_KEY))
422
0
      break;
423
0
  }
424
0
  if (i != nitems(key_string_table)) {
425
0
    strlcat(out, key_string_table[i].string, sizeof out);
426
0
    goto out;
427
0
  }
428
429
  /* Is this a Unicode key? */
430
0
  if (KEYC_IS_UNICODE(key)) {
431
0
    utf8_to_data(key, &ud);
432
0
    off = strlen(out);
433
0
    memcpy(out + off, ud.data, ud.size);
434
0
    out[off + ud.size] = '\0';
435
0
    goto out;
436
0
  }
437
438
  /* Invalid keys are errors. */
439
0
  if (key > 255) {
440
0
    snprintf(out, sizeof out, "Invalid#%llx", saved);
441
0
    goto out;
442
0
  }
443
444
  /* Printable ASCII keys. */
445
0
  if (key > 32 && key <= 126) {
446
0
    tmp[0] = key;
447
0
    tmp[1] = '\0';
448
0
  } else if (key == 127)
449
0
    xsnprintf(tmp, sizeof tmp, "C-?");
450
0
  else if (key >= 128)
451
0
    xsnprintf(tmp, sizeof tmp, "\\%llo", key);
452
453
0
  strlcat(out, tmp, sizeof out);
454
0
  goto out;
455
456
0
append:
457
0
  strlcat(out, s, sizeof out);
458
459
0
out:
460
0
  if (with_flags && (saved & KEYC_MASK_FLAGS) != 0) {
461
0
    strlcat(out, "[", sizeof out);
462
0
    if (saved & KEYC_LITERAL)
463
0
      strlcat(out, "L", sizeof out);
464
0
    if (saved & KEYC_KEYPAD)
465
0
      strlcat(out, "K", sizeof out);
466
0
    if (saved & KEYC_CURSOR)
467
0
      strlcat(out, "C", sizeof out);
468
0
    if (saved & KEYC_IMPLIED_META)
469
0
      strlcat(out, "I", sizeof out);
470
0
    if (saved & KEYC_BUILD_MODIFIERS)
471
0
      strlcat(out, "B", sizeof out);
472
0
    if (saved & KEYC_SENT)
473
0
      strlcat(out, "S", sizeof out);
474
0
    strlcat(out, "]", sizeof out);
475
0
  }
476
0
  return (out);
477
0
}