NeoMutt  2020-06-26-30-g76c339
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 "mutt.h"
39 #include "functions.h"
40 #include "globals.h"
41 #include "keymap.h"
42 #include "muttlib.h"
43 #include "opcodes.h"
44 #include "pager.h"
45 #include "protos.h" // IWYU pragma: keep
46 
54 static const struct Binding *help_lookup_function(int op, enum MenuType menu)
55 {
56  const struct Binding *map = NULL;
57 
58  if (menu != MENU_PAGER)
59  {
60  /* first look in the generic map for the function */
61  for (int i = 0; OpGeneric[i].name; i++)
62  if (OpGeneric[i].op == op)
63  return &OpGeneric[i];
64  }
65 
66  map = km_get_table(menu);
67  if (map)
68  {
69  for (int i = 0; map[i].name; i++)
70  if (map[i].op == op)
71  return &map[i];
72  }
73 
74  return NULL;
75 }
76 
87 void mutt_make_help(char *buf, size_t buflen, const char *txt, enum MenuType menu, int op)
88 {
89  char tmp[128];
90 
91  if (km_expand_key(tmp, sizeof(tmp), km_find_func(menu, op)) ||
92  km_expand_key(tmp, sizeof(tmp), km_find_func(MENU_GENERIC, op)))
93  {
94  snprintf(buf, buflen, "%s:%s", tmp, txt);
95  }
96  else
97  {
98  buf[0] = '\0';
99  }
100 }
101 
110 char *mutt_compile_help(char *buf, size_t buflen, enum MenuType menu,
111  const struct Mapping *items)
112 {
113  char *pbuf = buf;
114 
115  for (int i = 0; items[i].name && buflen > 2; i++)
116  {
117  if (i)
118  {
119  *pbuf++ = ' ';
120  *pbuf++ = ' ';
121  buflen -= 2;
122  }
123  mutt_make_help(pbuf, buflen, _(items[i].name), menu, items[i].value);
124  const size_t len = mutt_str_len(pbuf);
125  pbuf += len;
126  buflen -= len;
127  }
128  return buf;
129 }
130 
140 static int print_macro(FILE *fp, int maxwidth, const char **macro)
141 {
142  int n = maxwidth;
143  wchar_t wc;
144  size_t k;
145  size_t len = mutt_str_len(*macro);
146  mbstate_t mbstate1, mbstate2;
147 
148  memset(&mbstate1, 0, sizeof(mbstate1));
149  memset(&mbstate2, 0, sizeof(mbstate2));
150  for (; len && (k = mbrtowc(&wc, *macro, len, &mbstate1)); *macro += k, len -= k)
151  {
152  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
153  {
154  if (k == (size_t)(-1))
155  memset(&mbstate1, 0, sizeof(mbstate1));
156  k = (k == (size_t)(-1)) ? 1 : len;
157  wc = ReplacementChar;
158  }
159  /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
160  const int w = wcwidth(wc);
161  if (IsWPrint(wc) && (w >= 0))
162  {
163  if (w > n)
164  break;
165  n -= w;
166  {
167  char buf[MB_LEN_MAX * 2];
168  size_t n1, n2;
169  if (((n1 = wcrtomb(buf, wc, &mbstate2)) != (size_t)(-1)) &&
170  ((n2 = wcrtomb(buf + n1, 0, &mbstate2)) != (size_t)(-1)))
171  {
172  fputs(buf, fp);
173  }
174  }
175  }
176  else if ((wc < 0x20) || (wc == 0x7f))
177  {
178  if (n < 2)
179  break;
180  n -= 2;
181  if (wc == '\033') // Escape
182  fprintf(fp, "\\e");
183  else if (wc == '\n')
184  fprintf(fp, "\\n");
185  else if (wc == '\r')
186  fprintf(fp, "\\r");
187  else if (wc == '\t')
188  fprintf(fp, "\\t");
189  else
190  fprintf(fp, "^%c", (char) ((wc + '@') & 0x7f));
191  }
192  else
193  {
194  if (n < 1)
195  break;
196  n -= 1;
197  fprintf(fp, "?");
198  }
199  }
200  return maxwidth - n;
201 }
202 
211 static int get_wrapped_width(const char *t, size_t wid)
212 {
213  wchar_t wc;
214  size_t k;
215  size_t m, n;
216  size_t len = mutt_str_len(t);
217  const char *s = t;
218  mbstate_t mbstate;
219 
220  memset(&mbstate, 0, sizeof(mbstate));
221  for (m = wid, n = 0; len && (k = mbrtowc(&wc, s, len, &mbstate)) && (n <= wid);
222  s += k, len -= k)
223  {
224  if (*s == ' ')
225  m = n;
226  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
227  {
228  if (k == (size_t)(-1))
229  memset(&mbstate, 0, sizeof(mbstate));
230  k = (k == (size_t)(-1)) ? 1 : len;
231  wc = ReplacementChar;
232  }
233  if (!IsWPrint(wc))
234  wc = '?';
235  n += wcwidth(wc);
236  }
237  if (n > wid)
238  n = m;
239  else
240  n = wid;
241  return n;
242 }
243 
253 static int pad(FILE *fp, int col, int i)
254 {
255  if (col < i)
256  {
257  char fmt[32] = { 0 };
258  snprintf(fmt, sizeof(fmt), "%%-%ds", i - col);
259  fprintf(fp, fmt, "");
260  return i;
261  }
262  fputc(' ', fp);
263  return col + 1;
264 }
265 
282 static void format_line(FILE *fp, int ismacro, const char *t1, const char *t2,
283  const char *t3, int wraplen)
284 {
285  int col;
286  int col_b;
287 
288  fputs(t1, fp);
289 
290  /* don't try to press string into one line with less than 40 characters. */
291  bool split = (wraplen < 40);
292  if (split)
293  {
294  col = 0;
295  col_b = 1024;
296  fputc('\n', fp);
297  }
298  else
299  {
300  const int col_a = (wraplen > 83) ? (wraplen - 32) >> 2 : 12;
301  col_b = (wraplen > 49) ? (wraplen - 10) >> 1 : 19;
302  col = pad(fp, mutt_strwidth(t1), col_a);
303  }
304 
305  if (ismacro > 0)
306  {
307  if (!C_Pager || mutt_str_equal(C_Pager, "builtin"))
308  fputs("_\010", fp); // Ctrl-H (backspace)
309  fputs("M ", fp);
310  col += 2;
311 
312  if (!split)
313  {
314  col += print_macro(fp, col_b - col - 4, &t2);
315  if (mutt_strwidth(t2) > col_b - col)
316  t2 = "...";
317  }
318  }
319 
320  col += print_macro(fp, col_b - col - 1, &t2);
321  if (split)
322  fputc('\n', fp);
323  else
324  col = pad(fp, col, col_b);
325 
326  if (split)
327  {
328  print_macro(fp, 1024, &t3);
329  fputc('\n', fp);
330  }
331  else
332  {
333  while (*t3)
334  {
335  int n = wraplen - col;
336 
337  if (ismacro >= 0)
338  {
339  SKIPWS(t3);
340  n = get_wrapped_width(t3, n);
341  }
342 
343  n = print_macro(fp, n, &t3);
344 
345  if (*t3)
346  {
347  if (mutt_str_equal(C_Pager, "builtin"))
348  {
349  n += col - wraplen;
350  if (C_Markers)
351  n++;
352  }
353  else
354  {
355  fputc('\n', fp);
356  n = 0;
357  }
358  col = pad(fp, n, col_b);
359  }
360  }
361  }
362 
363  fputc('\n', fp);
364 }
365 
372 static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
373 {
374  struct Keymap *map = NULL;
375  const struct Binding *b = NULL;
376  char buf[128];
377 
378  /* browse through the keymap table */
379  for (map = Keymaps[menu]; map; map = map->next)
380  {
381  if (map->op != OP_NULL)
382  {
383  km_expand_key(buf, sizeof(buf), map);
384 
385  if (map->op == OP_MACRO)
386  {
387  if (map->desc)
388  format_line(fp, 1, buf, map->macro, map->desc, wraplen);
389  else
390  format_line(fp, -1, buf, "macro", map->macro, wraplen);
391  }
392  else
393  {
394  b = help_lookup_function(map->op, menu);
395  format_line(fp, 0, buf, b ? b->name : "UNKNOWN",
396  b ? _(OpStrings[b->op][1]) : _("ERROR: please report this bug"), wraplen);
397  }
398  }
399  }
400 }
401 
408 static bool is_bound(struct Keymap *map, int op)
409 {
410  for (; map; map = map->next)
411  if (map->op == op)
412  return true;
413  return false;
414 }
415 
424 static void dump_unbound(FILE *fp, const struct Binding *funcs,
425  struct Keymap *map, struct Keymap *aux, int wraplen)
426 {
427  for (int i = 0; funcs[i].name; i++)
428  {
429  if (!is_bound(map, funcs[i].op) && (!aux || !is_bound(aux, funcs[i].op)))
430  format_line(fp, 0, funcs[i].name, "", _(OpStrings[funcs[i].op][1]), wraplen);
431  }
432 }
433 
439 void mutt_help(enum MenuType menu, int wraplen)
440 {
441  char buf[128];
442  FILE *fp = NULL;
443 
444  /* We don't use the buffer pool because of the extended lifetime of t */
445  struct Buffer t = mutt_buffer_make(PATH_MAX);
446  mutt_buffer_mktemp(&t);
447 
448  const struct Binding *funcs = km_get_table(menu);
449  const char *desc = mutt_map_get_name(menu, Menus);
450  if (!desc)
451  desc = _("<UNKNOWN>");
452 
453  do
454  {
455  fp = mutt_file_fopen(mutt_b2s(&t), "w");
456  if (!fp)
457  {
458  mutt_perror(mutt_b2s(&t));
459  goto cleanup;
460  }
461 
462  dump_menu(fp, menu, wraplen);
463  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
464  {
465  fprintf(fp, "\n%s\n\n", _("Generic bindings:"));
466  dump_menu(fp, MENU_GENERIC, wraplen);
467  }
468 
469  fprintf(fp, "\n%s\n\n", _("Unbound functions:"));
470  if (funcs)
471  dump_unbound(fp, funcs, Keymaps[menu], NULL, wraplen);
472  if (menu != MENU_PAGER)
473  dump_unbound(fp, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu], wraplen);
474 
475  mutt_file_fclose(&fp);
476 
477  snprintf(buf, sizeof(buf), _("Help for %s"), desc);
478  } while (mutt_do_pager(buf, mutt_b2s(&t),
480  NULL) == OP_REFORMAT_WINCH);
481 
482 cleanup:
484 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
Convenience wrapper for the gui headers.
static void dump_unbound(FILE *fp, const struct Binding *funcs, struct Keymap *map, struct Keymap *aux, int wraplen)
Write out all the operations with no key bindings.
Definition: help.c:424
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:81
Manage keymappings.
#define mutt_perror(...)
Definition: logging.h:85
#define IsWPrint(wc)
Definition: mbyte.h:40
MenuType
Types of GUI selections.
Definition: keymap.h:70
int op
function id number
Definition: keymap.h:121
struct Keymap * Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:149
static int const char * fmt
Definition: acutest.h:488
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:140
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:54
#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:282
Hundreds of global variables to back the user variables.
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:58
Some miscellaneous functions.
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:211
Pager pager (email viewer)
Definition: keymap.h:79
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
Many unsorted constants and some structs.
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:55
const char * name
name of the function
Definition: keymap.h:120
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
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: pager.h:52
const char * OpStrings[][2]
Definition: opcodes.c:28
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function&#39;s mapping in a Menu.
Definition: keymap.c:899
#define mutt_b2s(buf)
Definition: buffer.h:41
Prototypes for many functions.
const struct Mapping Menus[]
Menu name lookup table.
Definition: keymap.c:61
struct Keymap * next
next key in map
Definition: keymap.h:51
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1338
#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:871
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:372
static bool is_bound(struct Keymap *map, int op)
Does a function have a keybinding?
Definition: help.c:408
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:639
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: pager.h:54
Definitions of user functions.
int n
Definition: acutest.h:492
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:665
void mutt_help(enum MenuType menu, int wraplen)
Display the help menu.
Definition: help.c:439
const struct Binding * km_get_table(enum MenuType menu)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1265
Mapping between user-readable string and a constant.
Definition: mapping.h:31
Text entry area.
Definition: keymap.h:75
WHERE char * C_Pager
Config: External command for viewing messages, or &#39;builtin&#39; to use NeoMutt&#39;s.
Definition: globals.h:128
void mutt_make_help(char *buf, size_t buflen, const char *txt, enum MenuType menu, int op)
Create one entry for the help bar.
Definition: help.c:87
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: pager.h:53
const char * name
Definition: mapping.h:33
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
char * mutt_compile_help(char *buf, size_t buflen, enum MenuType menu, const struct Mapping *items)
Create the text for the help menu.
Definition: help.c:110
WHERE bool C_Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: globals.h:233
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:253
static unsigned char * pbuf
Cache PGP data packet.
Definition: pgppacket.c:38
Mapping between a user key and a function.
Definition: keymap.h:118
Generic selection list.
Definition: keymap.h:77