/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 | } |