1*1e415b51Snicm /* $OpenBSD: cmd-list-keys.c,v 1.67 2023/01/17 10:40:51 nicm Exp $ */
2311827fbSnicm
3311827fbSnicm /*
498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm *
6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm * copyright notice and this permission notice appear in all copies.
9311827fbSnicm *
10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm */
18311827fbSnicm
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm
211d1963bbSnicm #include <stdlib.h>
22e662fe71Snicm #include <string.h>
23e662fe71Snicm
24311827fbSnicm #include "tmux.h"
25311827fbSnicm
26311827fbSnicm /*
27311827fbSnicm * List key bindings.
28311827fbSnicm */
29311827fbSnicm
3068e0a7f2Snicm static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
3133157bb3Snicm
3268e0a7f2Snicm static enum cmd_retval cmd_list_keys_commands(struct cmd *,
3368e0a7f2Snicm struct cmdq_item *);
3468895571Snicm
35311827fbSnicm const struct cmd_entry cmd_list_keys_entry = {
36c057646bSnicm .name = "list-keys",
37c057646bSnicm .alias = "lsk",
38c057646bSnicm
39a51dead1Snicm .args = { "1aNP:T:", 0, 1, NULL },
402f3e7a82Snicm .usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
41c057646bSnicm
427a61a8ddSnicm .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
43c057646bSnicm .exec = cmd_list_keys_exec
44311827fbSnicm };
45311827fbSnicm
4633157bb3Snicm const struct cmd_entry cmd_list_commands_entry = {
47c057646bSnicm .name = "list-commands",
48c057646bSnicm .alias = "lscm",
49c057646bSnicm
50a51dead1Snicm .args = { "F:", 0, 1, NULL },
512ecd3685Snicm .usage = "[-F format] [command]",
52c057646bSnicm
537a61a8ddSnicm .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
54c057646bSnicm .exec = cmd_list_keys_exec
5533157bb3Snicm };
5633157bb3Snicm
57c8877404Snicm static u_int
cmd_list_keys_get_width(const char * tablename,key_code only)58c8877404Snicm cmd_list_keys_get_width(const char *tablename, key_code only)
59c8877404Snicm {
60c8877404Snicm struct key_table *table;
61c8877404Snicm struct key_binding *bd;
62c8877404Snicm u_int width, keywidth = 0;
63c8877404Snicm
64c8877404Snicm table = key_bindings_get_table(tablename, 0);
65c8877404Snicm if (table == NULL)
66c8877404Snicm return (0);
67c8877404Snicm bd = key_bindings_first(table);
68c8877404Snicm while (bd != NULL) {
69c8877404Snicm if ((only != KEYC_UNKNOWN && bd->key != only) ||
70c8877404Snicm KEYC_IS_MOUSE(bd->key) ||
7167c16a7cSnicm bd->note == NULL ||
7267c16a7cSnicm *bd->note == '\0') {
73c8877404Snicm bd = key_bindings_next(table, bd);
74c8877404Snicm continue;
75c8877404Snicm }
765416581eSnicm width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
77c8877404Snicm if (width > keywidth)
78c8877404Snicm keywidth = width;
79c8877404Snicm
80c8877404Snicm bd = key_bindings_next(table, bd);
81c8877404Snicm }
82c8877404Snicm return (keywidth);
83c8877404Snicm }
84c8877404Snicm
85c8877404Snicm static int
cmd_list_keys_print_notes(struct cmdq_item * item,struct args * args,const char * tablename,u_int keywidth,key_code only,const char * prefix)86c8877404Snicm cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
87c8877404Snicm const char *tablename, u_int keywidth, key_code only, const char *prefix)
88c8877404Snicm {
89035dc73dSnicm struct client *tc = cmdq_get_target_client(item);
90c8877404Snicm struct key_table *table;
91c8877404Snicm struct key_binding *bd;
92c8877404Snicm const char *key;
932f3e7a82Snicm char *tmp, *note;
94c8877404Snicm int found = 0;
95c8877404Snicm
96c8877404Snicm table = key_bindings_get_table(tablename, 0);
97c8877404Snicm if (table == NULL)
98c8877404Snicm return (0);
99c8877404Snicm bd = key_bindings_first(table);
100c8877404Snicm while (bd != NULL) {
101c8877404Snicm if ((only != KEYC_UNKNOWN && bd->key != only) ||
102c8877404Snicm KEYC_IS_MOUSE(bd->key) ||
10367c16a7cSnicm ((bd->note == NULL || *bd->note == '\0') &&
10467c16a7cSnicm !args_has(args, 'a'))) {
105c8877404Snicm bd = key_bindings_next(table, bd);
106c8877404Snicm continue;
107c8877404Snicm }
108c8877404Snicm found = 1;
1095416581eSnicm key = key_string_lookup_key(bd->key, 0);
110c8877404Snicm
11167c16a7cSnicm if (bd->note == NULL || *bd->note == '\0')
1122f3e7a82Snicm note = cmd_list_print(bd->cmdlist, 1);
1132f3e7a82Snicm else
1142f3e7a82Snicm note = xstrdup(bd->note);
115c8877404Snicm tmp = utf8_padcstr(key, keywidth + 1);
116e7e79d0aSnicm if (args_has(args, '1') && tc != NULL) {
117e7e79d0aSnicm status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
118e7e79d0aSnicm note);
119e7e79d0aSnicm } else
1202f3e7a82Snicm cmdq_print(item, "%s%s%s", prefix, tmp, note);
121c8877404Snicm free(tmp);
1222f3e7a82Snicm free(note);
123c8877404Snicm
124c8877404Snicm if (args_has(args, '1'))
125c8877404Snicm break;
126c8877404Snicm bd = key_bindings_next(table, bd);
127c8877404Snicm }
128c8877404Snicm return (found);
129c8877404Snicm }
130c8877404Snicm
131c8877404Snicm static char *
cmd_list_keys_get_prefix(struct args * args,key_code * prefix)132c8877404Snicm cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
133c8877404Snicm {
134c8877404Snicm char *s;
135c8877404Snicm
136c8877404Snicm *prefix = options_get_number(global_s_options, "prefix");
137c8877404Snicm if (!args_has(args, 'P')) {
138c8877404Snicm if (*prefix != KEYC_NONE)
1395416581eSnicm xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
140c8877404Snicm else
141c8877404Snicm s = xstrdup("");
142c8877404Snicm } else
143c8877404Snicm s = xstrdup(args_get(args, 'P'));
144c8877404Snicm return (s);
145c8877404Snicm }
146c8877404Snicm
147de9718b4Snicm static enum cmd_retval
cmd_list_keys_exec(struct cmd * self,struct cmdq_item * item)14868e0a7f2Snicm cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
149311827fbSnicm {
15090d7ba38Snicm struct args *args = cmd_get_args(self);
151*1e415b51Snicm struct client *tc = cmdq_get_target_client(item);
152ec651339Snicm struct key_table *table;
153311827fbSnicm struct key_binding *bd;
1541693b10bSnicm const char *tablename, *r, *keystr;
155c8877404Snicm char *key, *cp, *tmp, *start, *empty;
156c8877404Snicm key_code prefix, only = KEYC_UNKNOWN;
157c8877404Snicm int repeat, width, tablewidth, keywidth, found = 0;
1587da19389Snicm size_t tmpsize, tmpused, cplen;
159e662fe71Snicm
16090d7ba38Snicm if (cmd_get_entry(self) == &cmd_list_commands_entry)
16168e0a7f2Snicm return (cmd_list_keys_commands(self, item));
16233157bb3Snicm
1631693b10bSnicm if ((keystr = args_string(args, 0)) != NULL) {
1641693b10bSnicm only = key_string_lookup_string(keystr);
165c8877404Snicm if (only == KEYC_UNKNOWN) {
1661693b10bSnicm cmdq_error(item, "invalid key: %s", keystr);
167c8877404Snicm return (CMD_RETURN_ERROR);
168c8877404Snicm }
1695b2ff571Snicm only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
170c8877404Snicm }
171c8877404Snicm
172ec651339Snicm tablename = args_get(args, 'T');
173ec651339Snicm if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
17468e0a7f2Snicm cmdq_error(item, "table %s doesn't exist", tablename);
175ec651339Snicm return (CMD_RETURN_ERROR);
176e662fe71Snicm }
177e662fe71Snicm
178c8877404Snicm if (args_has(args, 'N')) {
179c8877404Snicm if (tablename == NULL) {
180c8877404Snicm start = cmd_list_keys_get_prefix(args, &prefix);
181c8877404Snicm keywidth = cmd_list_keys_get_width("root", only);
182c8877404Snicm if (prefix != KEYC_NONE) {
183c8877404Snicm width = cmd_list_keys_get_width("prefix", only);
184c8877404Snicm if (width == 0)
185c8877404Snicm prefix = KEYC_NONE;
186c8877404Snicm else if (width > keywidth)
187c8877404Snicm keywidth = width;
188c8877404Snicm }
189c8877404Snicm empty = utf8_padcstr("", utf8_cstrwidth(start));
190c8877404Snicm
191c8877404Snicm found = cmd_list_keys_print_notes(item, args, "root",
192c8877404Snicm keywidth, only, empty);
193c8877404Snicm if (prefix != KEYC_NONE) {
194c8877404Snicm if (cmd_list_keys_print_notes(item, args,
195c8877404Snicm "prefix", keywidth, only, start))
196c8877404Snicm found = 1;
197c8877404Snicm }
198c8877404Snicm free(empty);
199c8877404Snicm } else {
200c8877404Snicm if (args_has(args, 'P'))
201c8877404Snicm start = xstrdup(args_get(args, 'P'));
202c8877404Snicm else
203c8877404Snicm start = xstrdup("");
204c8877404Snicm keywidth = cmd_list_keys_get_width(tablename, only);
205c8877404Snicm found = cmd_list_keys_print_notes(item, args, tablename,
206c8877404Snicm keywidth, only, start);
207c8877404Snicm
208c8877404Snicm }
209c8877404Snicm free(start);
210c8877404Snicm goto out;
211c8877404Snicm }
212c8877404Snicm
213ec651339Snicm repeat = 0;
214ec651339Snicm tablewidth = keywidth = 0;
2154e325abeSnicm table = key_bindings_first_table();
2164e325abeSnicm while (table != NULL) {
2174e325abeSnicm if (tablename != NULL && strcmp(table->name, tablename) != 0) {
2184e325abeSnicm table = key_bindings_next_table(table);
219ec651339Snicm continue;
2204e325abeSnicm }
2214e325abeSnicm bd = key_bindings_first(table);
2224e325abeSnicm while (bd != NULL) {
223c8877404Snicm if (only != KEYC_UNKNOWN && bd->key != only) {
224c8877404Snicm bd = key_bindings_next(table, bd);
225c8877404Snicm continue;
226c8877404Snicm }
2275416581eSnicm key = args_escape(key_string_lookup_key(bd->key, 0));
2284fbdc293Snicm
229bebc73f1Snicm if (bd->flags & KEY_BINDING_REPEAT)
230ec651339Snicm repeat = 1;
2314fbdc293Snicm
2321d1963bbSnicm width = utf8_cstrwidth(table->name);
233ec651339Snicm if (width > tablewidth)
234ec651339Snicm tablewidth = width;
235885a4698Snicm width = utf8_cstrwidth(key);
236ec651339Snicm if (width > keywidth)
237ec651339Snicm keywidth = width;
2384e325abeSnicm
2395c131106Snicm free(key);
2404e325abeSnicm bd = key_bindings_next(table, bd);
241ec651339Snicm }
2424e325abeSnicm table = key_bindings_next_table(table);
243ec651339Snicm }
244ec651339Snicm
2457da19389Snicm tmpsize = 256;
2467da19389Snicm tmp = xmalloc(tmpsize);
2471693b10bSnicm
2484e325abeSnicm table = key_bindings_first_table();
2494e325abeSnicm while (table != NULL) {
2504e325abeSnicm if (tablename != NULL && strcmp(table->name, tablename) != 0) {
2514e325abeSnicm table = key_bindings_next_table(table);
252ec651339Snicm continue;
2534e325abeSnicm }
2544e325abeSnicm bd = key_bindings_first(table);
2554e325abeSnicm while (bd != NULL) {
256c8877404Snicm if (only != KEYC_UNKNOWN && bd->key != only) {
257c8877404Snicm bd = key_bindings_next(table, bd);
258c8877404Snicm continue;
259c8877404Snicm }
260c8877404Snicm found = 1;
2615416581eSnicm key = args_escape(key_string_lookup_key(bd->key, 0));
262311827fbSnicm
263ec651339Snicm if (!repeat)
264ec651339Snicm r = "";
265bebc73f1Snicm else if (bd->flags & KEY_BINDING_REPEAT)
266ec651339Snicm r = "-r ";
267ec651339Snicm else
268ec651339Snicm r = " ";
2697da19389Snicm tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
2701d1963bbSnicm
2711d1963bbSnicm cp = utf8_padcstr(table->name, tablewidth);
2727da19389Snicm cplen = strlen(cp) + 1;
2737da19389Snicm while (tmpused + cplen + 1 >= tmpsize) {
2747da19389Snicm tmpsize *= 2;
2757da19389Snicm tmp = xrealloc(tmp, tmpsize);
2767da19389Snicm }
2777d64d0d8Snicm strlcat(tmp, cp, tmpsize);
2787da19389Snicm tmpused = strlcat(tmp, " ", tmpsize);
2791d1963bbSnicm free(cp);
2801d1963bbSnicm
2811d1963bbSnicm cp = utf8_padcstr(key, keywidth);
2827da19389Snicm cplen = strlen(cp) + 1;
2837da19389Snicm while (tmpused + cplen + 1 >= tmpsize) {
2847da19389Snicm tmpsize *= 2;
2857da19389Snicm tmp = xrealloc(tmp, tmpsize);
2867da19389Snicm }
2877d64d0d8Snicm strlcat(tmp, cp, tmpsize);
2887da19389Snicm tmpused = strlcat(tmp, " ", tmpsize);
2891d1963bbSnicm free(cp);
2901d1963bbSnicm
2915c131106Snicm cp = cmd_list_print(bd->cmdlist, 1);
2927da19389Snicm cplen = strlen(cp);
2937da19389Snicm while (tmpused + cplen + 1 >= tmpsize) {
2947da19389Snicm tmpsize *= 2;
2957da19389Snicm tmp = xrealloc(tmp, tmpsize);
2967da19389Snicm }
2977da19389Snicm strlcat(tmp, cp, tmpsize);
2988ae71cbbSnicm free(cp);
299ec651339Snicm
300*1e415b51Snicm if (args_has(args, '1') && tc != NULL) {
301*1e415b51Snicm status_message_set(tc, -1, 1, 0, "bind-key %s",
302*1e415b51Snicm tmp);
303*1e415b51Snicm } else
30468e0a7f2Snicm cmdq_print(item, "bind-key %s", tmp);
3055c131106Snicm free(key);
306*1e415b51Snicm
307*1e415b51Snicm if (args_has(args, '1'))
308*1e415b51Snicm break;
3094e325abeSnicm bd = key_bindings_next(table, bd);
310311827fbSnicm }
3114e325abeSnicm table = key_bindings_next_table(table);
312ec651339Snicm }
313311827fbSnicm
3147da19389Snicm free(tmp);
3157da19389Snicm
316c8877404Snicm out:
317c8877404Snicm if (only != KEYC_UNKNOWN && !found) {
3181693b10bSnicm cmdq_error(item, "unknown key: %s", args_string(args, 0));
319c8877404Snicm return (CMD_RETURN_ERROR);
320c8877404Snicm }
321a224d0d3Snicm return (CMD_RETURN_NORMAL);
322311827fbSnicm }
32368895571Snicm
324de9718b4Snicm static enum cmd_retval
cmd_list_keys_commands(struct cmd * self,struct cmdq_item * item)32568e0a7f2Snicm cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
32633157bb3Snicm {
32790d7ba38Snicm struct args *args = cmd_get_args(self);
32833157bb3Snicm const struct cmd_entry **entryp;
32926529d6fSnicm const struct cmd_entry *entry;
330de9718b4Snicm struct format_tree *ft;
3311693b10bSnicm const char *template, *s, *command;
332de9718b4Snicm char *line;
333de9718b4Snicm
334de9718b4Snicm if ((template = args_get(args, 'F')) == NULL) {
335de9718b4Snicm template = "#{command_list_name}"
336de9718b4Snicm "#{?command_list_alias, (#{command_list_alias}),} "
337de9718b4Snicm "#{command_list_usage}";
338de9718b4Snicm }
339de9718b4Snicm
340040343aeSnicm ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
341de9718b4Snicm format_defaults(ft, NULL, NULL, NULL, NULL);
34233157bb3Snicm
3431693b10bSnicm command = args_string(args, 0);
34433157bb3Snicm for (entryp = cmd_table; *entryp != NULL; entryp++) {
34533157bb3Snicm entry = *entryp;
3462ecd3685Snicm if (command != NULL &&
3472ecd3685Snicm (strcmp(entry->name, command) != 0 &&
3482ecd3685Snicm (entry->alias == NULL ||
3492ecd3685Snicm strcmp(entry->alias, command) != 0)))
3502ecd3685Snicm continue;
351de9718b4Snicm
352de9718b4Snicm format_add(ft, "command_list_name", "%s", entry->name);
35368e0a7f2Snicm if (entry->alias != NULL)
35468e0a7f2Snicm s = entry->alias;
35568e0a7f2Snicm else
35668e0a7f2Snicm s = "";
35768e0a7f2Snicm format_add(ft, "command_list_alias", "%s", s);
35868e0a7f2Snicm if (entry->usage != NULL)
35968e0a7f2Snicm s = entry->usage;
36068e0a7f2Snicm else
36168e0a7f2Snicm s = "";
36268e0a7f2Snicm format_add(ft, "command_list_usage", "%s", s);
36333157bb3Snicm
364de9718b4Snicm line = format_expand(ft, template);
365de9718b4Snicm if (*line != '\0')
36668e0a7f2Snicm cmdq_print(item, "%s", line);
367de9718b4Snicm free(line);
368de9718b4Snicm }
369de9718b4Snicm
370de9718b4Snicm format_free(ft);
37133157bb3Snicm return (CMD_RETURN_NORMAL);
37233157bb3Snicm }
373