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