Coverage Report

Created: 2023-06-07 06:04

/src/tmux/cmd-list-keys.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 <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
/*
27
 * List key bindings.
28
 */
29
30
static enum cmd_retval  cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
31
32
static enum cmd_retval  cmd_list_keys_commands(struct cmd *,
33
          struct cmdq_item *);
34
35
const struct cmd_entry cmd_list_keys_entry = {
36
  .name = "list-keys",
37
  .alias = "lsk",
38
39
  .args = { "1aNP:T:", 0, 1, NULL },
40
  .usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
41
42
  .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
43
  .exec = cmd_list_keys_exec
44
};
45
46
const struct cmd_entry cmd_list_commands_entry = {
47
  .name = "list-commands",
48
  .alias = "lscm",
49
50
  .args = { "F:", 0, 1, NULL },
51
  .usage = "[-F format] [command]",
52
53
  .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
54
  .exec = cmd_list_keys_exec
55
};
56
57
static u_int
58
cmd_list_keys_get_width(const char *tablename, key_code only)
59
0
{
60
0
  struct key_table  *table;
61
0
  struct key_binding  *bd;
62
0
  u_int      width, keywidth = 0;
63
64
0
  table = key_bindings_get_table(tablename, 0);
65
0
  if (table == NULL)
66
0
    return (0);
67
0
  bd = key_bindings_first(table);
68
0
  while (bd != NULL) {
69
0
    if ((only != KEYC_UNKNOWN && bd->key != only) ||
70
0
        KEYC_IS_MOUSE(bd->key) ||
71
0
        bd->note == NULL ||
72
0
        *bd->note == '\0') {
73
0
      bd = key_bindings_next(table, bd);
74
0
      continue;
75
0
    }
76
0
    width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
77
0
    if (width > keywidth)
78
0
      keywidth = width;
79
80
0
    bd = key_bindings_next(table, bd);
81
0
  }
82
0
  return (keywidth);
83
0
}
84
85
static int
86
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
87
    const char *tablename, u_int keywidth, key_code only, const char *prefix)
88
0
{
89
0
  struct client   *tc = cmdq_get_target_client(item);
90
0
  struct key_table  *table;
91
0
  struct key_binding  *bd;
92
0
  const char    *key;
93
0
  char      *tmp, *note;
94
0
  int                  found = 0;
95
96
0
  table = key_bindings_get_table(tablename, 0);
97
0
  if (table == NULL)
98
0
    return (0);
99
0
  bd = key_bindings_first(table);
100
0
  while (bd != NULL) {
101
0
    if ((only != KEYC_UNKNOWN && bd->key != only) ||
102
0
        KEYC_IS_MOUSE(bd->key) ||
103
0
        ((bd->note == NULL || *bd->note == '\0') &&
104
0
        !args_has(args, 'a'))) {
105
0
      bd = key_bindings_next(table, bd);
106
0
      continue;
107
0
    }
108
0
    found = 1;
109
0
    key = key_string_lookup_key(bd->key, 0);
110
111
0
    if (bd->note == NULL || *bd->note == '\0')
112
0
      note = cmd_list_print(bd->cmdlist, 1);
113
0
    else
114
0
      note = xstrdup(bd->note);
115
0
    tmp = utf8_padcstr(key, keywidth + 1);
116
0
    if (args_has(args, '1') && tc != NULL) {
117
0
      status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
118
0
          note);
119
0
    } else
120
0
      cmdq_print(item, "%s%s%s", prefix, tmp, note);
121
0
    free(tmp);
122
0
    free(note);
123
124
0
    if (args_has(args, '1'))
125
0
      break;
126
0
    bd = key_bindings_next(table, bd);
127
0
  }
128
0
  return (found);
