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