NeoMutt  2025-12-11-800-ga0ee0f
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
dump.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <limits.h>
32#include <stdbool.h>
33#include <stdio.h>
34#include <string.h>
35#include <wchar.h>
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "core/lib.h"
39#include "gui/lib.h"
40#include "dump.h"
41#include "pager/lib.h"
42#include "get.h"
43#include "keymap.h"
44#include "menu.h"
45#include "module_data.h"
46
54void escape_macro(const char *macro, struct Buffer *buf)
55{
56 wchar_t wc = 0;
57 size_t k;
58 size_t len = mutt_str_len(macro);
59 mbstate_t mbstate1 = { 0 };
60 mbstate_t mbstate2 = { 0 };
61
62 for (; (len > 0) && (k = mbrtowc(&wc, macro, MB_LEN_MAX, &mbstate1)); macro += k, len -= k)
63 {
64 if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
65 {
66 if (k == ICONV_ILLEGAL_SEQ)
67 memset(&mbstate1, 0, sizeof(mbstate1));
68 k = (k == ICONV_ILLEGAL_SEQ) ? 1 : len;
69 wc = ReplacementChar;
70 }
71
72 const int w = wcwidth(wc);
73 if (IsWPrint(wc) && (w >= 0))
74 {
75 char tmp[MB_LEN_MAX * 2] = { 0 };
76 if (wcrtomb(tmp, wc, &mbstate2) != ICONV_ILLEGAL_SEQ)
77 {
78 buf_addstr(buf, tmp);
79 }
80 }
81 else if ((wc < 0x20) || (wc == 0x7f))
82 {
83 if (wc == '\033') // Escape
84 buf_addstr(buf, "\\e");
85 else if (wc == '\n')
86 buf_addstr(buf, "\\n");
87 else if (wc == '\r')
88 buf_addstr(buf, "\\r");
89 else if (wc == '\t')
90 buf_addstr(buf, "\\t");
91 else
92 buf_add_printf(buf, "^%c", (char) ((wc + '@') & 0x7f));
93 }
94 else
95 {
96 buf_addch(buf, '?');
97 }
98 }
99}
100
108const char *help_lookup_function(const struct MenuDefinition *md, int op)
109{
110 struct SubMenu **smp = NULL;
111
112 ARRAY_FOREACH(smp, &md->submenus)
113 {
114 struct SubMenu *sm = *smp;
115
116 for (int i = 0; sm->functions[i].name; i++)
117 {
118 const struct MenuFuncOp *mfo = &sm->functions[i];
119 if (mfo->op == op)
120 return mfo->name;
121 }
122 }
123
124 return "UNKNOWN";
125}
126
134void gather_menu(const struct MenuDefinition *md, struct BindingInfoArray *bia_bind,
135 struct BindingInfoArray *bia_macro, bool one_submenu)
136{
137 struct Buffer *key_binding = buf_pool_get();
138 struct Buffer *macro = buf_pool_get();
139
140 struct SubMenu **smp = NULL;
141
142 ARRAY_FOREACH(smp, &md->submenus)
143 {
144 struct SubMenu *sm = *smp;
145 const char *name = sm->parent->name;
146
147 struct BindingInfo bi_label = { ARRAY_FOREACH_IDX_smp, { NULL, NULL, name } };
148
149 if (bia_bind)
150 ARRAY_ADD(bia_bind, bi_label);
151 if (bia_macro)
152 ARRAY_ADD(bia_macro, bi_label);
153
154 struct Keymap *map = NULL;
155 STAILQ_FOREACH(map, &sm->keymaps, entries)
156 {
157 struct BindingInfo bi = { ARRAY_FOREACH_IDX_smp, { NULL, NULL, NULL } };
158
159 buf_reset(key_binding);
160 keymap_expand_key(map, key_binding);
161
162 if (map->op == OP_MACRO)
163 {
164 if (!bia_macro || (map->op == OP_NULL))
165 continue;
166
167 buf_reset(macro);
168 escape_macro(map->macro, macro);
169 bi.a[0] = buf_strdup(key_binding);
170 bi.a[1] = buf_strdup(macro);
171 bi.a[2] = map->desc;
172 ARRAY_ADD(bia_macro, bi);
173 }
174 else
175 {
176 if (!bia_bind)
177 continue;
178
179 if (map->op == OP_NULL)
180 {
181 bi.a[0] = buf_strdup(key_binding);
182 bi.a[1] = "noop";
183 ARRAY_ADD(bia_bind, bi);
184 continue;
185 }
186
187 bi.a[0] = buf_strdup(key_binding);
188 bi.a[1] = help_lookup_function(md, map->op);
189 bi.a[2] = _(opcodes_get_description(map->op));
190 ARRAY_ADD(bia_bind, bi);
191 }
192 }
193
194 if (one_submenu)
195 break;
196 }
197
198 buf_pool_release(&key_binding);
199 buf_pool_release(&macro);
200}
201
205int binding_sort(const void *a, const void *b, void *sdata)
206{
207 const struct BindingInfo *x = (const struct BindingInfo *) a;
208 const struct BindingInfo *y = (const struct BindingInfo *) b;
209
210 // Sort by SubMenu
211 if (x->order != y->order)
212 return mutt_numeric_cmp(x->order, y->order);
213
214 // Sort by Keybinding
215 int rc = mutt_str_cmp(x->a[0], y->a[0]);
216 if (rc != 0)
217 return rc;
218
219 // No binding, sort by function instead
220 return mutt_str_cmp(x->a[1], y->a[1]);
221}
222
229int measure_column(struct BindingInfoArray *bia, int col)
230{
231 int max_width = 0;
232
233 struct BindingInfo *bi = NULL;
234 ARRAY_FOREACH(bi, bia)
235 {
236 const int col_width = mutt_strwidth(bi->a[col]);
237 max_width = MAX(max_width, col_width);
238 }
239
240 return max_width;
241}
242
249int print_bind(const struct MenuDefinition *md, FILE *fp)
250{
251 struct BindingInfoArray bia_bind = ARRAY_HEAD_INITIALIZER;
252
253 gather_menu(md, &bia_bind, NULL, true);
254 if (ARRAY_EMPTY(&bia_bind))
255 return 0;
256
257 ARRAY_SORT(&bia_bind, binding_sort, NULL);
258 const int wb0 = measure_column(&bia_bind, 0);
259 const int wb1 = measure_column(&bia_bind, 1);
260
261 const char *menu_name = md->name;
262
263 struct BindingInfo *bi = NULL;
264 ARRAY_FOREACH(bi, &bia_bind)
265 {
266 if (!bi->a[0])
267 continue;
268
269 fprintf(fp, "bind %s %*s %*s # %s\n", menu_name, -wb0, bi->a[0], -wb1,
270 bi->a[1], bi->a[2]);
271 }
272
273 const int count = ARRAY_SIZE(&bia_bind);
274 ARRAY_FOREACH(bi, &bia_bind)
275 {
276 // we only need to free the keybinding
277 FREE(&bi->a[0]);
278 }
279
280 ARRAY_FREE(&bia_bind);
281 return count - 1;
282}
283
289void colon_bind(const struct MenuDefinition *md, FILE *fp)
290{
292 if (md)
293 {
294 print_bind(md, fp);
295 }
296 else
297 {
298 struct MenuDefinition **mdp = NULL;
299 ARRAY_FOREACH(mdp, &mod_data->menu_defs)
300 {
301 if (print_bind((*mdp), fp) > 0)
302 {
303 fprintf(fp, "\n");
304 }
305 }
306 }
307}
308
315int print_macro(const struct MenuDefinition *md, FILE *fp)
316{
317 struct BindingInfoArray bia_macro = ARRAY_HEAD_INITIALIZER;
318
319 gather_menu(md, NULL, &bia_macro, true);
320 if (ARRAY_EMPTY(&bia_macro))
321 return 0;
322
323 ARRAY_SORT(&bia_macro, binding_sort, NULL);
324 const int wm0 = measure_column(&bia_macro, 0);
325
326 const char *menu_name = md->name;
327
328 struct BindingInfo *bi = NULL;
329 ARRAY_FOREACH(bi, &bia_macro)
330 {
331 if (!bi->a[0])
332 continue;
333
334 if (bi->a[2]) // description
335 {
336 fprintf(fp, "macro %s %*s \"%s\" \"%s\"\n", menu_name, -wm0, bi->a[0],
337 bi->a[1], bi->a[2]);
338 }
339 else
340 {
341 fprintf(fp, "macro %s %*s \"%s\"\n", menu_name, -wm0, bi->a[0], bi->a[1]);
342 }
343 }
344
345 const int count = ARRAY_SIZE(&bia_macro);
346 ARRAY_FOREACH(bi, &bia_macro)
347 {
348 // free the keybinding and the macro text
349 FREE(&bi->a[0]);
350 FREE(&bi->a[1]);
351 }
352
353 ARRAY_FREE(&bia_macro);
354 return count - 1;
355}
356
362void colon_macro(const struct MenuDefinition *md, FILE *fp)
363{
365 if (md)
366 {
367 print_macro(md, fp);
368 }
369 else
370 {
371 struct MenuDefinition **mdp = NULL;
372 ARRAY_FOREACH(mdp, &mod_data->menu_defs)
373 {
374 if (print_macro((*mdp), fp) > 0)
375 {
376 fprintf(fp, "\n");
377 }
378 }
379 }
380}
381
389void dump_bind_macro(const struct Command *cmd, const struct MenuDefinition *md,
390 struct Buffer *buf, struct Buffer *err)
391{
392 bool dump_all = (md == NULL);
393
394 struct Buffer *tempfile = buf_pool_get();
395 buf_mktemp(tempfile);
396 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w");
397 if (!fp)
398 {
399 // L10N: '%s' is the file name of the temporary file
400 buf_printf(err, _("Could not create temporary file %s"), buf_string(tempfile));
401 goto done;
402 }
403
404 if (cmd->id == CMD_BIND)
405 colon_bind(md, fp);
406 else
407 colon_macro(md, fp);
408
409 if (ftello(fp) == 0)
410 {
411 // L10N: '%s' is the name of the menu, e.g. 'index' or 'pager',
412 // it might also be 'all' when all menus are affected.
413 buf_printf(err, (cmd->id == CMD_BIND) ? _("%s: no binds for this menu") : _("%s: no macros for this menu"),
414 dump_all ? "all" : buf_string(buf));
415 goto done;
416 }
417 mutt_file_fclose(&fp);
418
419 struct PagerData pdata = { 0 };
420 struct PagerView pview = { &pdata };
421
422 pdata.fname = buf_string(tempfile);
423
424 pview.banner = cmd->name;
426 pview.mode = PAGER_MODE_OTHER;
427
428 mutt_do_pager(&pview, NULL);
429
430done:
431 mutt_file_fclose(&fp);
432 buf_pool_release(&tempfile);
433}
434
441int gather_unbound(const struct MenuDefinition *md, struct BindingInfoArray *bia_unbound)
442{
443 if (!bia_unbound)
444 return 0;
445
446 struct SubMenu **smp = NULL;
447
448 ARRAY_FOREACH(smp, &md->submenus)
449 {
450 struct SubMenu *sm = *smp;
451
452 for (int i = 0; sm->functions[i].name; i++)
453 {
454 const struct MenuFuncOp *mfo = &sm->functions[i];
455
456 if (mfo->flags & MFF_DEPRECATED)
457 continue;
458
459 if (is_bound(md, mfo->op))
460 continue;
461
462 struct BindingInfo bi = { 0 };
463 bi.a[0] = NULL;
464 bi.a[1] = mfo->name;
465 bi.a[2] = _(opcodes_get_description(mfo->op));
466 ARRAY_ADD(bia_unbound, bi);
467 }
468 }
469
470 return ARRAY_SIZE(bia_unbound);
471}
472
477struct StringArray km_get_func_array(const struct MenuDefinition *md)
478{
479 struct StringArray fna = ARRAY_HEAD_INITIALIZER;
480
481 struct SubMenu **smp = NULL;
482 ARRAY_FOREACH(smp, &md->submenus)
483 {
484 struct SubMenu *sm = *smp;
485
486 for (int i = 0; sm->functions[i].name; i++)
487 {
488 ARRAY_ADD(&fna, sm->functions[i].name);
489 }
490 }
491
492 return fna;
493}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
@ CMD_BIND
:bind
Definition command.h:67
Convenience wrapper for the config headers.
#define mutt_numeric_cmp(a, b)
Compare two numbers, return -1, 0, or 1.
Definition sort.h:27
Convenience wrapper for the core headers.
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition curs_lib.c:446
int mutt_do_pager(struct PagerView *pview, struct Email *e)
Display some page-able text to the user (help or attachment)
Definition do_pager.c:122
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
Get a key from the user.
#define MFF_DEPRECATED
Redraw the pager.
Definition get.h:51
int binding_sort(const void *a, const void *b, void *sdata)
Compare two BindingInfo by their keybinding - Implements sort_t -.
Definition dump.c:205
Convenience wrapper for the gui headers.
void colon_macro(const struct MenuDefinition *md, FILE *fp)
Dump the macros.
Definition dump.c:362
int measure_column(struct BindingInfoArray *bia, int col)
Measure one column of a table.
Definition dump.c:229
void dump_bind_macro(const struct Command *cmd, const struct MenuDefinition *md, struct Buffer *buf, struct Buffer *err)
Dump a Menu's binds or macros to the Pager.
Definition dump.c:389
void gather_menu(const struct MenuDefinition *md, struct BindingInfoArray *bia_bind, struct BindingInfoArray *bia_macro, bool one_submenu)
Gather info about one menu.
Definition dump.c:134
struct StringArray km_get_func_array(const struct MenuDefinition *md)
Get array of function names for a Menu.
Definition dump.c:477
void escape_macro(const char *macro, struct Buffer *buf)
Escape any special characters in a macro.
Definition dump.c:54
const char * help_lookup_function(const struct MenuDefinition *md, int op)
Find a keybinding for an operation.
Definition dump.c:108
int gather_unbound(const struct MenuDefinition *md, struct BindingInfoArray *bia_unbound)
Gather info about unbound functions for one menu.
Definition dump.c:441
int print_bind(const struct MenuDefinition *md, FILE *fp)
Display the bindings for one menu.
Definition dump.c:249
void colon_bind(const struct MenuDefinition *md, FILE *fp)
Dump the key bindings.
Definition dump.c:289
int print_macro(const struct MenuDefinition *md, FILE *fp)
Display the macros for one menu.
Definition dump.c:315
Dump key bindings.
bool keymap_expand_key(struct Keymap *km, struct Buffer *buf)
Get the key string bound to a Keymap.
Definition keymap.c:248
bool is_bound(const struct MenuDefinition *md, int op)
Does a function have a keybinding?
Definition menu.c:275
Key private Module data.
Keymap handling.
#define IsWPrint(wc)
Definition mbyte.h:40
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
Maniplate Menus and SubMenus.
@ MODULE_ID_KEY
ModuleKey, Key mappings
Definition module_api.h:73
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:61
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition charset.h:116
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition string.c:403
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
const char * opcodes_get_description(int op)
Get the description of an opcode.
Definition opcodes.c:68
GUI display a file/email/help in a viewport with paging.
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition lib.h:63
@ PAGER_MODE_OTHER
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition lib.h:143
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
Info about one keybinding.
Definition dump.h:40
const char * a[3]
Array of info.
Definition dump.h:42
int order
SubMenu sorting order.
Definition dump.h:41
String manipulation buffer.
Definition buffer.h:36
const char * name
Name of the Command.
Definition command.h:159
enum CommandId id
ID of the Command.
Definition command.h:160
Key private Module data.
Definition module_data.h:34
struct MenuDefinitionArray menu_defs
All registered Menus.
Definition module_data.h:39
A keyboard mapping.
Definition keymap.h:43
char * macro
Macro expansion (op == OP_MACRO)
Definition keymap.h:44
char * desc
Description of a macro for the help menu.
Definition keymap.h:45
short op
Operation to perform.
Definition keymap.h:46
Functions for a Dialog or Window.
Definition menu.h:77
const char * name
Menu name, e.g. "alias".
Definition menu.h:79
struct SubMenuPArray submenus
Parts making up the Menu.
Definition menu.h:80
Mapping between a function and an operation.
Definition menu.h:35
const char * name
Name of the function.
Definition menu.h:36
MenuFuncFlags flags
Flags, e.g. MFF_DEPRECATED.
Definition menu.h:38
int op
Operation, e.g. OP_DELETE.
Definition menu.h:37
Container for Accounts, Notifications.
Definition neomutt.h:41
Data to be displayed by PagerView.
Definition lib.h:162
const char * fname
Name of the file to read.
Definition lib.h:166
Paged view into some data.
Definition lib.h:173
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition lib.h:174
enum PagerMode mode
Pager mode.
Definition lib.h:175
PagerFlags flags
Additional settings to tweak pager's function.
Definition lib.h:176
const char * banner
Title to display in status bar.
Definition lib.h:177
Collection of related functions.
Definition menu.h:65
const struct MenuFuncOp * functions
All available functions.
Definition menu.h:67
struct KeymapList keymaps
All keybindings.
Definition menu.h:68
struct MenuDefinition * parent
Primary parent.
Definition menu.h:66
#define buf_mktemp(buf)
Definition tmp.h:33