129
0
}
130
131
static char *
132
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
133
0
{
134
0
  char  *s;
135
136
0
  *prefix = options_get_number(global_s_options, "prefix");
137
0
  if (!args_has(args, 'P')) {
138
0
    if (*prefix != KEYC_NONE)
139
0
      xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
140
0
    else
141
0
      s = xstrdup("");
142
0
  } else
143
0
    s = xstrdup(args_get(args, 'P'));
144
0
  return (s);
145
0
}
146
147
static enum cmd_retval
148
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
149
0
{
150
0
  struct args   *args = cmd_get_args(self);
151
0
  struct client   *tc = cmdq_get_target_client(item);
152
0
  struct key_table  *table;
153
0
  struct key_binding  *bd;
154
0
  const char    *tablename, *r, *keystr;
155
0
  char      *key, *cp, *tmp, *start, *empty;
156
0
  key_code     prefix, only = KEYC_UNKNOWN;
157
0
  int      repeat, width, tablewidth, keywidth, found = 0;
158
0
  size_t       tmpsize, tmpused, cplen;
159
160
0
  if (cmd_get_entry(self) == &cmd_list_commands_entry)
161
0
    return (cmd_list_keys_commands(self, item));
162
163
0
  if ((keystr = args_string(args, 0)) != NULL) {
164
0
    only = key_string_lookup_string(keystr);
165
0
    if (only == KEYC_UNKNOWN) {
166
0
      cmdq_error(item, "invalid key: %s", keystr);
167
0
      return (CMD_RETURN_ERROR);
168
0
    }
169
0
    only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
170
0
  }
171
172
0
  tablename = args_get(args, 'T');
173
0
  if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
174
0
    cmdq_error(item, "table %s doesn't exist", tablename);
175
0
    return (CMD_RETURN_ERROR);
176
0
  }
177
178
0
  if (args_has(args, 'N')) {
179
0
    if (tablename == NULL) {
180
0
      start = cmd_list_keys_get_prefix(args, &prefix);
181
0
      keywidth = cmd_list_keys_get_width("root", only);
182
0
      if (prefix != KEYC_NONE) {
183
0
        width = cmd_list_keys_get_width("prefix", only);
184
0
        if (width == 0)
185
0
          prefix = KEYC_NONE;
186
0
        else if (width > keywidth)
187
0
          keywidth = width;
188
0
      }
189
0
      empty = utf8_padcstr("", utf8_cstrwidth(start));
190
191
0
      found = cmd_list_keys_print_notes(item, args, "root",
192
0
          keywidth, only, empty);
193
0
      if (prefix != KEYC_NONE) {
194
0
        if (cmd_list_keys_print_notes(item, args,
195
0
            "prefix", keywidth, only, start))
196
0
          found = 1;
197
0
      }
198
0
      free(empty);
199
0
    } else {
200
0
      if (args_has(args, 'P'))
201
0
        start = xstrdup(args_get(args, 'P'));
202
0
      else
203
0
        start = xstrdup("");
204
0
      keywidth = cmd_list_keys_get_width(tablename, only);
205
0
      found = cmd_list_keys_print_notes(item, args, tablename,
206
0
          keywidth, only, start);
207
208
0
    }
209
0
    free(start);
210
0
    goto out;
211
0
  }
212
213
0
  repeat = 0;
214
0
  tablewidth = keywidth = 0;
215
0
  table = key_bindings_first_table();
216
0
  while (table != NULL) {
217
0
    if (tablename != NULL && strcmp(table->name, tablename) != 0) {
218
0
      table = key_bindings_next_table(table);
219
0
      continue;
220
0
    }
221
0
    bd = key_bindings_first(table);
222
0
    while (bd != NULL) {
223
0
      if (only != KEYC_UNKNOWN && bd->key != only) {
224
0
        bd = key_bindings_next(table, bd);
225
0
        continue;
226
0
      }
227
0
      key = args_escape(key_string_lookup_key(bd->key, 0));
228
229
0
      if (bd->flags & KEY_BINDING_REPEAT)
230
0
        repeat = 1;
231
232
0
      width = utf8_cstrwidth(table->name);
233
0
      if (width > tablewidth)
234
0
        tablewidth = width;
235
0
      width = utf8_cstrwidth(key);
236
0
      if (width > keywidth)
237
0
        keywidth = width;
238
239
0
      free(key);
240
0
      bd = key_bindings_next(table, bd);
241
0
    }
242
0
    table = key_bindings_next_table(table);
243
0
  }
244
245
0
  tmpsize = 256;
246
0
  tmp = xmalloc(tmpsize);
247
248
0
  table = key_bindings_first_table();
