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