blob: ae9f995c1bfa24b73631714f96a1710f71d0a10b [file] [log] [blame] [raw]
Nicholas Marriott35876ea2009-06-01 22:58:49 +00001/* $OpenBSD$ */
2
3/*
nicm995af0e2016-01-19 15:59:12 +00004 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
Nicholas Marriott35876ea2009-06-01 22:58:49 +00005 *
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
nicma209ea32015-11-12 12:43:36 +000021#include <stdlib.h>
Nicholas Marriotte43b6a22009-07-21 22:41:00 +000022#include <string.h>
23
Nicholas Marriott35876ea2009-06-01 22:58:49 +000024#include "tmux.h"
25
26/*
27 * List key bindings.
28 */
29
nicmb342bd02016-10-16 19:04:05 +000030static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
nicmf5bc8552014-10-20 22:44:30 +000031
nicmb342bd02016-10-16 19:04:05 +000032static enum cmd_retval cmd_list_keys_commands(struct cmd *,
33 struct cmdq_item *);
Nicholas Marriott86785002009-07-28 07:03:32 +000034
Nicholas Marriott35876ea2009-06-01 22:58:49 +000035const struct cmd_entry cmd_list_keys_entry = {
nicmecfeee22015-12-13 21:53:57 +000036 .name = "list-keys",
37 .alias = "lsk",
38
nicm08e63602021-08-21 10:22:38 +000039 .args = { "1aNP:T:", 0, 1, NULL },
nicma1f6bd52020-02-15 15:08:08 +000040 .usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
nicmecfeee22015-12-13 21:53:57 +000041
nicmed971262016-10-14 22:14:22 +000042 .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
nicmecfeee22015-12-13 21:53:57 +000043 .exec = cmd_list_keys_exec
nicmf5bc8552014-10-20 22:44:30 +000044};
45
46const struct cmd_entry cmd_list_commands_entry = {
nicmecfeee22015-12-13 21:53:57 +000047 .name = "list-commands",
48 .alias = "lscm",
49
nicm08e63602021-08-21 10:22:38 +000050 .args = { "F:", 0, 1, NULL },
nicmfccce692020-04-05 08:40:31 +000051 .usage = "[-F format] [command]",
nicmecfeee22015-12-13 21:53:57 +000052
nicmed971262016-10-14 22:14:22 +000053 .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
nicmecfeee22015-12-13 21:53:57 +000054 .exec = cmd_list_keys_exec
Nicholas Marriott35876ea2009-06-01 22:58:49 +000055};
56
nicmd0b8d032020-01-27 08:53:13 +000057static u_int
58cmd_list_keys_get_width(const char *tablename, key_code only)
59{
60 struct key_table *table;
61 struct key_binding *bd;
62 u_int width, keywidth = 0;
63
64 table = key_bindings_get_table(tablename, 0);
65 if (table == NULL)
66 return (0);
67 bd = key_bindings_first(table);
68 while (bd != NULL) {
69 if ((only != KEYC_UNKNOWN && bd->key != only) ||
70 KEYC_IS_MOUSE(bd->key) ||
nicmd67245c2020-05-16 16:02:24 +000071 bd->note == NULL ||
72 *bd->note == '\0') {
nicmd0b8d032020-01-27 08:53:13 +000073 bd = key_bindings_next(table, bd);
74 continue;
75 }
nicm292b3352020-05-16 16:35:13 +000076 width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
nicmd0b8d032020-01-27 08:53:13 +000077 if (width > keywidth)
78 keywidth = width;
79
80 bd = key_bindings_next(table, bd);
81 }
82 return (keywidth);
83}
84
85static int
86cmd_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{
nicm3f7f9a02020-04-13 20:51:57 +000089 struct client *tc = cmdq_get_target_client(item);
nicmd0b8d032020-01-27 08:53:13 +000090 struct key_table *table;
91 struct key_binding *bd;
92 const char *key;
nicma1f6bd52020-02-15 15:08:08 +000093 char *tmp, *note;
nicmd0b8d032020-01-27 08:53:13 +000094 int found = 0;
95
96 table = key_bindings_get_table(tablename, 0);
97 if (table == NULL)
98 return (0);
99 bd = key_bindings_first(table);
100 while (bd != NULL) {
101 if ((only != KEYC_UNKNOWN && bd->key != only) ||
102 KEYC_IS_MOUSE(bd->key) ||
nicmd67245c2020-05-16 16:02:24 +0000103 ((bd->note == NULL || *bd->note == '\0') &&
104 !args_has(args, 'a'))) {
nicmd0b8d032020-01-27 08:53:13 +0000105 bd = key_bindings_next(table, bd);
106 continue;
107 }
108 found = 1;
nicm292b3352020-05-16 16:35:13 +0000109 key = key_string_lookup_key(bd->key, 0);
nicmd0b8d032020-01-27 08:53:13 +0000110
nicmd67245c2020-05-16 16:02:24 +0000111 if (bd->note == NULL || *bd->note == '\0')
nicma1f6bd52020-02-15 15:08:08 +0000112 note = cmd_list_print(bd->cmdlist, 1);
113 else
114 note = xstrdup(bd->note);
nicmd0b8d032020-01-27 08:53:13 +0000115 tmp = utf8_padcstr(key, keywidth + 1);
nicme6abe552021-04-12 09:36:12 +0000116 if (args_has(args, '1') && tc != NULL) {
117 status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
118 note);
119 } else
nicma1f6bd52020-02-15 15:08:08 +0000120 cmdq_print(item, "%s%s%s", prefix, tmp, note);
nicmd0b8d032020-01-27 08:53:13 +0000121 free(tmp);
nicma1f6bd52020-02-15 15:08:08 +0000122 free(note);
nicmd0b8d032020-01-27 08:53:13 +0000123
124 if (args_has(args, '1'))
125 break;
126 bd = key_bindings_next(table, bd);
127 }
128 return (found);
129}
130
131static char *
132cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
133{
134 char *s;
135
136 *prefix = options_get_number(global_s_options, "prefix");
137 if (!args_has(args, 'P')) {
138 if (*prefix != KEYC_NONE)
nicm292b3352020-05-16 16:35:13 +0000139 xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
nicmd0b8d032020-01-27 08:53:13 +0000140 else
141 s = xstrdup("");
142 } else
143 s = xstrdup(args_get(args, 'P'));
144 return (s);
145}
146
nicm068b8b02016-06-15 08:54:11 +0000147static enum cmd_retval
nicmb342bd02016-10-16 19:04:05 +0000148cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000149{
nicmc20eb0c2020-04-13 08:26:27 +0000150 struct args *args = cmd_get_args(self);
nicmbded7432015-04-20 15:34:56 +0000151 struct key_table *table;
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000152 struct key_binding *bd;
nicm5f32b7d2021-08-20 19:50:16 +0000153 const char *tablename, *r, *keystr;
nicmd0b8d032020-01-27 08:53:13 +0000154 char *key, *cp, *tmp, *start, *empty;
155 key_code prefix, only = KEYC_UNKNOWN;
156 int repeat, width, tablewidth, keywidth, found = 0;
nicm02253d12019-10-03 10:39:08 +0000157 size_t tmpsize, tmpused, cplen;
nicmf5bc8552014-10-20 22:44:30 +0000158
nicmc20eb0c2020-04-13 08:26:27 +0000159 if (cmd_get_entry(self) == &cmd_list_commands_entry)
nicmb342bd02016-10-16 19:04:05 +0000160 return (cmd_list_keys_commands(self, item));
Nicholas Marriotte43b6a22009-07-21 22:41:00 +0000161
nicm5f32b7d2021-08-20 19:50:16 +0000162 if ((keystr = args_string(args, 0)) != NULL) {
163 only = key_string_lookup_string(keystr);
nicmd0b8d032020-01-27 08:53:13 +0000164 if (only == KEYC_UNKNOWN) {
nicm5f32b7d2021-08-20 19:50:16 +0000165 cmdq_error(item, "invalid key: %s", keystr);
nicmd0b8d032020-01-27 08:53:13 +0000166 return (CMD_RETURN_ERROR);
167 }
nicmdfcc9f82021-04-13 16:00:47 +0000168 only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
nicmd0b8d032020-01-27 08:53:13 +0000169 }
170
nicmbded7432015-04-20 15:34:56 +0000171 tablename = args_get(args, 'T');
172 if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
nicmb342bd02016-10-16 19:04:05 +0000173 cmdq_error(item, "table %s doesn't exist", tablename);
nicmbded7432015-04-20 15:34:56 +0000174 return (CMD_RETURN_ERROR);
nicmd0b8d032020-01-27 08:53:13 +0000175 }
176
177 if (args_has(args, 'N')) {
178 if (tablename == NULL) {
179 start = cmd_list_keys_get_prefix(args, &prefix);
180 keywidth = cmd_list_keys_get_width("root", only);
181 if (prefix != KEYC_NONE) {
182 width = cmd_list_keys_get_width("prefix", only);
183 if (width == 0)
184 prefix = KEYC_NONE;
185 else if (width > keywidth)
186 keywidth = width;
187 }
188 empty = utf8_padcstr("", utf8_cstrwidth(start));
189
190 found = cmd_list_keys_print_notes(item, args, "root",
191 keywidth, only, empty);
192 if (prefix != KEYC_NONE) {
193 if (cmd_list_keys_print_notes(item, args,
194 "prefix", keywidth, only, start))
195 found = 1;
196 }
197 free(empty);
198 } else {
199 if (args_has(args, 'P'))
200 start = xstrdup(args_get(args, 'P'));
201 else
202 start = xstrdup("");
203 keywidth = cmd_list_keys_get_width(tablename, only);
204 found = cmd_list_keys_print_notes(item, args, tablename,
205 keywidth, only, start);
206
207 }
208 free(start);
209 goto out;
Nicholas Marriotte43b6a22009-07-21 22:41:00 +0000210 }
211
nicmbded7432015-04-20 15:34:56 +0000212 repeat = 0;
213 tablewidth = keywidth = 0;
nicme463e862021-08-20 17:50:42 +0000214 table = key_bindings_first_table();
nicm6048b0f2018-08-02 11:44:07 +0000215 while (table != NULL) {
216 if (tablename != NULL && strcmp(table->name, tablename) != 0) {
217 table = key_bindings_next_table(table);
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000218 continue;
nicm6048b0f2018-08-02 11:44:07 +0000219 }
220 bd = key_bindings_first(table);
221 while (bd != NULL) {
nicmd0b8d032020-01-27 08:53:13 +0000222 if (only != KEYC_UNKNOWN && bd->key != only) {
223 bd = key_bindings_next(table, bd);
224 continue;
225 }
nicm292b3352020-05-16 16:35:13 +0000226 key = args_escape(key_string_lookup_key(bd->key, 0));
Nicholas Marriott5e904762011-07-04 00:31:57 +0000227
nicmbba58872017-04-21 14:01:19 +0000228 if (bd->flags & KEY_BINDING_REPEAT)
nicmbded7432015-04-20 15:34:56 +0000229 repeat = 1;
Nicholas Marriott5e904762011-07-04 00:31:57 +0000230
nicma209ea32015-11-12 12:43:36 +0000231 width = utf8_cstrwidth(table->name);
nicmbded7432015-04-20 15:34:56 +0000232 if (width > tablewidth)
nicm69e0b832015-11-12 11:05:34 +0000233 tablewidth = width;
234 width = utf8_cstrwidth(key);
nicmbded7432015-04-20 15:34:56 +0000235 if (width > keywidth)
236 keywidth = width;
nicm6048b0f2018-08-02 11:44:07 +0000237
nicm27bfb562019-05-23 14:03:44 +0000238 free(key);
nicm6048b0f2018-08-02 11:44:07 +0000239 bd = key_bindings_next(table, bd);
nicmbded7432015-04-20 15:34:56 +0000240 }
nicm6048b0f2018-08-02 11:44:07 +0000241 table = key_bindings_next_table(table);
nicmbded7432015-04-20 15:34:56 +0000242 }
243
nicm02253d12019-10-03 10:39:08 +0000244 tmpsize = 256;
245 tmp = xmalloc(tmpsize);
nicm5f32b7d2021-08-20 19:50:16 +0000246
nicme463e862021-08-20 17:50:42 +0000247 table = key_bindings_first_table();
nicm6048b0f2018-08-02 11:44:07 +0000248 while (table != NULL) {
249 if (tablename != NULL && strcmp(table->name, tablename) != 0) {
250 table = key_bindings_next_table(table);
Nicholas Marriott05855392009-11-05 12:04:50 +0000251 continue;
nicm6048b0f2018-08-02 11:44:07 +0000252 }
253 bd = key_bindings_first(table);
254 while (bd != NULL) {
nicmd0b8d032020-01-27 08:53:13 +0000255 if (only != KEYC_UNKNOWN && bd->key != only) {
256 bd = key_bindings_next(table, bd);
257 continue;
258 }
259 found = 1;
nicm292b3352020-05-16 16:35:13 +0000260 key = args_escape(key_string_lookup_key(bd->key, 0));
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000261
nicmbded7432015-04-20 15:34:56 +0000262 if (!repeat)
263 r = "";
nicmbba58872017-04-21 14:01:19 +0000264 else if (bd->flags & KEY_BINDING_REPEAT)
nicmbded7432015-04-20 15:34:56 +0000265 r = "-r ";
266 else
267 r = " ";
nicm02253d12019-10-03 10:39:08 +0000268 tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
nicma209ea32015-11-12 12:43:36 +0000269
270 cp = utf8_padcstr(table->name, tablewidth);
nicm02253d12019-10-03 10:39:08 +0000271 cplen = strlen(cp) + 1;
nicmbbe8ebf2019-10-14 09:16:48 +0000272 while (tmpused + cplen + 1 >= tmpsize) {
nicm02253d12019-10-03 10:39:08 +0000273 tmpsize *= 2;
274 tmp = xrealloc(tmp, tmpsize);
275 }
nicm886fdb12020-04-09 13:56:46 +0000276 strlcat(tmp, cp, tmpsize);
nicm02253d12019-10-03 10:39:08 +0000277 tmpused = strlcat(tmp, " ", tmpsize);
nicma209ea32015-11-12 12:43:36 +0000278 free(cp);
279
280 cp = utf8_padcstr(key, keywidth);
nicm02253d12019-10-03 10:39:08 +0000281 cplen = strlen(cp) + 1;
282 while (tmpused + cplen + 1 >= tmpsize) {
283 tmpsize *= 2;
284 tmp = xrealloc(tmp, tmpsize);
285 }
nicm886fdb12020-04-09 13:56:46 +0000286 strlcat(tmp, cp, tmpsize);
nicm02253d12019-10-03 10:39:08 +0000287 tmpused = strlcat(tmp, " ", tmpsize);
nicma209ea32015-11-12 12:43:36 +0000288 free(cp);
289
nicm27bfb562019-05-23 14:03:44 +0000290 cp = cmd_list_print(bd->cmdlist, 1);
nicm02253d12019-10-03 10:39:08 +0000291 cplen = strlen(cp);
292 while (tmpused + cplen + 1 >= tmpsize) {
293 tmpsize *= 2;
294 tmp = xrealloc(tmp, tmpsize);
295 }
296 strlcat(tmp, cp, tmpsize);
nicm6a2ca342015-11-27 15:06:43 +0000297 free(cp);
nicmbded7432015-04-20 15:34:56 +0000298
nicmb342bd02016-10-16 19:04:05 +0000299 cmdq_print(item, "bind-key %s", tmp);
nicm27bfb562019-05-23 14:03:44 +0000300
301 free(key);
nicm6048b0f2018-08-02 11:44:07 +0000302 bd = key_bindings_next(table, bd);
nicmbded7432015-04-20 15:34:56 +0000303 }
nicm6048b0f2018-08-02 11:44:07 +0000304 table = key_bindings_next_table(table);
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000305 }
306
nicm02253d12019-10-03 10:39:08 +0000307 free(tmp);
308
nicmd0b8d032020-01-27 08:53:13 +0000309out:
310 if (only != KEYC_UNKNOWN && !found) {
nicm5f32b7d2021-08-20 19:50:16 +0000311 cmdq_error(item, "unknown key: %s", args_string(args, 0));
nicmd0b8d032020-01-27 08:53:13 +0000312 return (CMD_RETURN_ERROR);
313 }
Nicholas Marriottede83122012-07-11 07:10:15 +0000314 return (CMD_RETURN_NORMAL);
Nicholas Marriott35876ea2009-06-01 22:58:49 +0000315}
Nicholas Marriott86785002009-07-28 07:03:32 +0000316
nicm068b8b02016-06-15 08:54:11 +0000317static enum cmd_retval
nicmb342bd02016-10-16 19:04:05 +0000318cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
nicmf5bc8552014-10-20 22:44:30 +0000319{
nicmc20eb0c2020-04-13 08:26:27 +0000320 struct args *args = cmd_get_args(self);
nicmf5bc8552014-10-20 22:44:30 +0000321 const struct cmd_entry **entryp;
nicm900f6fc2014-10-20 23:27:14 +0000322 const struct cmd_entry *entry;
nicm068b8b02016-06-15 08:54:11 +0000323 struct format_tree *ft;
nicm5f32b7d2021-08-20 19:50:16 +0000324 const char *template, *s, *command;
nicm068b8b02016-06-15 08:54:11 +0000325 char *line;
326
327 if ((template = args_get(args, 'F')) == NULL) {
328 template = "#{command_list_name}"
329 "#{?command_list_alias, (#{command_list_alias}),} "
330 "#{command_list_usage}";
331 }
332
nicm04cdd032020-04-13 10:59:58 +0000333 ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
nicm068b8b02016-06-15 08:54:11 +0000334 format_defaults(ft, NULL, NULL, NULL, NULL);
nicmf5bc8552014-10-20 22:44:30 +0000335
nicm5f32b7d2021-08-20 19:50:16 +0000336 command = args_string(args, 0);
nicmf5bc8552014-10-20 22:44:30 +0000337 for (entryp = cmd_table; *entryp != NULL; entryp++) {
338 entry = *entryp;
nicmfccce692020-04-05 08:40:31 +0000339 if (command != NULL &&
340 (strcmp(entry->name, command) != 0 &&
341 (entry->alias == NULL ||
342 strcmp(entry->alias, command) != 0)))
343 continue;
nicm068b8b02016-06-15 08:54:11 +0000344
345 format_add(ft, "command_list_name", "%s", entry->name);
nicmb342bd02016-10-16 19:04:05 +0000346 if (entry->alias != NULL)
347 s = entry->alias;
348 else
349 s = "";
350 format_add(ft, "command_list_alias", "%s", s);
351 if (entry->usage != NULL)
352 s = entry->usage;
353 else
354 s = "";
355 format_add(ft, "command_list_usage", "%s", s);
nicm068b8b02016-06-15 08:54:11 +0000356
357 line = format_expand(ft, template);
358 if (*line != '\0')
nicmb342bd02016-10-16 19:04:05 +0000359 cmdq_print(item, "%s", line);
nicm068b8b02016-06-15 08:54:11 +0000360 free(line);
nicmf5bc8552014-10-20 22:44:30 +0000361 }
362
nicm068b8b02016-06-15 08:54:11 +0000363 format_free(ft);
nicmf5bc8552014-10-20 22:44:30 +0000364 return (CMD_RETURN_NORMAL);
365}