Home | History | Annotate | Line # | Download | only in dist
cmd-list-keys.c revision 1.1.1.14
      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, "%s%s%s", prefix, tmp,
    118 			    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 key_table	*table;
    152 	struct key_binding	*bd;
    153 	const char		*tablename, *r, *keystr;
    154 	char			*key, *cp, *tmp, *start, *empty;
    155 	key_code		 prefix, only = KEYC_UNKNOWN;
    156 	int			 repeat, width, tablewidth, keywidth, found = 0;
    157 	size_t			 tmpsize, tmpused, cplen;
    158 
    159 	if (cmd_get_entry(self) == &cmd_list_commands_entry)
    160 		return (cmd_list_keys_commands(self, item));
    161 
    162 	if ((keystr = args_string(args, 0)) != NULL) {
    163 		only = key_string_lookup_string(keystr);
    164 		if (only == KEYC_UNKNOWN) {
    165 			cmdq_error(item, "invalid key: %s", keystr);
    166 			return (CMD_RETURN_ERROR);
    167 		}
    168 		only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
    169 	}
    170 
    171 	tablename = args_get(args, 'T');
    172 	if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
    173 		cmdq_error(item, "table %s doesn't exist", tablename);
    174 		return (CMD_RETURN_ERROR);
    175 	}
    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;
    210 	}
    211 
    212 	repeat = 0;
    213 	tablewidth = keywidth = 0;
    214 	table = key_bindings_first_table();
    215 	while (table != NULL) {
    216 		if (tablename != NULL && strcmp(table->name, tablename) != 0) {
    217 			table = key_bindings_next_table(table);
    218 			continue;
    219 		}
    220 		bd = key_bindings_first(table);
    221 		while (bd != NULL) {
    222 			if (only != KEYC_UNKNOWN && bd->key != only) {
    223 				bd = key_bindings_next(table, bd);
    224 				continue;
    225 			}
    226 			key = args_escape(key_string_lookup_key(bd->key, 0));
    227 
    228 			if (bd->flags & KEY_BINDING_REPEAT)
    229 				repeat = 1;
    230 
    231 			width = utf8_cstrwidth(table->name);
    232 			if (width > tablewidth)
    233 				tablewidth = width;
    234 			width = utf8_cstrwidth(key);
    235 			if (width > keywidth)
    236 				keywidth = width;
    237 
    238 			free(key);
    239 			bd = key_bindings_next(table, bd);
    240 		}
    241 		table = key_bindings_next_table(table);
    242 	}
    243 
    244 	tmpsize = 256;
    245 	tmp = xmalloc(tmpsize);
    246 
    247 	table = key_bindings_first_table();
    248 	while (table != NULL) {
    249 		if (tablename != NULL && strcmp(table->name, tablename) != 0) {
    250 			table = key_bindings_next_table(table);
    251 			continue;
    252 		}
    253 		bd = key_bindings_first(table);
    254 		while (bd != NULL) {
    255 			if (only != KEYC_UNKNOWN && bd->key != only) {
    256 				bd = key_bindings_next(table, bd);
    257 				continue;
    258 			}
    259 			found = 1;
    260 			key = args_escape(key_string_lookup_key(bd->key, 0));
    261 
    262 			if (!repeat)
    263 				r = "";
    264 			else if (bd->flags & KEY_BINDING_REPEAT)
    265 				r = "-r ";
    266 			else
    267 				r = "   ";
    268 			tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
    269 
    270 			cp = utf8_padcstr(table->name, tablewidth);
    271 			cplen = strlen(cp) + 1;
    272 			while (tmpused + cplen + 1 >= tmpsize) {
    273 				tmpsize *= 2;
    274 				tmp = xrealloc(tmp, tmpsize);
    275 			}
    276 			strlcat(tmp, cp, tmpsize);
    277 			tmpused = strlcat(tmp, " ", tmpsize);
    278 			free(cp);
    279 
    280 			cp = utf8_padcstr(key, keywidth);
    281 			cplen = strlen(cp) + 1;
    282 			while (tmpused + cplen + 1 >= tmpsize) {
    283 				tmpsize *= 2;
    284 				tmp = xrealloc(tmp, tmpsize);
    285 			}
    286 			strlcat(tmp, cp, tmpsize);
    287 			tmpused = strlcat(tmp, " ", tmpsize);
    288 			free(cp);
    289 
    290 			cp = cmd_list_print(bd->cmdlist, 1);
    291 			cplen = strlen(cp);
    292 			while (tmpused + cplen + 1 >= tmpsize) {
    293 				tmpsize *= 2;
    294 				tmp = xrealloc(tmp, tmpsize);
    295 			}
    296 			strlcat(tmp, cp, tmpsize);
    297 			free(cp);
    298 
    299 			cmdq_print(item, "bind-key %s", tmp);
    300 
    301 			free(key);
    302 			bd = key_bindings_next(table, bd);
    303 		}
    304 		table = key_bindings_next_table(table);
    305 	}
    306 
    307 	free(tmp);
    308 
    309 out:
    310 	if (only != KEYC_UNKNOWN && !found) {
    311 		cmdq_error(item, "unknown key: %s", args_string(args, 0));
    312 		return (CMD_RETURN_ERROR);
    313 	}
    314 	return (CMD_RETURN_NORMAL);
    315 }
    316 
    317 static enum cmd_retval
    318 cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
    319 {
    320 	struct args		 *args = cmd_get_args(self);
    321 	const struct cmd_entry	**entryp;
    322 	const struct cmd_entry	 *entry;
    323 	struct format_tree	 *ft;
    324 	const char		 *template, *s, *command;
    325 	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 
    333 	ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
    334 	format_defaults(ft, NULL, NULL, NULL, NULL);
    335 
    336 	command = args_string(args, 0);
    337 	for (entryp = cmd_table; *entryp != NULL; entryp++) {
    338 		entry = *entryp;
    339 		if (command != NULL &&
    340 		    (strcmp(entry->name, command) != 0 &&
    341 		    (entry->alias == NULL ||
    342 		    strcmp(entry->alias, command) != 0)))
    343 		    continue;
    344 
    345 		format_add(ft, "command_list_name", "%s", entry->name);
    346 		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);
    356 
    357 		line = format_expand(ft, template);
    358 		if (*line != '\0')
    359 			cmdq_print(item, "%s", line);
    360 		free(line);
    361 	}
    362 
    363 	format_free(ft);
    364 	return (CMD_RETURN_NORMAL);
    365 }
    366