249
0
  while (table != NULL) {
250
0
    if (tablename != NULL && strcmp(table->name, tablename) != 0) {
251
0
      table = key_bindings_next_table(table);
252
0
      continue;
253
0
    }
254
0
    bd = key_bindings_first(table);
255
0
    while (bd != NULL) {
256
0
      if (only != KEYC_UNKNOWN && bd->key != only) {
257
0
        bd = key_bindings_next(table, bd);
258
0
        continue;
259
0
      }
260
0
      found = 1;
261
0
      key = args_escape(key_string_lookup_key(bd->key, 0));
262
263
0
      if (!repeat)
264
0
        r = "";
265
0
      else if (bd->flags & KEY_BINDING_REPEAT)
266
0
        r = "-r ";
267
0
      else
268
0
        r = "   ";
269
0
      tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
270
271
0
      cp = utf8_padcstr(table->name, tablewidth);
272
0
      cplen = strlen(cp) + 1;
273
0
      while (tmpused + cplen + 1 >= tmpsize) {
274
0
        tmpsize *= 2;
275
0
        tmp = xrealloc(tmp, tmpsize);
276
0
      }
277
0
      strlcat(tmp, cp, tmpsize);
278
0
      tmpused = strlcat(tmp, " ", tmpsize);
279
0
      free(cp);
280
281
0
      cp = utf8_padcstr(key, keywidth);
282
0
      cplen = strlen(cp) + 1;
283
0
      while (tmpused + cplen + 1 >= tmpsize) {
284
0
        tmpsize *= 2;
285
0
        tmp = xrealloc(tmp, tmpsize);
286
0
      }
287
0
      strlcat(tmp, cp, tmpsize);
288
0
      tmpused = strlcat(tmp, " ", tmpsize);
289
0
      free(cp);
290
291
0
      cp = cmd_list_print(bd->cmdlist, 1);
292
0
      cplen = strlen(cp);
293
0
      while (tmpused + cplen + 1 >= tmpsize) {
294
0
        tmpsize *= 2;
295
0
        tmp = xrealloc(tmp, tmpsize);
296
0
      }
297
0
      strlcat(tmp, cp, tmpsize);
298
0
      free(cp);
299
300
0
      if (args_has(args, '1') && tc != NULL) {
301
0
        status_message_set(tc, -1, 1, 0, "bind-key %s",
302
0
            tmp);
303
0
      } else
304
0
        cmdq_print(item, "bind-key %s", tmp);
305
0
      free(key);
306
307
0
      if (args_has(args, '1'))
308
0
        break;
309
0
      bd = key_bindings_next(table, bd);
310
0
    }
311
0
    table = key_bindings_next_table(table);
312
0
  }
313
314
0
  free(tmp);
315
316
0
out:
317
0
  if (only != KEYC_UNKNOWN && !found) {
318
0
    cmdq_error(item, "unknown key: %s", args_string(args, 0));
319
0
    return (CMD_RETURN_ERROR);
320
0
  }
321
0
  return (CMD_RETURN_NORMAL);
322
0
}
323
324
static enum cmd_retval
325
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
326
0
{
327
0
  struct args    *args = cmd_get_args(self);
328
0
  const struct cmd_entry  **entryp;
329
0
  const struct cmd_entry   *entry;
330
0
  struct format_tree   *ft;
331
0
  const char     *template, *s, *command;
332
0
  char       *line;
333
334
0
  if ((template = args_get(args, 'F')) == NULL) {
335
0
    template = "#{command_list_name}"
336
0
        "#{?command_list_alias, (#{command_list_alias}),} "
337
0
        "#{command_list_usage}";
338
0
  }
339
340
0
  ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
341
0
  format_defaults(ft, NULL, NULL, NULL, NULL);
342
343
0
  command = args_string(args, 0);
344
0
  for (entryp = cmd_table; *entryp != NULL; entryp++) {
345
0
    entry = *entryp;
346
0
    if (command != NULL &&
347
0
        (strcmp(entry->name, command) != 0 &&
348
0
        (entry->alias == NULL ||
349
0
        strcmp(entry->alias, command) != 0)))
350
0
        continue;
351
352
0
    format_add(ft, "command_list_name", "%s", entry->name);
353
0
    if (entry->alias != NULL)
354
0
      s = entry->alias;
355
0
    else
356
0
      s = "";
357
0
    format_add(ft, "command_list_alias", "%s", s);
358
0
    if (entry->usage != NULL)
359
0
      s = entry->usage;
360
0
    else
361
0
      s = "";
362
0
    format_add(ft, "command_list_usage", "%s", s);
363
364
0
    line = format_expand(ft, template);
365
0
    if (*line != '\0')
366
0
      cmdq_print(item, "%s", line);
367
0
    free(line);
368
0
  }
369
370
0
  format_free(ft);
371
0
  return (CMD_RETURN_NORMAL);
372
0
}