Coverage Report

Created: 2026-05-30 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-list-keys.c
Line
Count
Source
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
#define LIST_KEYS_TEMPLATE          \
31
0
  "#{?notes_only,"          \
32
0
  "#{key_prefix} "          \
33
0
  "#{p|#{key_string_width}:key_string} "      \
34
0
  "#{?key_note,#{key_note},#{key_command}}"   \
35
0
  ","             \
36
0
  "bind-key #{?key_has_repeat,#{?key_repeat,-r,  },} "  \
37
0
  "-T #{p|#{key_table_width}:key_table} "     \
38
0
  "#{p|#{key_string_width}:key_string} "      \
39
0
  "#{key_command}}"
40
41
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
42
43
const struct cmd_entry cmd_list_keys_entry = {
44
  .name = "list-keys",
45
  .alias = "lsk",
46
47
  .args = { "1aF:NO:P:rT:", 0, 1, NULL },
48
  .usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
49
     "[-T key-table] [key]",
50
51
  .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
52
  .exec = cmd_list_keys_exec
53
};
54
55
static char *
56
cmd_list_keys_get_prefix(struct args *args)
57
0
{
58
0
  key_code  prefix;
59
60
0
  if (args_has(args, 'P'))
61
0
    return (xstrdup(args_get(args, 'P')));
62
63
0
  prefix = options_get_number(global_s_options, "prefix");
64
0
  if (prefix == KEYC_NONE)
65
0
    return (xstrdup(""));
66
0
  return (xstrdup(key_string_lookup_key(prefix, 0)));
67
0
}
68
69
static u_int
70
cmd_list_keys_get_width(struct key_binding **l, u_int n)
71
0
{
72
0
  u_int i, width, keywidth = 0;
73
74
0
  for (i = 0; i < n; i++) {
75
0
    width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
76
0
    if (width > keywidth)
77
0
      keywidth = width;
78
0
  }
79
0
  return (keywidth);
80
0
}
81
82
static u_int
83
cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
84
0
{
85
0
  u_int i, width, tablewidth = 0;
86
87
0
  for (i = 0; i < n; i++) {
88
0
    width = utf8_cstrwidth(l[i]->tablename);
89
0
    if (width > tablewidth)
90
0
      tablewidth = width;
91
0
  }
92
0
  return (tablewidth);
93
0
}
94
95
static struct key_binding **
96
cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
97
0
{
98
0
  const char       *tables[] = { "prefix", "root" };
99
0
  struct key_table     *t;
100
0
  struct key_binding    **lt;
101
0
  u_int         i, ltsz, len = 0, offset = 0;
102
0
  static struct key_binding **l = NULL;
103
0
  static u_int        lsz = 0;
104
105
0
  for (i = 0; i < nitems(tables); i++) {
106
0
    t = key_bindings_get_table(tables[i], 0);
107
0
    lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
108
0
    len += ltsz;
109
0
    if (lsz <= len) {
110
0
      lsz = len + 100;
111
0
      l = xreallocarray(l, lsz, sizeof *l);
112
0
    }
113
0
    memcpy(l + offset, lt, ltsz * sizeof *l);
114
0
    offset += ltsz;
115
0
  }
116
117
0
  *n = len;
118
0
  return (l);
119
0
}
120
121
static void
122
cmd_list_keys_filter_key_list(int filter_notes, int filter_key, key_code only,
123
    struct key_binding **l, u_int *n)
124
0
{
125
0
  key_code  key;
126
0
  u_int   i, j = 0;
127
128
0
  for (i = 0; i < *n; i++) {
129
0
    key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
130
0
    if (filter_key && only != key)
131
0
      continue;
132
0
    if (filter_notes && l[i]->note == NULL)
133
0
      continue;
134
0
    l[j++] = l[i];
135
0
  }
136
0
  *n = j;
137
0
}
138
139
static void
140
cmd_list_keys_format_add_key_binding(struct format_tree *ft,
141
    const struct key_binding *bd, const char *prefix)
