4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
30 static enum cmd_retval
cmd_list_keys_exec(struct cmd
*, struct cmdq_item
*);
32 static enum cmd_retval
cmd_list_keys_commands(struct cmd
*,
35 const struct cmd_entry cmd_list_keys_entry
= {
39 .args
= { "1aNP:T:", 0, 1, NULL
},
40 .usage
= "[-1aN] [-P prefix-string] [-T key-table] [key]",
42 .flags
= CMD_STARTSERVER
|CMD_AFTERHOOK
,
43 .exec
= cmd_list_keys_exec
46 const struct cmd_entry cmd_list_commands_entry
= {
47 .name
= "list-commands",
50 .args
= { "F:", 0, 1, NULL
},
51 .usage
= "[-F format] [command]",
53 .flags
= CMD_STARTSERVER
|CMD_AFTERHOOK
,
54 .exec
= cmd_list_keys_exec
58 cmd_list_keys_get_width(const char *tablename
, key_code only
)
60 struct key_table
*table
;
61 struct key_binding
*bd
;
62 u_int width
, keywidth
= 0;
64 table
= key_bindings_get_table(tablename
, 0);
67 bd
= key_bindings_first(table
);
69 if ((only
!= KEYC_UNKNOWN
&& bd
->key
!= only
) ||
70 KEYC_IS_MOUSE(bd
->key
) ||
73 bd
= key_bindings_next(table
, bd
);
76 width
= utf8_cstrwidth(key_string_lookup_key(bd
->key
, 0));
80 bd
= key_bindings_next(table
, bd
);
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
)
89 struct client
*tc
= cmdq_get_target_client(item
);
90 struct key_table
*table
;
91 struct key_binding
*bd
;
96 table
= key_bindings_get_table(tablename
, 0);
99 bd
= key_bindings_first(table
);
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
);
109 key
= key_string_lookup_key(bd
->key
, 0);
111 if (bd
->note
== NULL
|| *bd
->note
== '\0')
112 note
= cmd_list_print(bd
->cmdlist
, 1);
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
,
120 cmdq_print(item
, "%s%s%s", prefix
, tmp
, note
);
124 if (args_has(args
, '1'))
126 bd
= key_bindings_next(table
, bd
);
132 cmd_list_keys_get_prefix(struct args
*args
, key_code
*prefix
)
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));
143 s
= xstrdup(args_get(args
, 'P'));
147 static enum cmd_retval
148 cmd_list_keys_exec(struct cmd
*self
, struct cmdq_item
*item
)
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
;
160 if (cmd_get_entry(self
) == &cmd_list_commands_entry
)
161 return (cmd_list_keys_commands(self
, item
));
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
);
169 only
&= (KEYC_MASK_KEY
|KEYC_MASK_MODIFIERS
);
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
);
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
);
186 else if (width
> keywidth
)
189 empty
= utf8_padcstr("", utf8_cstrwidth(start
));
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
))
200 if (args_has(args
, 'P'))
201 start
= xstrdup(args_get(args
, 'P'));
204 keywidth
= cmd_list_keys_get_width(tablename
, only
);
205 found
= cmd_list_keys_print_notes(item
, args
, tablename
,
206 keywidth
, only
, start
);
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
);
221 bd
= key_bindings_first(table
);
223 if (only
!= KEYC_UNKNOWN
&& bd
->key
!= only
) {
224 bd
= key_bindings_next(table
, bd
);
227 key
= args_escape(key_string_lookup_key(bd
->key
, 0));
229 if (bd
->flags
& KEY_BINDING_REPEAT
)
232 width
= utf8_cstrwidth(table
->name
);
233 if (width
> tablewidth
)
235 width
= utf8_cstrwidth(key
);
236 if (width
> keywidth
)
240 bd
= key_bindings_next(table
, bd
);
242 table
= key_bindings_next_table(table
);
246 tmp
= xmalloc(tmpsize
);
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
);
254 bd
= key_bindings_first(table
);
256 if (only
!= KEYC_UNKNOWN
&& bd
->key
!= only
) {
257 bd
= key_bindings_next(table
, bd
);
261 key
= args_escape(key_string_lookup_key(bd
->key
, 0));
265 else if (bd
->flags
& KEY_BINDING_REPEAT
)
269 tmpused
= xsnprintf(tmp
, tmpsize
, "%s-T ", r
);
271 cp
= utf8_padcstr(table
->name
, tablewidth
);
272 cplen
= strlen(cp
) + 1;
273 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
275 tmp
= xrealloc(tmp
, tmpsize
);
277 strlcat(tmp
, cp
, tmpsize
);
278 tmpused
= strlcat(tmp
, " ", tmpsize
);
281 cp
= utf8_padcstr(key
, keywidth
);
282 cplen
= strlen(cp
) + 1;
283 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
285 tmp
= xrealloc(tmp
, tmpsize
);
287 strlcat(tmp
, cp
, tmpsize
);
288 tmpused
= strlcat(tmp
, " ", tmpsize
);
291 cp
= cmd_list_print(bd
->cmdlist
, 1);
293 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
295 tmp
= xrealloc(tmp
, tmpsize
);
297 strlcat(tmp
, cp
, tmpsize
);
300 if (args_has(args
, '1') && tc
!= NULL
) {
301 status_message_set(tc
, -1, 1, 0, "bind-key %s",
304 cmdq_print(item
, "bind-key %s", tmp
);
307 if (args_has(args
, '1'))
309 bd
= key_bindings_next(table
, bd
);
311 table
= key_bindings_next_table(table
);
317 if (only
!= KEYC_UNKNOWN
&& !found
) {
318 cmdq_error(item
, "unknown key: %s", args_string(args
, 0));
319 return (CMD_RETURN_ERROR
);
321 return (CMD_RETURN_NORMAL
);
324 static enum cmd_retval
325 cmd_list_keys_commands(struct cmd
*self
, struct cmdq_item
*item
)
327 struct args
*args
= cmd_get_args(self
);
328 const struct cmd_entry
**entryp
;
329 const struct cmd_entry
*entry
;
330 struct format_tree
*ft
;
331 const char *template, *s
, *command
;
334 if ((template = args_get(args
, 'F')) == NULL
) {
335 template = "#{command_list_name}"
336 "#{?command_list_alias, (#{command_list_alias}),} "
337 "#{command_list_usage}";
340 ft
= format_create(cmdq_get_client(item
), item
, FORMAT_NONE
, 0);
341 format_defaults(ft
, NULL
, NULL
, NULL
, NULL
);
343 command
= args_string(args
, 0);
344 for (entryp
= cmd_table
; *entryp
!= NULL
; entryp
++) {
346 if (command
!= NULL
&&
347 (strcmp(entry
->name
, command
) != 0 &&
348 (entry
->alias
== NULL
||
349 strcmp(entry
->alias
, command
) != 0)))
352 format_add(ft
, "command_list_name", "%s", entry
->name
);
353 if (entry
->alias
!= NULL
)
357 format_add(ft
, "command_list_alias", "%s", s
);
358 if (entry
->usage
!= NULL
)
362 format_add(ft
, "command_list_usage", "%s", s
);
364 line
= format_expand(ft
, template);
366 cmdq_print(item
, "%s", line
);
371 return (CMD_RETURN_NORMAL
);