Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Dump key bindings |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2023 Richard Russon <rich@flatcap.org> |
7 | | * |
8 | | * @copyright |
9 | | * This program is free software: you can redistribute it and/or modify it under |
10 | | * the terms of the GNU General Public License as published by the Free Software |
11 | | * Foundation, either version 2 of the License, or (at your option) any later |
12 | | * version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
17 | | * details. |
18 | | * |
19 | | * You should have received a copy of the GNU General Public License along with |
20 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /** |
24 | | * @page key_dump Dump key bindings |
25 | | * |
26 | | * Dump key bindings |
27 | | */ |
28 | | |
29 | | #include "config.h" |
30 | | #include <stdbool.h> |
31 | | #include <stdint.h> |
32 | | #include <stdio.h> |
33 | | #include "mutt/lib.h" |
34 | | #include "config/lib.h" |
35 | | #include "core/lib.h" |
36 | | #include "gui/lib.h" |
37 | | #include "key/lib.h" |
38 | | #include "menu/lib.h" |
39 | | #include "pager/lib.h" |
40 | | #include "parse/lib.h" |
41 | | |
42 | | /** |
43 | | * dump_bind - Dumps all the binds maps of a menu into a buffer |
44 | | * @param buf Output buffer |
45 | | * @param menu Menu to dump |
46 | | * @param name Menu name |
47 | | * @retval true Menu is empty |
48 | | * @retval false Menu is not empty |
49 | | */ |
50 | | static bool dump_bind(struct Buffer *buf, enum MenuType menu, const char *name) |
51 | 0 | { |
52 | 0 | bool empty = true; |
53 | 0 | struct Keymap *map = NULL; |
54 | |
|
55 | 0 | STAILQ_FOREACH(map, &Keymaps[menu], entries) |
56 | 0 | { |
57 | 0 | if (map->op == OP_MACRO) |
58 | 0 | continue; |
59 | | |
60 | 0 | char key_binding[128] = { 0 }; |
61 | 0 | const char *fn_name = NULL; |
62 | |
|
63 | 0 | km_expand_key(key_binding, sizeof(key_binding), map); |
64 | 0 | if (map->op == OP_NULL) |
65 | 0 | { |
66 | 0 | buf_add_printf(buf, "bind %s %s noop\n", name, key_binding); |
67 | 0 | continue; |
68 | 0 | } |
69 | | |
70 | | /* The pager and editor menus don't use the generic map, |
71 | | * however for other menus try generic first. */ |
72 | 0 | if ((menu != MENU_PAGER) && (menu != MENU_EDITOR) && (menu != MENU_GENERIC)) |
73 | 0 | { |
74 | 0 | fn_name = mutt_get_func(OpGeneric, map->op); |
75 | 0 | } |
76 | | |
77 | | /* if it's one of the menus above or generic doesn't find |
78 | | * the function, try with its own menu. */ |
79 | 0 | if (!fn_name) |
80 | 0 | { |
81 | 0 | const struct MenuFuncOp *funcs = km_get_table(menu); |
82 | 0 | if (!funcs) |
83 | 0 | continue; |
84 | | |
85 | 0 | fn_name = mutt_get_func(funcs, map->op); |
86 | 0 | } |
87 | | |
88 | 0 | buf_add_printf(buf, "bind %s %s %s\n", name, key_binding, fn_name); |
89 | 0 | empty = false; |
90 | 0 | } |
91 | |
|
92 | 0 | return empty; |
93 | 0 | } |
94 | | |
95 | | /** |
96 | | * dump_all_binds - Dumps all the binds inside every menu |
97 | | * @param buf Output buffer |
98 | | */ |
99 | | static void dump_all_binds(struct Buffer *buf) |
100 | 0 | { |
101 | 0 | for (enum MenuType i = 1; i < MENU_MAX; i++) |
102 | 0 | { |
103 | 0 | const bool empty = dump_bind(buf, i, mutt_map_get_name(i, MenuNames)); |
104 | | |
105 | | /* Add a new line for readability between menus. */ |
106 | 0 | if (!empty && (i < (MENU_MAX - 1))) |
107 | 0 | buf_addch(buf, '\n'); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | /** |
112 | | * dump_macro - Dumps all the macros maps of a menu into a buffer |
113 | | * @param buf Output buffer |
114 | | * @param menu Menu to dump |
115 | | * @param name Menu name |
116 | | * @retval true Menu is empty |
117 | | * @retval false Menu is not empty |
118 | | */ |
119 | | static bool dump_macro(struct Buffer *buf, enum MenuType menu, const char *name) |
120 | 0 | { |
121 | 0 | bool empty = true; |
122 | 0 | struct Keymap *map = NULL; |
123 | |
|
124 | 0 | STAILQ_FOREACH(map, &Keymaps[menu], entries) |
125 | 0 | { |
126 | 0 | if (map->op != OP_MACRO) |
127 | 0 | continue; |
128 | | |
129 | 0 | char key_binding[128] = { 0 }; |
130 | 0 | km_expand_key(key_binding, sizeof(key_binding), map); |
131 | |
|
132 | 0 | struct Buffer tmp = buf_make(0); |
133 | 0 | escape_string(&tmp, map->macro); |
134 | |
|
135 | 0 | if (map->desc) |
136 | 0 | { |
137 | 0 | buf_add_printf(buf, "macro %s %s \"%s\" \"%s\"\n", name, key_binding, |
138 | 0 | tmp.data, map->desc); |
139 | 0 | } |
140 | 0 | else |
141 | 0 | { |
142 | 0 | buf_add_printf(buf, "macro %s %s \"%s\"\n", name, key_binding, tmp.data); |
143 | 0 | } |
144 | |
|
145 | 0 | buf_dealloc(&tmp); |
146 | 0 | empty = false; |
147 | 0 | } |
148 | |
|
149 | 0 | return empty; |
150 | 0 | } |
151 | | |
152 | | /** |
153 | | * dump_all_macros - Dumps all the macros inside every menu |
154 | | * @param buf Output buffer |
155 | | */ |
156 | | static void dump_all_macros(struct Buffer *buf) |
157 | 0 | { |
158 | 0 | for (enum MenuType i = 1; i < MENU_MAX; i++) |
159 | 0 | { |
160 | 0 | const bool empty = dump_macro(buf, i, mutt_map_get_name(i, MenuNames)); |
161 | | |
162 | | /* Add a new line for legibility between menus. */ |
163 | 0 | if (!empty && (i < (MENU_MAX - 1))) |
164 | 0 | buf_addch(buf, '\n'); |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | /** |
169 | | * dump_bind_macro - Parse 'bind' and 'macro' commands - Implements ICommand::parse() |
170 | | */ |
171 | | enum CommandResult dump_bind_macro(struct Buffer *buf, struct Buffer *s, |
172 | | intptr_t data, struct Buffer *err) |
173 | 0 | { |
174 | 0 | FILE *fp_out = NULL; |
175 | 0 | bool dump_all = false, bind = (data == 0); |
176 | |
|
177 | 0 | if (!MoreArgs(s)) |
178 | 0 | dump_all = true; |
179 | 0 | else |
180 | 0 | parse_extract_token(buf, s, TOKEN_NO_FLAGS); |
181 | |
|
182 | 0 | if (MoreArgs(s)) |
183 | 0 | { |
184 | | /* More arguments potentially means the user is using the |
185 | | * ::command_t :bind command thus we delegate the task. */ |
186 | 0 | return MUTT_CMD_ERROR; |
187 | 0 | } |
188 | | |
189 | 0 | struct Buffer filebuf = buf_make(4096); |
190 | 0 | if (dump_all || mutt_istr_equal(buf_string(buf), "all")) |
191 | 0 | { |
192 | 0 | if (bind) |
193 | 0 | dump_all_binds(&filebuf); |
194 | 0 | else |
195 | 0 | dump_all_macros(&filebuf); |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | const int menu_index = mutt_map_get_value(buf_string(buf), MenuNames); |
200 | 0 | if (menu_index == -1) |
201 | 0 | { |
202 | | // L10N: '%s' is the (misspelled) name of the menu, e.g. 'index' or 'pager' |
203 | 0 | buf_printf(err, _("%s: no such menu"), buf_string(buf)); |
204 | 0 | buf_dealloc(&filebuf); |
205 | 0 | return MUTT_CMD_ERROR; |
206 | 0 | } |
207 | | |
208 | 0 | if (bind) |
209 | 0 | dump_bind(&filebuf, menu_index, buf_string(buf)); |
210 | 0 | else |
211 | 0 | dump_macro(&filebuf, menu_index, buf_string(buf)); |
212 | 0 | } |
213 | | |
214 | 0 | if (buf_is_empty(&filebuf)) |
215 | 0 | { |
216 | | // L10N: '%s' is the name of the menu, e.g. 'index' or 'pager', |
217 | | // it might also be 'all' when all menus are affected. |
218 | 0 | buf_printf(err, bind ? _("%s: no binds for this menu") : _("%s: no macros for this menu"), |
219 | 0 | dump_all ? "all" : buf_string(buf)); |
220 | 0 | buf_dealloc(&filebuf); |
221 | 0 | return MUTT_CMD_ERROR; |
222 | 0 | } |
223 | | |
224 | 0 | struct Buffer *tempfile = buf_pool_get(); |
225 | 0 | buf_mktemp(tempfile); |
226 | 0 | fp_out = mutt_file_fopen(buf_string(tempfile), "w"); |
227 | 0 | if (!fp_out) |
228 | 0 | { |
229 | | // L10N: '%s' is the file name of the temporary file |
230 | 0 | buf_printf(err, _("Could not create temporary file %s"), buf_string(tempfile)); |
231 | 0 | buf_dealloc(&filebuf); |
232 | 0 | buf_pool_release(&tempfile); |
233 | 0 | return MUTT_CMD_ERROR; |
234 | 0 | } |
235 | 0 | fputs(filebuf.data, fp_out); |
236 | |
|
237 | 0 | mutt_file_fclose(&fp_out); |
238 | 0 | buf_dealloc(&filebuf); |
239 | |
|
240 | 0 | struct PagerData pdata = { 0 }; |
241 | 0 | struct PagerView pview = { &pdata }; |
242 | |
|
243 | 0 | pdata.fname = buf_string(tempfile); |
244 | |
|
245 | 0 | pview.banner = (bind) ? "bind" : "macro"; |
246 | 0 | pview.flags = MUTT_PAGER_NO_FLAGS; |
247 | 0 | pview.mode = PAGER_MODE_OTHER; |
248 | |
|
249 | 0 | mutt_do_pager(&pview, NULL); |
250 | 0 | buf_pool_release(&tempfile); |
251 | |
|
252 | 0 | return MUTT_CMD_SUCCESS; |
253 | 0 | } |