142
0
{
143
0
  char  *s;
144
145
0
  if (bd->flags & KEY_BINDING_REPEAT)
146
0
    format_add(ft, "key_repeat", "1");
147
0
  else
148
0
    format_add(ft, "key_repeat", "0");
149
150
0
  if (bd->note != NULL)
151
0
    format_add(ft, "key_note", "%s", bd->note);
152
0
  else
153
0
    format_add(ft, "key_note", "%s", "");
154
155
0
  format_add(ft, "key_prefix", "%s", prefix);
156
0
  format_add(ft, "key_table", "%s", bd->tablename);
157
158
0
  format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0));
159
160
0
  s = cmd_list_print(bd->cmdlist, CMD_LIST_PRINT_ESCAPED|
161
0
      CMD_LIST_PRINT_NO_GROUPS);
162
0
  format_add(ft, "key_command", "%s", s);
163
0
  free(s);
164
0
}
165
166
static enum cmd_retval
167
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
168
0
{
169
0
  struct args   *args = cmd_get_args(self);
170
0
  struct client   *tc = cmdq_get_target_client(item);
171
0
  struct format_tree  *ft;
172
0
  struct key_table  *table = NULL;
173
0
  struct key_binding  **l;
174
0
  key_code     only = KEYC_UNKNOWN;
175
0
  const char    *template, *tablename, *keystr;
176
0
  char      *line;
177
0
  char      *prefix = NULL;
178
0
  u_int      i, n;
179
0
  int      single, notes_only, filter_notes, filter_key;
180
0
  struct sort_criteria   sort_crit;
181
182
0
  if ((keystr = args_string(args, 0)) != NULL) {
183
0
    only = key_string_lookup_string(keystr);
184
0
    if (only == KEYC_UNKNOWN) {
185
0
      cmdq_error(item, "invalid key: %s", keystr);
186
0
      return (CMD_RETURN_ERROR);
187
0
    }
188
0
    only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
189
0
  }
190
191
0
  sort_crit.order = sort_order_from_string(args_get(args, 'O'));
192
0
  if (sort_crit.order == SORT_END && args_has(args, 'O')) {
193
0
    cmdq_error(item, "invalid sort order");
194
0
    return (CMD_RETURN_ERROR);
195
0
  }
196
0
  sort_crit.reversed = args_has(args, 'r');
197
198
0
  if ((tablename = args_get(args, 'T')) != NULL) {
199
0
    table = key_bindings_get_table(tablename, 0);
200
0
    if (table == NULL) {
201
0
      cmdq_error(item, "table %s doesn't exist", tablename);
202
0
      return (CMD_RETURN_ERROR);
203
0
    }
204
0
  }
205
206
0
  prefix = cmd_list_keys_get_prefix(args);
207
0
  single = args_has(args, '1');
208
0
  notes_only = args_has(args, 'N');
209
210
0
  if ((template = args_get(args, 'F')) == NULL)
211
0
    template = LIST_KEYS_TEMPLATE;
212
213
0
  if (table)
214
0
    l = sort_get_key_bindings_table(table, &n, &sort_crit);
215
0
  else if (notes_only)
216
0
    l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit);
217
0
  else
218
0
    l = sort_get_key_bindings(&n, &sort_crit);
219
220
0
  filter_notes = notes_only && !args_has(args, 'a');
221
0
  filter_key = only != KEYC_UNKNOWN;
222
0
  if (filter_notes || filter_key) {
223
0
    cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l,
224
0
        &n);
225
0
  }
226
0
  if (single)
227
0
    n = 1;
228
229
0
  ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
230
0
  format_defaults(ft, NULL, NULL, NULL, NULL);
231
0
  format_add(ft, "notes_only", "%d", notes_only);
232
0
  format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
233
0
  format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
234
0
  format_add(ft, "key_table_width", "%u",
235
0
      cmd_list_keys_get_table_width(l, n));
236
0
  for (i = 0; i < n; i++) {
237
0
    cmd_list_keys_format_add_key_binding(ft, l[i], prefix);
238
239
0
    line = format_expand(ft, template);
240
0
    if ((single && tc != NULL) || n == 1)
241
0
      status_message_set(tc, -1, 1, 0, 0, "%s", line);
242
0
    else if (*line != '\0')
243
0
      cmdq_print(item, "%s", line);
244
0
    free(line);
245
246
0
    if (single)
247
0
      break;
248
0
  }
249
0
  format_free(ft);
250
0
  free(prefix);
251
252
0
  return (CMD_RETURN_NORMAL);
253
0
}