NeoMutt  2020-11-20
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 "gui/lib.h"
38 #include "functions.h"
39 #include "keymap.h"
40 #include "mutt_globals.h"
41 #include "muttlib.h"
42 #include "opcodes.h"
43 #include "pager.h"
44 #include "protos.h" // IWYU pragma: keep
45 
53 static const struct Binding *help_lookup_function(int op, enum MenuType menu)
54 {
55  const struct Binding *map = NULL;
56 
57  if (menu != MENU_PAGER)
58  {
59  /* first look in the generic map for the function */
60  for (int i = 0; OpGeneric[i].name; i++)
61  if (OpGeneric[i].op == op)
62  return &OpGeneric[i];
63  }
64 
65  map = km_get_table(menu);
66  if (map)
67  {
68  for (int i = 0; map[i].name; i++)
69  if (map[i].op == op)
70  return &map[i];
71  }
72 
73  return NULL;
74 }
75 
85 static int print_macro(FILE *fp, int maxwidth, const char **macro)
86 {
87  int n = maxwidth;
88  wchar_t wc;
89  size_t k;
90  size_t len = mutt_str_len(*macro);
91  mbstate_t mbstate1, mbstate2;
92 
93  memset(&mbstate1, 0, sizeof(mbstate1));
94  memset(&mbstate2, 0, sizeof(mbstate2));
95  for (; len && (k = mbrtowc(&wc, *macro, len, &mbstate1)); *macro += k, len -= k)
96  {
97  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
98  {
99  if (k == (size_t)(-1))
100  memset(&mbstate1, 0, sizeof(mbstate1));
101  k = (k == (size_t)(-1)) ? 1 : len;
102  wc = ReplacementChar;
103  }
104  /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
105  const int w = wcwidth(wc);
106  if (IsWPrint(wc) && (w >= 0))
107  {
108  if (w > n)
109  break;
110  n -= w;
111  {
112  char buf[MB_LEN_MAX * 2];
113  size_t n1, n2;
114  if (((n1 = wcrtomb(buf, wc, &mbstate2)) != (size_t)(-1)) &&
115  ((n2 = wcrtomb(buf + n1, 0, &mbstate2)) != (size_t)(-1)))
116  {
117  fputs(buf, fp);
118  }
119  }
120  }
121  else if ((wc < 0x20) || (wc == 0x7f))
122  {
123  if (n < 2)
124  break;
125  n -= 2;
126  if (wc == '\033') // Escape
127  fprintf(fp, "\\e");
128  else if (wc == '\n')
129  fprintf(fp, "\\n");
130  else if (wc == '\r')
131  fprintf(fp, "\\r");
132  else if (wc == '\t')
133  fprintf(fp, "\\t");
134  else
135  fprintf(fp, "^%c", (char) ((wc + '@') & 0x7f));
136  }
137  else
138  {
139  if (n < 1)
140  break;
141  n -= 1;
142  fprintf(fp, "?");
143  }
144  }
145  return maxwidth - n;
146 }
147 
156 static int get_wrapped_width(const char *t, size_t wid)
157 {
158  wchar_t wc;
159  size_t k;
160  size_t m, n;
161  size_t len = mutt_str_len(t);
162  const char *s = t;
163  mbstate_t mbstate;
164 
165  memset(&mbstate, 0, sizeof(mbstate));
166  for (m = wid, n = 0; len && (k = mbrtowc(&wc, s, len, &mbstate)) && (n <= wid);
167  s += k, len -= k)
168  {
169  if (*s == ' ')
170  m = n;
171  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
172  {
173  if (k == (size_t)(-1))
174  memset(&mbstate, 0, sizeof(mbstate));
175  k = (k == (size_t)(-1)) ? 1 : len;
176  wc = ReplacementChar;
177  }
178  if (!IsWPrint(wc))
179  wc = '?';
180  n += wcwidth(wc);
181  }
182  if (n > wid)
183  n = m;
184  else
185  n = wid;
186  return n;
187 }
188 
198 static int pad(FILE *fp, int col, int i)
199 {
200  if (col < i)
201  {
202  char fmt[32] = { 0 };
203  snprintf(fmt, sizeof(fmt), "%%-%ds", i - col);
204  fprintf(fp, fmt, "");
205  return i;
206  }
207  fputc(' ', fp);
208  return col + 1;
209 }
210 
227 static void format_line(FILE *fp, int ismacro, const char *t1, const char *t2,
228  const char *t3, int wraplen)
229 {
230  int col;
231  int col_b;
232 
233  fputs(t1, fp);
234 
235  /* don't try to press string into one line with less than 40 characters. */
236  bool split = (wraplen < 40);
237  if (split)
238  {
239  col = 0;
240  col_b = 1024;
241  fputc('\n', fp);
242  }
243  else
244  {
245  const int col_a = (wraplen > 83) ? (wraplen - 32) >> 2 : 12;
246  col_b = (wraplen > 49) ? (wraplen - 10) >> 1 : 19;
247  col = pad(fp, mutt_strwidth(t1), col_a);
248  }
249 
250  if (ismacro > 0)
251  {
252  if (!C_Pager || mutt_str_equal(C_Pager, "builtin"))
253  fputs("_\010", fp); // Ctrl-H (backspace)
254  fputs("M ", fp);
255  col += 2;
256 
257  if (!split)
258  {
259  col += print_macro(fp, col_b - col - 4, &t2);
260  if (mutt_strwidth(t2) > col_b - col)
261  t2 = "...";
262  }
263  }
264 
265  col += print_macro(fp, col_b - col - 1, &t2);
266  if (split)
267  fputc('\n', fp);
268  else
269  col = pad(fp, col, col_b);
270 
271  if (split)
272  {
273  print_macro(fp, 1024, &t3);
274  fputc('\n', fp);
275  }
276  else
277  {
278  while (*t3)
279  {
280  int n = wraplen - col;
281 
282  if (ismacro >= 0)
283  {
284  SKIPWS(t3);
285  n = get_wrapped_width(t3, n);
286  }
287 
288  n = print_macro(fp, n, &t3);
289 
290  if (*t3)
291  {
292  if (mutt_str_equal(C_Pager, "builtin"))
293  {
294  n += col - wraplen;
295  if (C_Markers)
296  n++;
297  }
298  else
299  {
300  fputc('\n', fp);
301  n = 0;
302  }
303  col = pad(fp, n, col_b);
304  }
305  }
306  }
307 
308  fputc('\n', fp);
309 }
310 
317 static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
318 {
319  struct Keymap *map = NULL;
320  const struct Binding *b = NULL;
321  char buf[128];
322 
323  STAILQ_FOREACH(map, &Keymaps[menu], entries)
324  {
325  if (map->op != OP_NULL)
326  {
327  km_expand_key(buf, sizeof(buf), map);
328 
329  if (map->op == OP_MACRO)
330  {
331  if (map->desc)
332  format_line(fp, 1, buf, map->macro, map->desc, wraplen);
333  else
334  format_line(fp, -1, buf, "macro", map->macro, wraplen);
335  }
336  else
337  {
338  b = help_lookup_function(map->op, menu);
339  format_line(fp, 0, buf, b ? b->name : "UNKNOWN",
340  b ? _(OpStrings[b->op][1]) : _("ERROR: please report this bug"), wraplen);
341  }
342  }
343  }
344 }
345 
352 static bool is_bound(struct KeymapList *km_list, int op)
353 {
354  struct Keymap *map = NULL;
355  STAILQ_FOREACH(map, km_list, entries)
356  {
357  if (map->op == op)
358  return true;
359  }
360  return false;
361 }
362 
371 static void dump_unbound(FILE *fp, const struct Binding *funcs,
372  struct KeymapList *km_list, struct KeymapList *aux, int wraplen)
373 {
374  for (int i = 0; funcs[i].name; i++)
375  {
376  if (!is_bound(km_list, funcs[i].op) && (!aux || !is_bound(aux, funcs[i].op)))
377  format_line(fp, 0, funcs[i].name, "", _(OpStrings[funcs[i].op][1]), wraplen);
378  }
379 }
380 
386 void mutt_help(enum MenuType menu, int wraplen)
387 {
388  char buf[128];
389  FILE *fp = NULL;
390 
391  /* We don't use the buffer pool because of the extended lifetime of t */
392  struct Buffer t = mutt_buffer_make(PATH_MAX);
393  mutt_buffer_mktemp(&t);
394 
395  const struct Binding *funcs = km_get_table(menu);
396  const char *desc = mutt_map_get_name(menu, Menus);
397  if (!desc)
398  desc = _("<UNKNOWN>");
399 
400  do
401  {
402  fp = mutt_file_fopen(mutt_b2s(&t), "w");
403  if (!fp)
404  {
405  mutt_perror(mutt_b2s(&t));
406  goto cleanup;
407  }
408 
409  dump_menu(fp, menu, wraplen);
410  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
411  {
412  fprintf(fp, "\n%s\n\n", _("Generic bindings:"));
413  dump_menu(fp, MENU_GENERIC, wraplen);
414  }
415 
416  fprintf(fp, "\n%s\n\n", _("Unbound functions:"));
417  if (funcs)
418  dump_unbound(fp, funcs, &Keymaps[menu], NULL, wraplen);
419  if (menu != MENU_PAGER)
420  dump_unbound(fp, OpGeneric, &Keymaps[MENU_GENERIC], &Keymaps[menu], wraplen);
421 
422  mutt_file_fclose(&fp);
423 
424  snprintf(buf, sizeof(buf), _("Help for %s"), desc);
425  } while (mutt_do_pager(buf, mutt_b2s(&t),
427  NULL) == OP_REFORMAT_WINCH);
428 
429 cleanup:
431 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
Convenience wrapper for the gui headers.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
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:371
#define mutt_perror(...)
Definition: logging.h:85
#define IsWPrint(wc)
Definition: mbyte.h:40
struct KeymapList Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:149
MenuType
Types of GUI selections.
Definition: keymap.h:72
int op
function id number
Definition: keymap.h:123
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:85
String manipulation buffer.
Definition: buffer.h:33
#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:53
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: pager.h:56
All user-callable functions.
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:227
Some miscellaneous functions.
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:156
Pager pager (email viewer)
Definition: keymap.h:81
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
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
const char * name
name of the function
Definition: keymap.h:122
short op
operation to perform
Definition: keymap.h:51
#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
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: pager.h:52
const char * OpStrings[][2]
Definition: opcodes.c:28
#define mutt_b2s(buf)
Definition: buffer.h:41
Prototypes for many functions.
const struct Mapping Menus[]
Menu name lookup table.
Definition: keymap.c:61
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1359
#define PATH_MAX
Definition: mutt.h:44
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:911
char * macro
macro expansion (op == OP_MACRO)
Definition: keymap.h:49
static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
Write all the key bindings to a file.
Definition: help.c:317
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:58
WHERE bool C_Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: mutt_globals.h:151
static bool is_bound(struct KeymapList *km_list, int op)
Does a function have a keybinding?
Definition: help.c:352
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
char * desc
description of a macro for the help menu
Definition: keymap.h:50
A keyboard mapping.
Definition: keymap.h:47
GUI display a file/email/help in a viewport with paging.
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: pager.h:54
Definitions of user functions.
WHERE char * C_Pager
Config: External command for viewing messages, or &#39;builtin&#39; to use NeoMutt&#39;s.
Definition: mutt_globals.h:99
int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color, struct Pager *info)
Display some page-able text to the user.
Definition: curs_lib.c:690
void mutt_help(enum MenuType menu, int wraplen)
Display the help menu.
Definition: help.c:386
const struct Binding * km_get_table(enum MenuType menu)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1306
Text entry area.
Definition: keymap.h:77
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: pager.h:53
Hundreds of global variables to back the user variables.
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:198
Mapping between a user key and a function.
Definition: keymap.h:120
Generic selection list.
Definition: keymap.h:79