1*b6124414Snicm /* $OpenBSD: cmd-list-keys.c,v 1.68 2025/01/27 09:16:05 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 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 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 * 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 14868e0a7f2Snicm cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) 149311827fbSnicm { 15090d7ba38Snicm struct args *args = cmd_get_args(self); 1511e415b51Snicm 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 3001e415b51Snicm if (args_has(args, '1') && tc != NULL) { 3011e415b51Snicm status_message_set(tc, -1, 1, 0, "bind-key %s", 3021e415b51Snicm tmp); 3031e415b51Snicm } else 30468e0a7f2Snicm cmdq_print(item, "bind-key %s", tmp); 3055c131106Snicm free(key); 3061e415b51Snicm 3071e415b51Snicm if (args_has(args, '1')) 3081e415b51Snicm 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 324*b6124414Snicm static void 325*b6124414Snicm cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft, 326*b6124414Snicm const char *template, struct cmdq_item *item) 32733157bb3Snicm { 328*b6124414Snicm const char *s; 329de9718b4Snicm char *line; 330de9718b4Snicm 331de9718b4Snicm format_add(ft, "command_list_name", "%s", entry->name); 33268e0a7f2Snicm if (entry->alias != NULL) 33368e0a7f2Snicm s = entry->alias; 33468e0a7f2Snicm else 33568e0a7f2Snicm s = ""; 33668e0a7f2Snicm format_add(ft, "command_list_alias", "%s", s); 33768e0a7f2Snicm if (entry->usage != NULL) 33868e0a7f2Snicm s = entry->usage; 33968e0a7f2Snicm else 34068e0a7f2Snicm s = ""; 34168e0a7f2Snicm format_add(ft, "command_list_usage", "%s", s); 34233157bb3Snicm 343de9718b4Snicm line = format_expand(ft, template); 344de9718b4Snicm if (*line != '\0') 34568e0a7f2Snicm cmdq_print(item, "%s", line); 346de9718b4Snicm free(line); 347de9718b4Snicm } 348de9718b4Snicm 349*b6124414Snicm static enum cmd_retval 350*b6124414Snicm cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) 351*b6124414Snicm { 352*b6124414Snicm struct args *args = cmd_get_args(self); 353*b6124414Snicm const struct cmd_entry **entryp; 354*b6124414Snicm const struct cmd_entry *entry; 355*b6124414Snicm struct format_tree *ft; 356*b6124414Snicm const char *template, *command; 357*b6124414Snicm char *cause; 358*b6124414Snicm 359*b6124414Snicm if ((template = args_get(args, 'F')) == NULL) { 360*b6124414Snicm template = "#{command_list_name}" 361*b6124414Snicm "#{?command_list_alias, (#{command_list_alias}),} " 362*b6124414Snicm "#{command_list_usage}"; 363*b6124414Snicm } 364*b6124414Snicm 365*b6124414Snicm ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); 366*b6124414Snicm format_defaults(ft, NULL, NULL, NULL, NULL); 367*b6124414Snicm 368*b6124414Snicm command = args_string(args, 0); 369*b6124414Snicm if (command == NULL) { 370*b6124414Snicm for (entryp = cmd_table; *entryp != NULL; entryp++) 371*b6124414Snicm cmd_list_single_command(*entryp, ft, template, item); 372*b6124414Snicm } else { 373*b6124414Snicm entry = cmd_find(command, &cause); 374*b6124414Snicm if (entry != NULL) 375*b6124414Snicm cmd_list_single_command(entry, ft, template, item); 376*b6124414Snicm else { 377*b6124414Snicm cmdq_error(item, "%s", cause); 378*b6124414Snicm free(cause); 379*b6124414Snicm format_free(ft); 380*b6124414Snicm return (CMD_RETURN_ERROR); 381*b6124414Snicm } 382*b6124414Snicm } 383*b6124414Snicm 384de9718b4Snicm format_free(ft); 38533157bb3Snicm return (CMD_RETURN_NORMAL); 38633157bb3Snicm } 387