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 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
;
159 if (cmd_get_entry(self
) == &cmd_list_commands_entry
)
160 return (cmd_list_keys_commands(self
, item
));
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
);
168 only
&= (KEYC_MASK_KEY
|KEYC_MASK_MODIFIERS
);
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
);
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
);
185 else if (width
> keywidth
)
188 empty
= utf8_padcstr("", utf8_cstrwidth(start
));
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
))
199 if (args_has(args
, 'P'))
200 start
= xstrdup(args_get(args
, 'P'));
203 keywidth
= cmd_list_keys_get_width(tablename
, only
);
204 found
= cmd_list_keys_print_notes(item
, args
, tablename
,
205 keywidth
, only
, start
);
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
);
220 bd
= key_bindings_first(table
);
222 if (only
!= KEYC_UNKNOWN
&& bd
->key
!= only
) {
223 bd
= key_bindings_next(table
, bd
);
226 key
= args_escape(key_string_lookup_key(bd
->key
, 0));
228 if (bd
->flags
& KEY_BINDING_REPEAT
)
231 width
= utf8_cstrwidth(table
->name
);
232 if (width
> tablewidth
)
234 width
= utf8_cstrwidth(key
);
235 if (width
> keywidth
)
239 bd
= key_bindings_next(table
, bd
);
241 table
= key_bindings_next_table(table
);
245 tmp
= xmalloc(tmpsize
);
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
);
253 bd
= key_bindings_first(table
);
255 if (only
!= KEYC_UNKNOWN
&& bd
->key
!= only
) {
256 bd
= key_bindings_next(table
, bd
);
260 key
= args_escape(key_string_lookup_key(bd
->key
, 0));
264 else if (bd
->flags
& KEY_BINDING_REPEAT
)
268 tmpused
= xsnprintf(tmp
, tmpsize
, "%s-T ", r
);
270 cp
= utf8_padcstr(table
->name
, tablewidth
);
271 cplen
= strlen(cp
) + 1;
272 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
274 tmp
= xrealloc(tmp
, tmpsize
);
276 strlcat(tmp
, cp
, tmpsize
);
277 tmpused
= strlcat(tmp
, " ", tmpsize
);
280 cp
= utf8_padcstr(key
, keywidth
);
281 cplen
= strlen(cp
) + 1;
282 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
284 tmp
= xrealloc(tmp
, tmpsize
);
286 strlcat(tmp
, cp
, tmpsize
);
287 tmpused
= strlcat(tmp
, " ", tmpsize
);
290 cp
= cmd_list_print(bd
->cmdlist
, 1);
292 while (tmpused
+ cplen
+ 1 >= tmpsize
) {
294 tmp
= xrealloc(tmp
, tmpsize
);
296 strlcat(tmp
, cp
, tmpsize
);
299 cmdq_print(item
, "bind-key %s", tmp
);
302 bd
= key_bindings_next(table
, bd
);
304 table
= key_bindings_next_table(table
);
310 if (only
!= KEYC_UNKNOWN
&& !found
) {
311 cmdq_error(item
, "unknown key: %s", args_string(args
, 0));
312 return (CMD_RETURN_ERROR
);
314 return (CMD_RETURN_NORMAL
);
317 static enum cmd_retval
318 cmd_list_keys_commands(struct cmd
*self
, struct cmdq_item
*item
)
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
;
327 if ((template = args_get(args
, 'F')) == NULL
) {
328 template = "#{command_list_name}"
329 "#{?command_list_alias, (#{command_list_alias}),} "
330 "#{command_list_usage}";
333 ft
= format_create(cmdq_get_client(item
), item
, FORMAT_NONE
, 0);
334 format_defaults(ft
, NULL
, NULL
, NULL
, NULL
);
336 command
= args_string(args
, 0);
337 for (entryp
= cmd_table
; *entryp
!= NULL
; entryp
++) {
339 if (command
!= NULL
&&
340 (strcmp(entry
->name
, command
) != 0 &&
341 (entry
->alias
== NULL
||
342 strcmp(entry
->alias
, command
) != 0)))
345 format_add(ft
, "command_list_name", "%s", entry
->name
);
346 if (entry
->alias
!= NULL
)
350 format_add(ft
, "command_list_alias", "%s", s
);
351 if (entry
->usage
!= NULL
)
355 format_add(ft
, "command_list_usage", "%s", s
);
357 line
= format_expand(ft
, template);
359 cmdq_print(item
, "%s", line
);
364 return (CMD_RETURN_NORMAL
);