From: Arnaud Lacombe <al@sigfpe.info>
[apps/madmutt.git] / help.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #define HELP_C
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include "lib/intl.h"
17 #include "lib/str.h"
18
19 #include "mutt.h"
20 #include "mutt_curses.h"
21 #include "keymap.h"
22 #include "pager.h"
23 #include "mapping.h"
24
25 #include <ctype.h>
26 #include <string.h>
27
28 static struct binding_t *help_lookupFunction (int op, int menu)
29 {
30   int i;
31   struct binding_t *map;
32
33   if (menu != MENU_PAGER) {
34     /* first look in the generic map for the function */
35     for (i = 0; OpGeneric[i].name; i++)
36       if (OpGeneric[i].op == op)
37         return (&OpGeneric[i]);
38   }
39
40   if ((map = km_get_table (menu))) {
41     for (i = 0; map[i].name; i++)
42       if (map[i].op == op)
43         return (&map[i]);
44   }
45
46   return (NULL);
47 }
48
49 void mutt_make_help (char *d, size_t dlen, char *txt, int menu, int op)
50 {
51   char buf[SHORT_STRING];
52
53   if (km_expand_key (buf, sizeof (buf), km_find_func (menu, op)) ||
54       km_expand_key (buf, sizeof (buf), km_find_func (MENU_GENERIC, op)))
55     snprintf (d, dlen, "%s:%s", buf, txt);
56   else
57     d[0] = 0;
58 }
59
60 char *mutt_compile_help (char *buf, size_t buflen, int menu,
61                          struct mapping_t *items)
62 {
63   int i;
64   size_t len;
65   char *pbuf = buf;
66
67   for (i = 0; items[i].name && buflen > 2; i++) {
68     if (i) {
69       *pbuf++ = ' ';
70       *pbuf++ = ' ';
71       buflen -= 2;
72     }
73     mutt_make_help (pbuf, buflen, _(items[i].name), menu, items[i].value);
74     len = str_len (pbuf);
75     pbuf += len;
76     buflen -= len;
77   }
78   return buf;
79 }
80
81 static int print_macro (FILE * f, int maxwidth, const char **macro)
82 {
83   int n = maxwidth;
84   wchar_t wc;
85   int w;
86   size_t k;
87   size_t len = str_len (*macro);
88   mbstate_t mbstate1, mbstate2;
89
90   memset (&mbstate1, 0, sizeof (mbstate1));
91   memset (&mbstate2, 0, sizeof (mbstate2));
92   for (; len && (k = mbrtowc (&wc, *macro, len, &mbstate1));
93        *macro += k, len -= k) {
94     if (k == (size_t) (-1) || k == (size_t) (-2)) {
95       k = (k == (size_t) (-1)) ? 1 : len;
96       wc = replacement_char ();
97     }
98     /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */
99     if (IsWPrint (wc) && (w = wcwidth (wc)) >= 0) {
100       if (w > n)
101         break;
102       n -= w;
103       {
104         char buf[MB_LEN_MAX * 2];
105         size_t n1, n2;
106
107         if ((n1 = wcrtomb (buf, wc, &mbstate2)) != (size_t) (-1) &&
108             (n2 = wcrtomb (buf + n1, 0, &mbstate2)) != (size_t) (-1))
109           fputs (buf, f);
110       }
111     }
112     else if (wc < 0x20 || wc == 0x7f) {
113       if (2 > n)
114         break;
115       n -= 2;
116       if (wc == '\033')
117         fprintf (f, "\\e");
118       else if (wc == '\n')
119         fprintf (f, "\\n");
120       else if (wc == '\r')
121         fprintf (f, "\\r");
122       else if (wc == '\t')
123         fprintf (f, "\\t");
124       else
125         fprintf (f, "^%c", (char) ((wc + '@') & 0x7f));
126     }
127     else {
128       if (1 > n)
129         break;
130       n -= 1;
131       fprintf (f, "?");
132     }
133   }
134   return (maxwidth - n);
135 }
136
137 static int pad (FILE * f, int col, int i)
138 {
139   char fmt[8];
140
141   if (col < i) {
142     snprintf (fmt, sizeof (fmt), "%%-%ds", i - col);
143     fprintf (f, fmt, "");
144     return (i);
145   }
146   fputc (' ', f);
147   return (col + 1);
148 }
149
150 static void format_line (FILE * f, int ismacro,
151                          const char *t1, const char *t2, const char *t3)
152 {
153   int col;
154   int col_a, col_b;
155   int split;
156   int n;
157
158   fputs (t1, f);
159
160   /* don't try to press string into one line with less than 40 characters.
161      The double paranthesis avoid a gcc warning, sigh ... */
162   if ((split = COLS < 40)) {
163     col_a = col = 0;
164     col_b = LONG_STRING;
165     fputc ('\n', f);
166   }
167   else {
168     col_a = COLS > 83 ? (COLS - 32) >> 2 : 12;
169     col_b = COLS > 49 ? (COLS - 10) >> 1 : 19;
170     col = pad (f, str_len (t1), col_a);
171   }
172
173   if (ismacro > 0) {
174     if (!str_cmp (Pager, "builtin"))
175       fputs ("_\010", f);
176     fputs ("M ", f);
177     col += 2;
178
179     if (!split) {
180       col += print_macro (f, col_b - col - 4, &t2);
181       if (str_len (t2) > col_b - col)
182         t2 = "...";
183     }
184   }
185
186   col += print_macro (f, col_b - col - 1, &t2);
187   if (split)
188     fputc ('\n', f);
189   else
190     col = pad (f, col, col_b);
191
192   if (split) {
193     print_macro (f, LONG_STRING, &t3);
194     fputc ('\n', f);
195   }
196   else {
197     while (*t3) {
198       n = COLS - col;
199
200       if (ismacro >= 0) {
201         SKIPWS (t3);
202
203         /* FIXME: this is completely wrong */
204         if ((n = str_len (t3)) > COLS - col) {
205           n = COLS - col;
206           for (col_a = n; col_a > 0 && t3[col_a] != ' '; col_a--);
207           if (col_a)
208             n = col_a;
209         }
210       }
211
212       print_macro (f, n, &t3);
213
214       if (*t3) {
215         if (str_cmp (Pager, "builtin")) {
216           fputc ('\n', f);
217           n = 0;
218         }
219         else {
220           n += col - COLS;
221           if (option (OPTMARKERS))
222             ++n;
223         }
224         col = pad (f, n, col_b);
225       }
226     }
227   }
228
229   fputc ('\n', f);
230 }
231
232 static void dump_menu (FILE * f, int menu)
233 {
234   struct keymap_t *map;
235   struct binding_t *b;
236   char buf[SHORT_STRING];
237
238   /* browse through the keymap table */
239   for (map = Keymaps[menu]; map; map = map->next) {
240     if (map->op != OP_NULL) {
241       km_expand_key (buf, sizeof (buf), map);
242
243       if (map->op == OP_MACRO) {
244         if (map->descr == NULL)
245           format_line (f, -1, buf, "macro", map->macro);
246         else
247           format_line (f, 1, buf, map->macro, map->descr);
248       }
249       else {
250         b = help_lookupFunction (map->op, menu);
251         format_line (f, 0, buf, b ? b->name : "UNKNOWN",
252                      b ? _(HelpStrings[b->op]) :
253                      _("ERROR: please report this bug"));
254       }
255     }
256   }
257 }
258
259 static int is_bound (struct keymap_t *map, int op)
260 {
261   for (; map; map = map->next)
262     if (map->op == op)
263       return 1;
264   return 0;
265 }
266
267 static void dump_unbound (FILE * f,
268                           struct binding_t *funcs,
269                           struct keymap_t *map, struct keymap_t *aux)
270 {
271   int i;
272
273   for (i = 0; funcs[i].name; i++) {
274     if (!is_bound (map, funcs[i].op) &&
275         (!aux || !is_bound (aux, funcs[i].op)))
276       format_line (f, 0, funcs[i].name, "", _(HelpStrings[funcs[i].op]));
277   }
278 }
279
280 void mutt_help (int menu)
281 {
282   char t[_POSIX_PATH_MAX];
283   char buf[SHORT_STRING];
284   char *desc;
285   FILE *f;
286   struct binding_t *funcs;
287
288   mutt_mktemp (t);
289
290   funcs = km_get_table (menu);
291   desc = mutt_getnamebyvalue (menu, Menus);
292   if (!desc)
293     desc = _("<UNKNOWN>");
294
295   do {
296     if ((f = safe_fopen (t, "w")) == NULL) {
297       mutt_perror (t);
298       return;
299     }
300
301     dump_menu (f, menu);
302     if (menu != MENU_EDITOR && menu != MENU_PAGER) {
303       fputs (_("\nGeneric bindings:\n\n"), f);
304       dump_menu (f, MENU_GENERIC);
305     }
306
307     fputs (_("\nUnbound functions:\n\n"), f);
308     if (funcs)
309       dump_unbound (f, funcs, Keymaps[menu], NULL);
310     if (menu != MENU_PAGER)
311       dump_unbound (f, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu]);
312
313     fclose (f);
314
315     snprintf (buf, sizeof (buf), _("Help for %s"), desc);
316   }
317   while
318     (mutt_do_pager (buf, t,
319                     M_PAGER_RETWINCH | M_PAGER_MARKER | M_PAGER_NSKIP, NULL)
320      == OP_REFORMAT_WINCH);
321 }