xref: /openbsd/usr.bin/tmux/cmd-list-keys.c (revision 1e415b51)
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