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