Home | History | Annotate | Line # | Download | only in dist
cmd-list-keys.c revision 1.1.1.16
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) 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 {
     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) ||
     71 		    bd->note == NULL ||
     72 		    *bd->note == '\0') {
     73 			bd = key_bindings_next(table, bd);
     74 			continue;
     75 		}
     76 		width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
     77 		if (width > keywidth)
     78 			keywidth = width;
     79 
     80 		bd = key_bindings_next(table, bd);
     81 	}
     82 	return (keywidth);
     83 }
     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 {
     89 	struct client		*tc = cmdq_get_target_client(item);
     90 	struct key_table	*table;
     91 	struct key_binding	*bd;
     92 	const char		*key;
     93 	char			*tmp, *note;
     94 	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) ||
    103 		    ((bd->note == NULL || *bd->note == '\0') &&
    104 		    !args_has(args, 'a'))) {
    105 			bd = key_bindings_next(table, bd);
    106 			continue;
    107 		}
    108 		found = 1;
    109 		key = key_string_lookup_key(bd->key, 0);
    110 
    111 		if (bd->note == NULL || *bd->note == '\0')
    112 			note = cmd_list_print(bd->cmdlist, 1);
    113 		else
    114 			note = xstrdup(bd->note);
    115 		tmp = utf8_padcstr(key, keywidth + 1);
    116 		if (args_has(args, '1') && tc != NULL) {
    117 			status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
    118 			    tmp, note);
    119 		} else
    120 			cmdq_print(item, "%s%s%s", prefix, tmp, note);
    121 		free(tmp);
    122 		free(note);
    123 
    124 		if (args_has(args, '1'))
    125 			break;
    126 		bd = key_bindings_next(table, bd);
    127 	}
    128 	return (found);
    129 }
    130 
    131 static char *
    132 cmd_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)
    139 			xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
    140 		else
    141 			s = xstrdup("");
    142 	} else
    143 		s = xstrdup(args_get(args, 'P'));
    144 	return (s);
    145 }
    146 
    147 static enum cmd_retval
    148 cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
    149 {
    150 	struct args		*args = cmd_get_args(self);
    151 	struct client		*tc = cmdq_get_target_client(item);
    152 	struct key_table	*table;
    153 	struct key_binding	*bd;
    154 	const char		*tablename, *r, *keystr;
    155 	char			*key, *cp, *tmp, *start, *empty;
    156 	key_code		 prefix, only = KEYC_UNKNOWN;
    157 	int			 repeat, width, tablewidth, keywidth, found = 0;
    158 	size_t			 tmpsize, tmpused, cplen;
    159 
    160 	if (cmd_get_entry(self) == &cmd_list_commands_entry)
    161 		return (cmd_list_keys_commands(self, item));
    162 
    163 	if ((keystr = args_string(args, 0)) != NULL) {
    164 		only = key_string_lookup_string(keystr);
    165 		if (only == KEYC_UNKNOWN) {
    166 			cmdq_error(item, "invalid key: %s", keystr);
    167 			return (CMD_RETURN_ERROR);
    168 		}
    169 		only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
    170 	}
    171 
    172 	tablename = args_get(args, 'T');
    173 	if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
    174 		cmdq_error(item, "table %s doesn't exist", tablename);
    175 		return (CMD_RETURN_ERROR);
    176 	}
    177 
    178 	if (args_has(args, 'N')) {
    179 		if (tablename == NULL) {
    180 			start = cmd_list_keys_get_prefix(args, &prefix);
    181 			keywidth = cmd_list_keys_get_width("root", only);
    182 			if (prefix != KEYC_NONE) {
    183 				width = cmd_list_keys_get_width("prefix", only);
    184 				if (width == 0)
    185 					prefix = KEYC_NONE;
    186 				else if (width > keywidth)
    187 					keywidth = width;
    188 			}
    189 			empty = utf8_padcstr("", utf8_cstrwidth(start));
    190 
    191 			found = cmd_list_keys_print_notes(item, args, "root",
    192 			    keywidth, only, empty);
    193 			if (prefix != KEYC_NONE) {
    194 				if (cmd_list_keys_print_notes(item, args,
    195 				    "prefix", keywidth, only, start))
    196 					found = 1;
    197 			}
    198 			free(empty);
    199 		} else {
    200 			if (args_has(args, 'P'))
    201 				start = xstrdup(args_get(args, 'P'));
    202 			else
    203 				start = xstrdup("");
    204 			keywidth = cmd_list_keys_get_width(tablename, only);
    205 			found = cmd_list_keys_print_notes(item, args, tablename,
    206 			    keywidth, only, start);
    207 
    208 		}
    209 		free(start);
    210 		goto out;
    211 	}
    212 
    213 	repeat = 0;
    214 	tablewidth = keywidth = 0;
    215 	table = key_bindings_first_table();
    216 	while (table != NULL) {
    217 		if (tablename != NULL && strcmp(table->name, tablename) != 0) {
    218 			table = key_bindings_next_table(table);
    219 			continue;
    220 		}
    221 		bd = key_bindings_first(table);
    222 		while (bd != NULL) {
    223 			if (only != KEYC_UNKNOWN && bd->key != only) {
    224 				bd = key_bindings_next(table, bd);
    225 				continue;
    226 			}
    227 			key = args_escape(key_string_lookup_key(bd->key, 0));
    228 
    229 			if (bd->flags & KEY_BINDING_REPEAT)
    230 				repeat = 1;
    231 
    232 			width = utf8_cstrwidth(table->name);
    233 			if (width > tablewidth)
    234 				tablewidth = width;
    235 			width = utf8_cstrwidth(key);
    236 			if (width > keywidth)
    237 				keywidth = width;
    238 
    239 			free(key);
    240 			bd = key_bindings_next(table, bd);
    241 		}
    242 		table = key_bindings_next_table(table);
    243 	}
    244 
    245 	tmpsize = 256;
    246 	tmp = xmalloc(tmpsize);
    247 
    248 	table = key_bindings_first_table();
    249 	while (table != NULL) {
    250 		if (tablename != NULL && strcmp(table->name, tablename) != 0) {
    251 			table = key_bindings_next_table(table);
    252 			continue;
    253 		}
    254 		bd = key_bindings_first(table);
    255 		while (bd != NULL) {
    256 			if (only != KEYC_UNKNOWN && bd->key != only) {
    257 				bd = key_bindings_next(table, bd);
    258 				continue;
    259 			}
    260 			found = 1;
    261 			key = args_escape(key_string_lookup_key(bd->key, 0));
    262 
    263 			if (!repeat)
    264 				r = "";
    265 			else if (bd->flags & KEY_BINDING_REPEAT)
    266 				r = "-r ";
    267 			else
    268 				r = "   ";
    269 			tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
    270 
    271 			cp = utf8_padcstr(table->name, tablewidth);
    272 			cplen = strlen(cp) + 1;
    273 			while (tmpused + cplen + 1 >= tmpsize) {
    274 				tmpsize *= 2;
    275 				tmp = xrealloc(tmp, tmpsize);
    276 			}
    277 			strlcat(tmp, cp, tmpsize);
    278 			tmpused = strlcat(tmp, " ", tmpsize);
    279 			free(cp);
    280 
    281 			cp = utf8_padcstr(key, keywidth);
    282 			cplen = strlen(cp) + 1;
    283 			while (tmpused + cplen + 1 >= tmpsize) {
    284 				tmpsize *= 2;
    285 				tmp = xrealloc(tmp, tmpsize);
    286 			}
    287 			strlcat(tmp, cp, tmpsize);
    288 			tmpused = strlcat(tmp, " ", tmpsize);
    289 			free(cp);
    290 
    291 			cp = cmd_list_print(bd->cmdlist, 1);
    292 			cplen = strlen(cp);
    293 			while (tmpused + cplen + 1 >= tmpsize) {
    294 				tmpsize *= 2;
    295 				tmp = xrealloc(tmp, tmpsize);
    296 			}
    297 			strlcat(tmp, cp, tmpsize);
    298 			free(cp);
    299 
    300 			if (args_has(args, '1') && tc != NULL) {
    301 				status_message_set(tc, -1, 1, 0, 0,
    302 				    "bind-key %s", tmp);
    303 			} else
    304 				cmdq_print(item, "bind-key %s", tmp);
    305 			free(key);
    306 
    307 			if (args_has(args, '1'))
    308 				break;
    309 			bd = key_bindings_next(table, bd);
    310 		}
    311 		table = key_bindings_next_table(table);
    312 	}
    313 
    314 	free(tmp);
    315 
    316 out:
    317 	if (only != KEYC_UNKNOWN && !found) {
    318 		cmdq_error(item, "unknown key: %s", args_string(args, 0));
    319 		return (CMD_RETURN_ERROR);
    320 	}
    321 	return (CMD_RETURN_NORMAL);
    322 }
    323 
    324 static void
    325 cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
    326     const char *template, struct cmdq_item *item)
    327 {
    328 	const char	*s;
    329 	char		*line;
    330 
    331 	format_add(ft, "command_list_name", "%s", entry->name);
    332 	if (entry->alias != NULL)
    333 		s = entry->alias;
    334 	else
    335 		s = "";
    336 	format_add(ft, "command_list_alias", "%s", s);
    337 	if (entry->usage != NULL)
    338 		s = entry->usage;
    339 	else
    340 		s = "";
    341 	format_add(ft, "command_list_usage", "%s", s);
    342 
    343 	line = format_expand(ft, template);
    344 	if (*line != '\0')
    345 		cmdq_print(item, "%s", line);
    346 	free(line);
    347 }
    348 
    349 static enum cmd_retval
    350 cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
    351 {
    352 	struct args		 *args = cmd_get_args(self);
    353 	const struct cmd_entry	**entryp;
    354 	const struct cmd_entry	 *entry;
    355 	struct format_tree	 *ft;
    356 	const char		 *template,  *command;
    357 	char			 *cause;
    358 
    359 	if ((template = args_get(args, 'F')) == NULL) {
    360 		template = "#{command_list_name}"
    361 		    "#{?command_list_alias, (#{command_list_alias}),} "
    362 		    "#{command_list_usage}";
    363 	}
    364 
    365 	ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
    366 	format_defaults(ft, NULL, NULL, NULL, NULL);
    367 
    368 	command = args_string(args, 0);
    369 	if (command == NULL) {
    370 		for (entryp = cmd_table; *entryp != NULL; entryp++)
    371 			cmd_list_single_command(*entryp, ft, template, item);
    372 	} else {
    373 		entry = cmd_find(command, &cause);
    374 		if (entry != NULL)
    375 			cmd_list_single_command(entry, ft, template, item);
    376 		else {
    377 			cmdq_error(item, "%s", cause);
    378 			free(cause);
    379 			format_free(ft);
    380 			return (CMD_RETURN_ERROR);
    381 		}
    382 	}
    383 
    384 	format_free(ft);
    385 	return (CMD_RETURN_NORMAL);
    386 }
    387