NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
help.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <stddef.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 "menu/lib.h"
41 #include "pager/lib.h"
42 #include "functions.h"
43 #include "keymap.h"
44 #include "muttlib.h"
45 #include "opcodes.h"
46 #include "protos.h" // IWYU pragma: keep
47 
55 static const struct Binding *help_lookup_function(int op, enum MenuType menu)
56 {
57  const struct Binding *map = NULL;
58 
59  if (menu != MENU_PAGER)
60  {
61  /* first look in the generic map for the function */
62  for (int i = 0; OpGeneric[i].name; i++)
63  if (OpGeneric[i].op == op)
64  return &OpGeneric[i];
65  }
66 
67  map = km_get_table(menu);
68  if (map)
69  {
70  for (int i = 0; map[i].name; i++)
71  if (map[i].op == op)
72  return &map[i];
73  }
74 
75  return NULL;
76 }
77 
87 static int print_macro(FILE *fp, int maxwidth, const char **macro)
88 {
89  int n = maxwidth;
90  wchar_t wc;
91  size_t k;
92  size_t len = mutt_str_len(*macro);
93  mbstate_t mbstate1, mbstate2;
94 
95  memset(&mbstate1, 0, sizeof(mbstate1));
96  memset(&mbstate2, 0, sizeof(mbstate2));
97  for (; len && (k = mbrtowc(&wc, *macro, len, &mbstate1)); *macro += k, len -= k)
98  {
99  if ((k == (size_t) (-1)) || (k == (size_t) (-2)))
100  {
101  if (k == (size_t) (-1))
102  memset(&mbstate1, 0, sizeof(mbstate1));
103  k = (k == (size_t) (-1)) ? 1 : len;
104  wc = ReplacementChar;
105  }
106  /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
107  const int w = wcwidth(wc);
108  if (IsWPrint(wc) && (w >= 0))
109  {
110  if (w > n)
111  break;
112  n -= w;
113  {
114  char buf[MB_LEN_MAX * 2];
115  size_t n1, n2;
116  if (((n1 = wcrtomb(buf, wc, &mbstate2)) != (size_t) (-1)) &&
117  ((n2 = wcrtomb(buf + n1, 0, &mbstate2)) != (size_t) (-1)))
118  {
119  fputs(buf, fp);
120  }
121  }
122  }
123  else if ((wc < 0x20) || (wc == 0x7f))
124  {
125  if (n < 2)
126  break;
127  n -= 2;
128  if (wc == '\033') // Escape
129  fprintf(fp, "\\e");
130  else if (wc == '\n')
131  fprintf(fp, "\\n");
132  else if (wc == '\r')
133  fprintf(fp, "\\r");
134  else if (wc == '\t')
135  fprintf(fp, "\\t");
136  else
137  fprintf(fp, "^%c", (char) ((wc + '@') & 0x7f));
138  }
139  else
140  {
141  if (n < 1)
142  break;
143  n -= 1;
144  fprintf(fp, "?");
145  }
146  }
147  return maxwidth - n;
148 }
149 
158 static int get_wrapped_width(const char *t, size_t wid)
159 {
160  wchar_t wc;
161  size_t k;
162  size_t m, n;
163  size_t len = mutt_str_len(t);
164  const char *s = t;
165  mbstate_t mbstate;
166 
167  memset(&mbstate, 0, sizeof(mbstate));
168  for (m = wid, n = 0; len && (k = mbrtowc(&wc, s, len, &mbstate)) && (n <= wid);
169  s += k, len -= k)
170  {
171  if (*s == ' ')
172  m = n;
173  if ((k == (size_t) (-1)) || (k == (size_t) (-2)))
174  {
175  if (k == (size_t) (-1))
176  memset(&mbstate, 0, sizeof(mbstate));
177  k = (k == (size_t) (-1)) ? 1 : len;
178  wc = ReplacementChar;
179  }
180  if (!IsWPrint(wc))
181  wc = '?';
182  n += wcwidth(wc);
183  }
184  if (n > wid)
185  n = m;
186  else
187  n = wid;
188  return n;
189 }
190 
200 static int pad(FILE *fp, int col, int i)
201 {
202  if (col < i)
203  {
204  char fmt[32] = { 0 };
205  snprintf(fmt, sizeof(fmt), "%%-%ds", i - col);
206  fprintf(fp, fmt, "");
207  return i;
208  }
209  fputc(' ', fp);
210  return col + 1;
211 }
212 
229 static void format_line(FILE *fp, int ismacro, const char *t1, const char *t2,
230  const char *t3, int wraplen)
231 {
232  int col;
233  int col_b;
234 
235  fputs(t1, fp);
236 
237  /* don't try to press string into one line with less than 40 characters. */
238  bool split = (wraplen < 40);
239  if (split)
240  {
241  col = 0;
242  col_b = 1024;
243  fputc('\n', fp);
244  }
245  else
246  {
247  const int col_a = (wraplen > 83) ? (wraplen - 32) >> 2 : 12;
248  col_b = (wraplen > 49) ? (wraplen - 10) >> 1 : 19;
249  col = pad(fp, mutt_strwidth(t1), col_a);
250  }
251 
252  const char *const c_pager = cs_subset_string(NeoMutt->sub, "pager");
253  if (ismacro > 0)
254  {
255  if (!c_pager || mutt_str_equal(c_pager, "builtin"))
256  fputs("_\010", fp); // Ctrl-H (backspace)
257  fputs("M ", fp);
258  col += 2;
259 
260  if (!split)
261  {
262  col += print_macro(fp, col_b - col - 4, &t2);
263  if (mutt_strwidth(t2) > col_b - col)
264  t2 = "...";
265  }
266  }
267 
268  col += print_macro(fp, col_b - col - 1, &t2);
269  if (split)
270  fputc('\n', fp);
271  else
272  col = pad(fp, col, col_b);
273 
274  if (split)
275  {
276  print_macro(fp, 1024, &t3);
277  fputc('\n', fp);
278  }
279  else
280  {
281  while (*t3)
282  {
283  int n = wraplen - col;
284 
285  if (ismacro >= 0)
286  {
287  SKIPWS(t3);
288  n = get_wrapped_width(t3, n);
289  }
290 
291  n = print_macro(fp, n, &t3);
292 
293  if (*t3)
294  {
295  if (mutt_str_equal(c_pager, "builtin"))
296  {
297  n += col - wraplen;
298  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
299  if (c_markers)
300  n++;
301  }
302  else
303  {
304  fputc('\n', fp);
305  n = 0;
306  }
307  col = pad(fp, n, col_b);
308  }
309  }
310  }
311 
312  fputc('\n', fp);
313 }
314 
321 static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
322 {
323  struct Keymap *map = NULL;
324  const struct Binding *b = NULL;
325  char buf[128];
326 
327  STAILQ_FOREACH(map, &Keymaps[menu], entries)
328  {
329  if (map->op != OP_NULL)
330  {
331  km_expand_key(buf, sizeof(buf), map);
332 
333  if (map->op == OP_MACRO)
334  {
335  if (map->desc)
336  format_line(fp, 1, buf, map->macro, map->desc, wraplen);
337  else
338  format_line(fp, -1, buf, "macro", map->macro, wraplen);
339  }
340  else
341  {
342  b = help_lookup_function(map->op, menu);
343  format_line(fp, 0, buf, b ? b->name : "UNKNOWN",
344  b ? _(OpStrings[b->op][1]) : _("ERROR: please report this bug"), wraplen);
345  }
346  }
347  }
348 }
349 
356 static bool is_bound(struct KeymapList *km_list, int op)
357 {
358  struct Keymap *map = NULL;
359  STAILQ_FOREACH(map, km_list, entries)
360  {
361  if (map->op == op)
362  return true;
363  }
364  return false;
365 }
366 
375 static void dump_unbound(FILE *fp, const struct Binding *funcs,
376  struct KeymapList *km_list, struct KeymapList *aux, int wraplen)
377 {
378  for (int i = 0; funcs[i].name; i++)
379  {
380  if (!is_bound(km_list, funcs[i].op) && (!aux || !is_bound(aux, funcs[i].op)))
381  format_line(fp, 0, funcs[i].name, "", _(OpStrings[funcs[i].op][1]), wraplen);
382  }
383 }
384 
389 void mutt_help(enum MenuType menu)
390 {
391  const int wraplen = AllDialogsWindow->state.cols;
392  char banner[128];
393  FILE *fp = NULL;
394 
395  /* We don't use the buffer pool because of the extended lifetime of t */
396  struct Buffer t = mutt_buffer_make(PATH_MAX);
397  mutt_buffer_mktemp(&t);
398 
399  const struct Binding *funcs = km_get_table(menu);
400  const char *desc = mutt_map_get_name(menu, MenuNames);
401  if (!desc)
402  desc = _("<UNKNOWN>");
403 
404  struct PagerData pdata = { 0 };
405  struct PagerView pview = { &pdata };
406 
407  pview.mode = PAGER_MODE_HELP;
409 
410  do
411  {
412  fp = mutt_file_fopen(mutt_buffer_string(&t), "w");
413  if (!fp)
414  {
416  goto cleanup;
417  }
418 
419  dump_menu(fp, menu, wraplen);
420  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
421  {
422  fprintf(fp, "\n%s\n\n", _("Generic bindings:"));
423  dump_menu(fp, MENU_GENERIC, wraplen);
424  }
425 
426  fprintf(fp, "\n%s\n\n", _("Unbound functions:"));
427  if (funcs)
428  dump_unbound(fp, funcs, &Keymaps[menu], NULL, wraplen);
429  if (menu != MENU_PAGER)
430  dump_unbound(fp, OpGeneric, &Keymaps[MENU_GENERIC], &Keymaps[menu], wraplen);
431 
432  mutt_file_fclose(&fp);
433 
434  snprintf(banner, sizeof(banner), _("Help for %s"), desc);
435  pdata.fname = mutt_buffer_string(&t);
436  pview.banner = banner;
437  } while (mutt_do_pager(&pview, NULL) == OP_REFORMAT_WINCH);
438 
439 cleanup:
441 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
Convenience wrapper for the gui headers.
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: lib.h:65
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
Manage keymappings.
static void dump_unbound(FILE *fp, const struct Binding *funcs, struct KeymapList *km_list, struct KeymapList *aux, int wraplen)
Write out all the operations with no key bindings.
Definition: help.c:375
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
MenuType
Types of GUI selections.
Definition: type.h:35
#define IsWPrint(wc)
Definition: mbyte.h:39
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:63
struct KeymapList Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:126
Generic selection list.
Definition: type.h:45
int op
function id number
Definition: keymap.h:94
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
static int print_macro(FILE *fp, int maxwidth, const char **macro)
Print a macro string to a file.
Definition: help.c:87
String manipulation buffer.
Definition: buffer.h:33
Paged view into some data.
Definition: lib.h:154
#define _(a)
Definition: message.h:28
static const struct Binding * help_lookup_function(int op, enum MenuType menu)
Find a keybinding for an operation.
Definition: help.c:55
All user-callable functions.
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:389
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
Convenience wrapper for the config headers.
static void format_line(FILE *fp, int ismacro, const char *t1, const char *t2, const char *t3, int wraplen)
Write a formatted line to a file.
Definition: help.c:229
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Some miscellaneous functions.
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:158
Pager pager (email viewer)
Definition: type.h:54
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
Data to be displayed by PagerView.
Definition: lib.h:143
Text entry area.
Definition: type.h:43
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
const struct Binding OpGeneric[]
Key bindings for the generic menu.
Definition: functions.c:53
Convenience wrapper for the core headers.
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:64
const char * name
name of the function
Definition: keymap.h:93
short op
operation to perform
Definition: keymap.h:52
#define SKIPWS(ch)
Definition: string2.h:46
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
const char * OpStrings[][2]
Definition: opcodes.c:34
Pager is invoked via 3rd path to show help.
Definition: lib.h:134
enum PagerMode mode
Pager mode.
Definition: lib.h:157
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
Prototypes for many functions.
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:983
#define PATH_MAX
Definition: mutt.h:40
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:918
char * macro
macro expansion (op == OP_MACRO)
Definition: keymap.h:50
static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
Write all the key bindings to a file.
Definition: help.c:321
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:120
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:57
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
struct MuttWindow * AllDialogsWindow
Parent of all Dialogs.
Definition: dialog.c:76
static bool is_bound(struct KeymapList *km_list, int op)
Does a function have a keybinding?
Definition: help.c:356
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
PagerFlags flags
Additional settings to tweak pager&#39;s function.
Definition: lib.h:158
char * desc
description of a macro for the help menu
Definition: keymap.h:51
const struct Binding * km_get_table(enum MenuType mtype)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1313
A keyboard mapping.
Definition: keymap.h:48
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
Definitions of user functions.
const struct Mapping MenuNames[]
Menu name lookup table.
Definition: type.c:31
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:67
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
const char * fname
Name of the file to read.
Definition: lib.h:148
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
const char * banner
Title to display in status bar.
Definition: lib.h:159
GUI display a file/email/help in a viewport with paging.
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:200
Mapping between a user key and a function.
Definition: keymap.h:91