NeoMutt  2022-04-29-70-g0c028c
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 MenuFuncOphelp_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 MenuFuncOp *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 MenuFuncOp* 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  if (menu != MENU_PAGER && (menu != MENU_GENERIC))
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  const struct MenuFuncOp *funcs = km_get_table(menu);
66  if (funcs)
67  {
68  for (int i = 0; funcs[i].name; i++)
69  if (funcs[i].op == op)
70  return &funcs[i];
71  }
72 
73  return NULL;
74 }
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:287
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: keymap.c:1231
Mapping between a function and an operation.
Definition: keymap.h:92
const char * name
Name of the function.
Definition: keymap.h:93
int op
Operation, e.g. OP_DELETE.
Definition: keymap.h:94
@ MENU_GENERIC
Generic selection list.
Definition: type.h:45
@ 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 85 of file help.c.

86 {
87  int n = maxwidth;
88  wchar_t wc = 0;
89  size_t k;
90  size_t len = mutt_str_len(*macro);
91  mbstate_t mbstate1 = { 0 };
92  mbstate_t mbstate2 = { 0 };
93 
94  for (; len && (k = mbrtowc(&wc, *macro, len, &mbstate1)); *macro += k, len -= k)
95  {
96  if ((k == (size_t) (-1)) || (k == (size_t) (-2)))
97  {
98  if (k == (size_t) (-1))
99  memset(&mbstate1, 0, sizeof(mbstate1));
100  k = (k == (size_t) (-1)) ? 1 : len;
101  wc = ReplacementChar;
102  }
103  /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
104  const int w = wcwidth(wc);
105  if (IsWPrint(wc) && (w >= 0))
106  {
107  if (w > n)
108  break;
109  n -= w;
110  {
111  char buf[MB_LEN_MAX * 2];
112  size_t n1, n2;
113  if (((n1 = wcrtomb(buf, wc, &mbstate2)) != (size_t) (-1)) &&
114  ((n2 = wcrtomb(buf + n1, 0, &mbstate2)) != (size_t) (-1)))
115  {
116  fputs(buf, fp);
117  }
118  }
119  }
120  else if ((wc < 0x20) || (wc == 0x7f))
121  {
122  if (n < 2)
123  break;
124  n -= 2;
125  if (wc == '\033') // Escape
126  fprintf(fp, "\\e");
127  else if (wc == '\n')
128  fprintf(fp, "\\n");
129  else if (wc == '\r')
130  fprintf(fp, "\\r");
131  else if (wc == '\t')
132  fprintf(fp, "\\t");
133  else
134  fprintf(fp, "^%c", (char) ((wc + '@') & 0x7f));
135  }
136  else
137  {
138  if (n < 1)
139  break;
140  n -= 1;
141  fprintf(fp, "?");
142  }
143  }
144  return maxwidth - n;
145 }
#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:544
+ 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 155 of file help.c.

156 {
157  wchar_t wc = 0;
158  size_t k;
159  size_t m, n;
160  size_t len = mutt_str_len(t);
161  const char *s = t;
162  mbstate_t mbstate = { 0 };
163 
164  for (m = wid, n = 0; len && (k = mbrtowc(&wc, s, len, &mbstate)) && (n <= wid);
165  s += k, len -= k)
166  {
167  if (*s == ' ')
168  m = n;
169  if ((k == (size_t) (-1)) || (k == (size_t) (-2)))
170  {
171  if (k == (size_t) (-1))
172  memset(&mbstate, 0, sizeof(mbstate));
173  k = (k == (size_t) (-1)) ? 1 : len;
174  wc = ReplacementChar;
175  }
176  if (!IsWPrint(wc))
177  wc = '?';
178  n += wcwidth(wc);
179  }
180  if (n > wid)
181  n = m;
182  else
183  n = wid;
184  return n;
185 }
+ 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 196 of file help.c.

197 {
198  if (col < i)
199  {
200  char fmt[32] = { 0 };
201  snprintf(fmt, sizeof(fmt), "%%-%ds", i - col);
202  fprintf(fp, fmt, "");
203  return i;
204  }
205  fputc(' ', fp);
206  return col + 1;
207 }
+ 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 225 of file help.c.

