NeoMutt  2021-10-22-8-g9cb437
Teaching an old dog new tricks
DOXYGEN
help.c File Reference

Generate the help-page and GUI display it. More...

#include "config.h"
#include <stddef.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "menu/lib.h"
#include "pager/lib.h"
#include "functions.h"
#include "keymap.h"
#include "muttlib.h"
#include "opcodes.h"
#include "protos.h"
+ Include dependency graph for help.c:

Go to the source code of this file.

Functions

static const struct Bindinghelp_lookup_function (int op, enum MenuType menu)
 Find a keybinding for an operation. More...
 
static int print_macro (FILE *fp, int maxwidth, const char **macro)
 Print a macro string to a file. More...
 
static int get_wrapped_width (const char *t, size_t wid)
 Wrap a string at a sensible place. More...
 
static int pad (FILE *fp, int col, int i)
 Write some padding to a file. More...
 
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. More...
 
static void dump_menu (FILE *fp, enum MenuType menu, int wraplen)
 Write all the key bindings to a file. More...
 
static bool is_bound (struct KeymapList *km_list, int op)
 Does a function have a keybinding? More...
 
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. More...
 
void mutt_help (enum MenuType menu)
 Display the help menu. More...
 

Detailed Description

Generate the help-page and GUI display it.

Authors
  • Michael R. Elkins

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file help.c.

Function Documentation

◆ help_lookup_function()

static const struct Binding* help_lookup_function ( int  op,
enum MenuType  menu 
)
static

Find a keybinding for an operation.

Parameters
opOperation, e.g. OP_DELETE
menuCurrent Menu, e.g. MENU_PAGER
Return values
ptrKey binding
NULLNo key binding found

Definition at line 55 of file help.c.

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 }
const struct Binding OpGeneric[]
Key bindings for the generic menu.
Definition: functions.c:53
const struct Binding * km_get_table(enum MenuType mtype)
Lookup a menu's keybindings.
Definition: keymap.c:1301
Mapping between a user key and a function.
Definition: keymap.h:92
int op
function id number
Definition: keymap.h:94
const char * name
name of the function
Definition: keymap.h:93
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ print_macro()

static int print_macro ( FILE *  fp,
int  maxwidth,
const char **  macro 
)
static

Print a macro string to a file.

Parameters
[in]fpFile to write to
[in]maxwidthMaximum width in screen columns
[out]macroMacro string
Return values
numNumber of screen columns used

The macro pointer is move past the string we've printed

Definition at line 87 of file help.c.

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 }
#define IsWPrint(wc)
Definition: mbyte.h:39
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:57
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_wrapped_width()

static int get_wrapped_width ( const char *  t,
size_t  wid 
)
static

Wrap a string at a sensible place.

Parameters
tString to wrap
widMaximum width
Return values
numBreak after this many characters

If the string's too long, look for some whitespace to break at.

Definition at line 158 of file help.c.

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 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pad()

static int pad ( FILE *  fp,
int  col,
int  i 
)
static

Write some padding to a file.

Parameters
fpFile to write to
colCurrent screen column
iScreen column to pad until
Return values
num
  • i - Padding was added
  • col - Content was already wider than col

Definition at line 200 of file help.c.

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 }
+ Here is the caller graph for this function:

◆ format_line()

static void format_line ( FILE *  fp,
int  ismacro,
const char *  t1,
const char *  t2,
const char *  t3,
int  wraplen 
)
static

Write a formatted line to a file.

Parameters
fpFile to write to
ismacroLayout mode, see below
t1Text part 1
t2Text part 2
t3Text part 3
wraplenWidth to wrap to

Assemble the three columns of text.

ismacro can be:

  • 1 : Macro with a description
  • 0 : Non-macro
  • -1 : Macro with no description

Definition at line 229 of file help.c.

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 }
int mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:988
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:200
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:158
static int print_macro(FILE *fp, int maxwidth, const char **macro)
Print a macro string to a file.
Definition: help.c:87
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
#define SKIPWS(ch)
Definition: string2.h:46
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ dump_menu()

static void dump_menu ( FILE *  fp,
enum MenuType  menu,
int  wraplen 
)
static

Write all the key bindings to a file.

Parameters
fpFile to write to
menuCurrent Menu, e.g. MENU_PAGER
wraplenWidth to wrap to

Definition at line 321 of file help.c.

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 }
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 struct Binding * help_lookup_function(int op, enum MenuType menu)
Find a keybinding for an operation.
Definition: help.c:55
struct KeymapList Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:120
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:910
#define _(a)
Definition: message.h:28
const char * OpStrings[][2]
Definition: opcodes.c:34
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
A keyboard mapping.
Definition: keymap.h:49
char * macro
macro expansion (op == OP_MACRO)
Definition: keymap.h:50
char * desc
description of a macro for the help menu
Definition: keymap.h:51
short op
operation to perform
Definition: keymap.h:52
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_bound()

static bool is_bound ( struct KeymapList *  km_list,
int  op 
)
static

Does a function have a keybinding?

Parameters
km_listKeymap to examine
opOperation, e.g. OP_DELETE
Return values
trueA key is bound to that operation

Definition at line 356 of file help.c.

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 }
+ Here is the caller graph for this function:

◆ dump_unbound()

static void dump_unbound ( FILE *  fp,
const struct Binding funcs,
struct KeymapList *  km_list,
struct KeymapList *  aux,
int  wraplen 
)
static

Write out all the operations with no key bindings.

Parameters
fpFile to write to
funcsAll the bindings for the current menu
km_listFirst key map to consider
auxSecond key map to consider
wraplenWidth to wrap to

Definition at line 375 of file help.c.

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 }
static bool is_bound(struct KeymapList *km_list, int op)
Does a function have a keybinding?
Definition: help.c:356
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_help()

void mutt_help ( enum MenuType  menu)

Display the help menu.

Parameters
menuCurrent Menu

Definition at line 389 of file help.c.

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);
436  pview.banner = banner;
437  } while (mutt_do_pager(&pview, NULL) == OP_REFORMAT_WINCH);
438 
439 cleanup:
441 }
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
struct MuttWindow * AllDialogsWindow
Parent of all Dialogs.
Definition: dialog.c:74
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
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
#define mutt_perror(...)
Definition: logging.h:88
static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
Write all the key bindings to a file.
Definition: help.c:321
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
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
#define PATH_MAX
Definition: mutt.h:40
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: lib.h:70
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:68
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:72
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:69
@ PAGER_MODE_HELP
Pager is invoked via 3rd path to show help.
Definition: lib.h:139
String manipulation buffer.
Definition: buffer.h:34
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Data to be displayed by PagerView.
Definition: lib.h:149
const char * fname
Name of the file to read.
Definition: lib.h:153
Paged view into some data.
Definition: lib.h:160
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:161
enum PagerMode mode
Pager mode.
Definition: lib.h:162
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:163
const char * banner
Title to display in status bar.
Definition: lib.h:164
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
const struct Mapping MenuNames[]
Menu name lookup table.
Definition: type.c:31
@ MENU_GENERIC
Generic selection list.
Definition: type.h:45
@ MENU_EDITOR
Text entry area.
Definition: type.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function: