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