1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
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 | |