NeoMutt  2019-12-07-60-g0cfa53
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/mutt.h"
37 #include "gui/lib.h"
38 #include "globals.h"
39 #include "keymap.h"
40 #include "muttlib.h"
41 #include "opcodes.h"
42 #include "pager.h"
43 #include "protos.h" // IWYU pragma: keep
44 
45 static const char *HelpStrings[] = {
46 #define DEFINE_HELP_MESSAGE(opcode, help_string) help_string,
48 #undef DEFINE_HELP_MESSAGE
49  NULL,
50 };
51 
59 static const struct Binding *help_lookup_function(int op, enum MenuType menu)
60 {
61  const struct Binding *map = NULL;
62 
63  if (menu != MENU_PAGER)
64  {
65  /* first look in the generic map for the function */
66  for (int i = 0; OpGeneric[i].name; i++)
67  if (OpGeneric[i].op == op)
68  return &OpGeneric[i];
69  }
70 
71  map = km_get_table(menu);
72  if (map)
73  {
74  for (int i = 0; map[i].name; i++)
75  if (map[i].op == op)
76  return &map[i];
77  }
78 
79  return NULL;
80 }
81 
92 void mutt_make_help(char *buf, size_t buflen, const char *txt, enum MenuType menu, int op)
93 {
94  char tmp[128];
95 
96  if (km_expand_key(tmp, sizeof(tmp), km_find_func(menu, op)) ||
97  km_expand_key(tmp, sizeof(tmp), km_find_func(MENU_GENERIC, op)))
98  {
99  snprintf(buf, buflen, "%s:%s", tmp, txt);
100  }
101  else
102  {
103  buf[0] = '\0';
104  }
105 }
106 
115 char *mutt_compile_help(char *buf, size_t buflen, enum MenuType menu,
116  const struct Mapping *items)
117 {
118  char *pbuf = buf;
119 
120  for (int i = 0; items[i].name && buflen > 2; i++)
121  {
122  if (i)
123  {
124  *pbuf++ = ' ';
125  *pbuf++ = ' ';
126  buflen -= 2;
127  }
128  mutt_make_help(pbuf, buflen, _(items[i].name), menu, items[i].value);
129  const size_t len = mutt_str_strlen(pbuf);
130  pbuf += len;
131  buflen -= len;
132  }
133  return buf;
134 }
135 
145 static int print_macro(FILE *fp, int maxwidth, const char **macro)
146 {
147  int n = maxwidth;
148  wchar_t wc;
149  size_t k;
150  size_t len = mutt_str_strlen(*macro);
151  mbstate_t mbstate1, mbstate2;
152 
153  memset(&mbstate1, 0, sizeof(mbstate1));
154  memset(&mbstate2, 0, sizeof(mbstate2));
155  for (; len && (k = mbrtowc(&wc, *macro, len, &mbstate1)); *macro += k, len -= k)
156  {
157  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
158  {
159  if (k == (size_t)(-1))
160  memset(&mbstate1, 0, sizeof(mbstate1));
161  k = (k == (size_t)(-1)) ? 1 : len;
162  wc = ReplacementChar;
163  }
164  /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
165  const int w = wcwidth(wc);
166  if (IsWPrint(wc) && (w >= 0))
167  {
168  if (w > n)
169  break;
170  n -= w;
171  {
172  char buf[MB_LEN_MAX * 2];
173  size_t n1, n2;
174  if (((n1 = wcrtomb(buf, wc, &mbstate2)) != (size_t)(-1)) &&
175  ((n2 = wcrtomb(buf + n1, 0, &mbstate2)) != (size_t)(-1)))
176  {
177  fputs(buf, fp);
178  }
179  }
180  }
181  else if ((wc < 0x20) || (wc == 0x7f))
182  {
183  if (n < 2)
184  break;
185  n -= 2;
186  if (wc == '\033') // Escape
187  fprintf(fp, "\\e");
188  else if (wc == '\n')
189  fprintf(fp, "\\n");
190  else if (wc == '\r')
191  fprintf(fp, "\\r");
192  else if (wc == '\t')
193  fprintf(fp, "\\t");
194  else
195  fprintf(fp, "^%c", (char) ((wc + '@') & 0x7f));
196  }
197  else
198  {
199  if (n < 1)
200  break;
201  n -= 1;
202  fprintf(fp, "?");
203  }
204  }
205  return maxwidth - n;
206 }
207 
216 static int get_wrapped_width(const char *t, size_t wid)
217 {
218  wchar_t wc;
219  size_t k;
220  size_t m, n;
221  size_t len = mutt_str_strlen(t);
222  const char *s = t;
223  mbstate_t mbstate;
224 
225  memset(&mbstate, 0, sizeof(mbstate));
226  for (m = wid, n = 0; len && (k = mbrtowc(&wc, s, len, &mbstate)) && (n <= wid);
227  s += k, len -= k)
228  {
229  if (*s == ' ')
230  m = n;
231  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
232  {
233  if (k == (size_t)(-1))
234  memset(&mbstate, 0, sizeof(mbstate));
235  k = (k == (size_t)(-1)) ? 1 : len;
236  wc = ReplacementChar;
237  }
238  if (!IsWPrint(wc))
239  wc = '?';
240  n += wcwidth(wc);
241  }
242  if (n > wid)
243  n = m;
244  else
245  n = wid;
246  return n;
247 }
248 
257 static int pad(FILE *fp, int col, int i)
258 {
259  if (col < i)
260  {
261  char fmt[32] = { 0 };
262  snprintf(fmt, sizeof(fmt), "%%-%ds", i - col);
263  fprintf(fp, fmt, "");
264  return i;
265  }
266  fputc(' ', fp);
267  return col + 1;
268 }
269 
286 static void format_line(FILE *fp, int ismacro, const char *t1, const char *t2,
287  const char *t3, int wraplen)
288 {
289  int col;
290  int col_b;
291 
292  fputs(t1, fp);
293 
294  /* don't try to press string into one line with less than 40 characters. */
295  bool split = (wraplen < 40);
296  if (split)
297  {
298  col = 0;
299  col_b = 1024;
300  fputc('\n', fp);
301  }
302  else
303  {
304  const int col_a = (wraplen > 83) ? (wraplen - 32) >> 2 : 12;
305  col_b = (wraplen > 49) ? (wraplen - 10) >> 1 : 19;
306  col = pad(fp, mutt_strwidth(t1), col_a);
307  }
308 
309  if (ismacro > 0)
310  {
311  if (!C_Pager || (mutt_str_strcmp(C_Pager, "builtin") == 0))
312  fputs("_\010", fp); // Ctrl-H (backspace)
313  fputs("M ", fp);
314  col += 2;
315 
316  if (!split)
317  {
318  col += print_macro(fp, col_b - col - 4, &t2);
319  if (mutt_strwidth(t2) > col_b - col)
320  t2 = "...";
321  }
322  }
323 
324  col += print_macro(fp, col_b - col - 1, &t2);
325  if (split)
326  fputc('\n', fp);
327  else
328  col = pad(fp, col, col_b);
329 
330  if (split)
331  {
332  print_macro(fp, 1024, &t3);
333  fputc('\n', fp);
334  }
335  else
336  {
337  while (*t3)
338  {
339  int n = wraplen - col;
340 
341  if (ismacro >= 0)
342  {
343  SKIPWS(t3);
344  n = get_wrapped_width(t3, n);
345  }
346 
347  n = print_macro(fp, n, &t3);
348 
349  if (*t3)
350  {
351  if (mutt_str_strcmp(C_Pager, "builtin") == 0)
352  {
353  n += col - wraplen;
354  if (C_Markers)
355  n++;
356  }
357  else
358  {
359  fputc('\n', fp);
360  n = 0;
361  }
362  col = pad(fp, n, col_b);
363  }
364  }
365  }
366 
367  fputc('\n', fp);
368 }
369 
376 static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
377 {
378  struct Keymap *map = NULL;
379  const struct Binding *b = NULL;
380  char buf[128];
381 
382  /* browse through the keymap table */
383  for (map = Keymaps[menu]; map; map = map->next)
384  {
385  if (map->op != OP_NULL)
386  {
387  km_expand_key(buf, sizeof(buf), map);
388 
389  if (map->op == OP_MACRO)
390  {
391  if (map->desc)
392  format_line(fp, 1, buf, map->macro, map->desc, wraplen);
393  else
394  format_line(fp, -1, buf, "macro", map->macro, wraplen);
395  }
396  else
397  {
398  b = help_lookup_function(map->op, menu);
399  format_line(fp, 0, buf, b ? b->name : "UNKNOWN",
400  b ? _(HelpStrings[b->op]) : _("ERROR: please report this bug"), wraplen);
401  }
402  }
403  }
404 }
405 
412 static bool is_bound(struct Keymap *map, int op)
413 {
414  for (; map; map = map->next)
415  if (map->op == op)
416  return true;
417  return false;
418 }
419 
428 static void dump_unbound(FILE *fp, const struct Binding *funcs,
429  struct Keymap *map, struct Keymap *aux, int wraplen)
430 {
431  for (int i = 0; funcs[i].name; i++)
432  {
433  if (!is_bound(map, funcs[i].op) && (!aux || !is_bound(aux, funcs[i].op)))
434  format_line(fp, 0, funcs[i].name, "", _(HelpStrings[funcs[i].op]), wraplen);
435  }
436 }
437 
443 void mutt_help(enum MenuType menu, int wraplen)
444 {
445  char buf[128];
446  FILE *fp = NULL;
447 
448  /* We don't use the buffer pool because of the extended lifetime of t */
449  struct Buffer t = mutt_buffer_make(PATH_MAX);
450  mutt_buffer_mktemp(&t);
451 
452  const struct Binding *funcs = km_get_table(menu);
453  const char *desc = mutt_map_get_name(menu, Menus);
454  if (!desc)
455  desc = _("<UNKNOWN>");
456 
457  do
458  {
459  fp = mutt_file_fopen(mutt_b2s(&t), "w");
460  if (!fp)
461  {
462  mutt_perror(mutt_b2s(&t));
463  goto cleanup;
464  }
465 
466  dump_menu(fp, menu, wraplen);
467  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
468  {
469  fprintf(fp, "\n%s\n\n", _("Generic bindings:"));
470  dump_menu(fp, MENU_GENERIC, wraplen);
471  }
472 
473  fprintf(fp, "\n%s\n\n", _("Unbound functions:"));
474  if (funcs)
475  dump_unbound(fp, funcs, Keymaps[menu], NULL, wraplen);
476  if (menu != MENU_PAGER)
477  dump_unbound(fp, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu], wraplen);
478 
479  mutt_file_fclose(&fp);
480 
481  snprintf(buf, sizeof(buf), _("Help for %s"), desc);
482  } while (mutt_do_pager(buf, mutt_b2s(&t),
484  NULL) == OP_REFORMAT_WINCH);
485 
486 cleanup:
488 }
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:428
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:78
Manage keymappings.
#define mutt_perror(...)
Definition: logging.h:85
#define IsWPrint(wc)
Definition: mbyte.h:40
MenuType
Types of GUI selections.
Definition: keymap.h:69
int op
function id number
Definition: keymap.h:117
struct Keymap * Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:147
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:145
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:59
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: pager.h:56
All user-callable functions.
#define OPS(_fmt)
Definition: opcodes.h:311
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:286
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:59
Some miscellaneous functions.
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:216
Pager pager (email viewer)
Definition: keymap.h:78
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:152
const char * name
Definition: pgpmicalg.c:46
const char * name
name of the function
Definition: keymap.h:116
short op
operation to perform
Definition: keymap.h:51
#define SKIPWS(ch)
Definition: string2.h:47
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
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function&#39;s mapping in a Menu.
Definition: keymap.c:854
#define mutt_b2s(buf)
Definition: buffer.h:41
Prototypes for many functions.
const struct Mapping Menus[]
Menu name lookup table.
Definition: keymap.c:60
struct Keymap * next
next key in map
Definition: keymap.h:50
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:50
const struct Binding OpGeneric[]
Key bindings for the generic menu.
Definition: functions.h:54
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:826
char * macro
macro expansion (op == OP_MACRO)
Definition: keymap.h:48
static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
Write all the key bindings to a file.
Definition: help.c:376
static bool is_bound(struct Keymap *map, int op)
Does a function have a keybinding?
Definition: help.c:412
char * desc
description of a macro for the help menu
Definition: keymap.h:49
A keyboard mapping.
Definition: keymap.h:46
GUI display a file/email/help in a viewport with paging.
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: pager.h:54
static const char * HelpStrings[]
Definition: help.c:45
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:664
void mutt_help(enum MenuType menu, int wraplen)
Display the help menu.
Definition: help.c:443
const struct Binding * km_get_table(enum MenuType menu)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1221
Mapping between user-readable string and a constant.
Definition: mapping.h:29
Text entry area.
Definition: keymap.h:74
WHERE char * C_Pager
Config: External command for viewing messages, or &#39;builtin&#39; to use NeoMutt&#39;s.
Definition: globals.h:132
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:92
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: pager.h:53
const char * name
Definition: mapping.h:31
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:585
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:115
WHERE bool C_Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: globals.h:241
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:257
static unsigned char * pbuf
Cache PGP data packet.
Definition: pgppacket.c:38
Mapping between a user key and a function.
Definition: keymap.h:114
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
#define DEFINE_HELP_MESSAGE(opcode, help_string)
Generic selection list.
Definition: keymap.h:76