227 {
228  int col;
229  int col_b;
230 
231  fputs(t1, fp);
232 
233  /* don't try to press string into one line with less than 40 characters. */
234  bool split = (wraplen < 40);
235  if (split)
236  {
237  col = 0;
238  col_b = 1024;
239  fputc('\n', fp);
240  }
241  else
242  {
243  const int col_a = (wraplen > 83) ? (wraplen - 32) >> 2 : 12;
244  col_b = (wraplen > 49) ? (wraplen - 10) >> 1 : 19;
245  col = pad(fp, mutt_strwidth(t1), col_a);
246  }
247 
248  const char *const c_pager = cs_subset_string(NeoMutt->sub, "pager");
249  if (ismacro > 0)
250  {
251  if (!c_pager || mutt_str_equal(c_pager, "builtin"))
252  fputs("_\010", fp); // Ctrl-H (backspace)
253  fputs("M ", fp);
254  col += 2;
255 
256  if (!split)
257  {
258  col += print_macro(fp, col_b - col - 4, &t2);
259  if (mutt_strwidth(t2) > col_b - col)
260  t2 = "...";
261  }
262  }
263 
264  col += print_macro(fp, col_b - col - 1, &t2);
265  if (split)
266  fputc('\n', fp);
267  else
268  col = pad(fp, col, col_b);
269 
270  if (split)
271  {
272  print_macro(fp, 1024, &t3);
273  fputc('\n', fp);
274  }
275  else
276  {
277  while (*t3)
278  {
279  int n = wraplen - col;
280 
281  if (ismacro >= 0)
282  {
283  SKIPWS(t3);
284  n = get_wrapped_width(t3, n);
285  }
286 
287  n = print_macro(fp, n, &t3);
288 
289  if (*t3)
290  {
291  if (mutt_str_equal(c_pager, "builtin"))
292  {
293  n += col - wraplen;
294  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
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 }
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:1014
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:196
static int get_wrapped_width(const char *t, size_t wid)
Wrap a string at a sensible place.
Definition: help.c:155
static int print_macro(FILE *fp, int maxwidth, const char **macro)
Print a macro string to a file.
Definition: help.c:85
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:784
#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 317 of file help.c.

318 {
319  struct Keymap *map = NULL;
320  char buf[128];
321 
322  STAILQ_FOREACH(map, &Keymaps[menu], entries)
323  {
324  if (map->op != OP_NULL)
325  {
326  km_expand_key(buf, sizeof(buf), map);
327 
328  if (map->op == OP_MACRO)
329  {
330  if (map->desc)
331  format_line(fp, 1, buf, map->macro, map->desc, wraplen);
332  else
333  format_line(fp, -1, buf, "macro", map->macro, wraplen);
334  }
335  else
336  {
337  const struct MenuFuncOp *funcs = help_lookup_function(map->op, menu);
338  format_line(fp, 0, buf, funcs ? funcs->name : "UNKNOWN",
339  funcs ? _(opcodes_get_description(funcs->op)) :
340  _("ERROR: please report this bug"),
341  wraplen);
342  }
343  }
344  }
345 }
static const struct MenuFuncOp * help_lookup_function(int op, enum MenuType menu)
Find a keybinding for an operation.
Definition: help.c:55
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:225
struct KeymapList Keymaps[MENU_MAX]
Array of Keymap keybindings, one for each Menu.
Definition: keymap.c:126
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:929
#define _(a)
Definition: message.h:28
const char * opcodes_get_description(int op)
Get the description of an opcode.
Definition: opcodes.c:64
#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 353 of file help.c.

354 {
355  struct Keymap *map = NULL;
356  STAILQ_FOREACH(map, km_list, entries)
357  {
358  if (map->op == op)
359  return true;
360  }
361  return false;
362 }
+ Here is the caller graph for this function:

◆ dump_unbound()

static void dump_unbound ( FILE *  fp,
const struct MenuFuncOp 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 372 of file help.c.

374 {
375  for (int i = 0; funcs[i].name; i++)
376  {
377  if (!is_bound(km_list, funcs[i].op) && (!aux || !is_bound(aux, funcs[i].op)))
378  format_line(fp, 0, funcs[i].name, "", _(opcodes_get_description(funcs[i].op)), wraplen);
379  }
380 }
static bool is_bound(struct KeymapList *km_list, int op)
Does a function have a keybinding?
Definition: help.c:353
+ 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 386 of file help.c.

387 {
388  char banner[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 MenuFuncOp *funcs = km_get_table(menu);
396  const char *desc = mutt_map_get_name(menu, MenuNames);
397  if (!desc)
398  desc = _("<UNKNOWN>");
399 
400  struct PagerData pdata = { 0 };
401  struct PagerView pview = { &pdata };
402 
403  pview.mode = PAGER_MODE_HELP;
405 
406  do
407  {
408  fp = mutt_file_fopen(mutt_buffer_string(&t), "w");
409  if (!fp)
410  {
412  goto cleanup;
413  }
414 
415  const int wraplen = AllDialogsWindow->state.cols;
416  dump_menu(fp, menu, wraplen);
417  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER) && (menu != MENU_GENERIC))
418  {
419  fprintf(fp, "\n%s\n\n", _("Generic bindings:"));
420  dump_menu(fp, MENU_GENERIC, wraplen);
421  }
422 
423  fprintf(fp, "\n%s\n\n", _("Unbound functions:"));
424  if (funcs)
425  dump_unbound(fp, funcs, &Keymaps[menu], NULL, wraplen);
426  if ((menu != MENU_EDITOR) && (menu != MENU_PAGER) && (menu != MENU_GENERIC))
427  dump_unbound(fp, OpGeneric, &Keymaps[MENU_GENERIC], &Keymaps[menu], wraplen);
428 
429  mutt_file_fclose(&fp);
430 
431  snprintf(banner, sizeof(banner), _("Help for %s"), desc);
433  pview.banner = banner;
434  } while (mutt_do_pager(&pview, NULL) == OP_REFORMAT_WINCH);
435 
436 cleanup:
438 }
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:152
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
#define mutt_perror(...)
Definition: logging.h:88
static void dump_unbound(FILE *fp, const struct MenuFuncOp *funcs, struct KeymapList *km_list, struct KeymapList *aux, int wraplen)
Write out all the operations with no key bindings.
Definition: help.c:372
static void dump_menu(FILE *fp, enum MenuType menu, int wraplen)
Write all the key bindings to a file.
Definition: help.c:317
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:69
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:67
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:71
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:68
@ PAGER_MODE_HELP
Pager is invoked via 3rd path to show help.
Definition: lib.h:138
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:158
const char * fname
Name of the file to read.
Definition: lib.h:162
Paged view into some data.
Definition: lib.h:169
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:170
enum PagerMode mode
Pager mode.
Definition: lib.h:171
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:172
const char * banner
Title to display in status bar.
Definition: lib.h:173
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_EDITOR
Text entry area.
Definition: type.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function: