NeoMutt  2022-04-29-215-gc12b98
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:288
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: keymap.c:1230
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:567
+ 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}
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
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:907
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
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
#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] = { 0 };
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 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
static const struct MenuFuncOp * 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:126
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:928
#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] = { 0 };
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);
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
436cleanup:
438}
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:67
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:309
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:123
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
#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: