mutt stable branch with some hacks
at jcs 389 lines 8.6 kB view raw
1/* 2 * Copyright (C) 1996-2000,2009 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 34static const struct binding_t *help_lookupFunction (int op, int menu) 35{ 36 int i; 37 const struct binding_t *map; 38 39 if (menu != MENU_PAGER) 40 { 41 /* first look in the generic map for the function */ 42 for (i = 0; OpGeneric[i].name; i++) 43 if (OpGeneric[i].op == op) 44 return (&OpGeneric[i]); 45 } 46 47 if ((map = km_get_table(menu))) 48 { 49 for (i = 0; map[i].name; i++) 50 if (map[i].op == op) 51 return (&map[i]); 52 } 53 54 return (NULL); 55} 56 57void mutt_make_help (char *d, size_t dlen, const char *txt, int menu, int op) 58{ 59 char buf[SHORT_STRING]; 60 61 if (km_expand_key (buf, sizeof (buf), km_find_func (menu, op)) || 62 km_expand_key (buf, sizeof (buf), km_find_func (MENU_GENERIC, op))) 63 snprintf (d, dlen, "%s:%s", buf, txt); 64 else 65 d[0] = 0; 66} 67 68char * 69mutt_compile_help (char *buf, size_t buflen, int menu, const struct mapping_t *items) 70{ 71 int i; 72 size_t len; 73 char *pbuf = buf; 74 75 for (i = 0; items[i].name && buflen > 2; i++) 76 { 77 if (i) 78 { 79 *pbuf++ = ' '; 80 *pbuf++ = ' '; 81 buflen -= 2; 82 } 83 mutt_make_help (pbuf, buflen, _(items[i].name), menu, items[i].value); 84 len = mutt_strlen (pbuf); 85 pbuf += len; 86 buflen -= len; 87 } 88 return buf; 89} 90 91static int print_macro (FILE *f, int maxwidth, const char **macro) 92{ 93 int n = maxwidth; 94 wchar_t wc; 95 int w; 96 size_t k; 97 size_t len = mutt_strlen (*macro); 98 mbstate_t mbstate1, mbstate2; 99 100 memset (&mbstate1, 0, sizeof (mbstate1)); 101 memset (&mbstate2, 0, sizeof (mbstate2)); 102 for (; len && (k = mbrtowc (&wc, *macro, len, &mbstate1)); *macro += k, len -= k) 103 { 104 if (k == (size_t)(-1) || k == (size_t)(-2)) 105 { 106 if (k == (size_t)(-1)) 107 memset (&mbstate1, 0, sizeof (mbstate1)); 108 k = (k == (size_t)(-1)) ? 1 : len; 109 wc = replacement_char (); 110 } 111 /* glibc-2.1.3's wcwidth() returns 1 for unprintable chars! */ 112 if (IsWPrint (wc) && (w = wcwidth (wc)) >= 0) 113 { 114 if (w > n) 115 break; 116 n -= w; 117 { 118 char buf[MB_LEN_MAX*2]; 119 size_t n1, n2; 120 if ((n1 = wcrtomb (buf, wc, &mbstate2)) != (size_t)(-1) && 121 (n2 = wcrtomb (buf + n1, 0, &mbstate2)) != (size_t)(-1)) 122 fputs (buf, f); 123 } 124 } 125 else if (wc < 0x20 || wc == 0x7f) 126 { 127 if (2 > n) 128 break; 129 n -= 2; 130 if (wc == '\033') 131 fprintf (f, "\\e"); 132 else if (wc == '\n') 133 fprintf (f, "\\n"); 134 else if (wc == '\r') 135 fprintf (f, "\\r"); 136 else if (wc == '\t') 137 fprintf (f, "\\t"); 138 else 139 fprintf (f, "^%c", (char)((wc + '@') & 0x7f)); 140 } 141 else 142 { 143 if (1 > n) 144 break; 145 n -= 1; 146 fprintf (f, "?"); 147 } 148 } 149 return (maxwidth - n); 150} 151 152static int get_wrapped_width (const char *t, size_t wid) 153{ 154 wchar_t wc; 155 size_t k; 156 size_t m, n; 157 size_t len = mutt_strlen (t); 158 const char *s = t; 159 mbstate_t mbstate; 160 161 memset (&mbstate, 0, sizeof (mbstate)); 162 for (m = wid, n = 0; 163 len && (k = mbrtowc (&wc, s, len, &mbstate)) && (n <= wid); 164 s += k, len -= k) 165 { 166 if (*s == ' ') 167 m = n; 168 if (k == (size_t)(-1) || k == (size_t)(-2)) 169 { 170 if (k == (size_t)(-1)) 171 memset (&mbstate, 0, sizeof (mbstate)); 172 k = (k == (size_t)(-1)) ? 1 : len; 173 wc = replacement_char (); 174 } 175 if (!IsWPrint (wc)) 176 wc = '?'; 177 n += wcwidth (wc); 178 } 179 if (n > wid) 180 n = m; 181 else 182 n = wid; 183 return n; 184} 185 186static int pad (FILE *f, int col, int i) 187{ 188 char fmt[15]; /* min size to accommodate %%-%ds */ 189 190 if (col < i) 191 { 192 snprintf (fmt, sizeof(fmt), "%%-%ds", i - col); 193 fprintf (f, fmt, ""); 194 return (i); 195 } 196 fputc (' ', f); 197 return (col + 1); 198} 199 200static void format_line (FILE *f, int ismacro, 201 const char *t1, const char *t2, const char *t3) 202{ 203 int col; 204 int col_a, col_b; 205 int split; 206 int n; 207 208 fputs (t1, f); 209 210 /* don't try to press string into one line with less than 40 characters. 211 The double parenthesis avoids a gcc warning, sigh ... */ 212 if ((split = MuttIndexWindow->cols < 40)) 213 { 214 col_a = col = 0; 215 col_b = LONG_STRING; 216 fputc ('\n', f); 217 } 218 else 219 { 220 col_a = MuttIndexWindow->cols > 83 ? (MuttIndexWindow->cols - 32) >> 2 : 12; 221 col_b = MuttIndexWindow->cols > 49 ? (MuttIndexWindow->cols - 10) >> 1 : 19; 222 col = pad (f, mutt_strwidth(t1), col_a); 223 } 224 225 if (ismacro > 0) 226 { 227 if (!Pager || !mutt_strcmp (Pager, "builtin")) 228 fputs ("_\010", f); 229 fputs ("M ", f); 230 col += 2; 231 232 if (!split) 233 { 234 col += print_macro (f, col_b - col - 4, &t2); 235 if (mutt_strwidth (t2) > col_b - col) 236 t2 = "..."; 237 } 238 } 239 240 col += print_macro (f, col_b - col - 1, &t2); 241 if (split) 242 fputc ('\n', f); 243 else 244 col = pad (f, col, col_b); 245 246 if (split) 247 { 248 print_macro (f, LONG_STRING, &t3); 249 fputc ('\n', f); 250 } 251 else 252 { 253 while (*t3) 254 { 255 n = MuttIndexWindow->cols - col; 256 257 if (ismacro >= 0) 258 { 259 SKIPWS(t3); 260 n = get_wrapped_width (t3, n); 261 } 262 263 n = print_macro (f, n, &t3); 264 265 if (*t3) 266 { 267 if (Pager && mutt_strcmp (Pager, "builtin")) 268 { 269 fputc ('\n', f); 270 n = 0; 271 } 272 else 273 { 274 n += col - MuttIndexWindow->cols; 275 if (option (OPTMARKERS)) 276 ++n; 277 } 278 col = pad (f, n, col_b); 279 } 280 } 281 } 282 283 fputc ('\n', f); 284} 285 286static void dump_menu (FILE *f, int menu) 287{ 288 struct keymap_t *map; 289 const struct binding_t *b; 290 char buf[SHORT_STRING]; 291 292 /* browse through the keymap table */ 293 for (map = Keymaps[menu]; map; map = map->next) 294 { 295 if (map->op != OP_NULL) 296 { 297 km_expand_key (buf, sizeof (buf), map); 298 299 if (map->op == OP_MACRO) 300 { 301 if (map->descr == NULL) 302 format_line (f, -1, buf, "macro", map->macro); 303 else 304 format_line (f, 1, buf, map->macro, map->descr); 305 } 306 else 307 { 308 b = help_lookupFunction (map->op, menu); 309 format_line (f, 0, buf, b ? b->name : "UNKNOWN", 310 b ? _(HelpStrings[b->op]) : _("ERROR: please report this bug")); 311 } 312 } 313 } 314} 315 316static int is_bound (struct keymap_t *map, int op) 317{ 318 for (; map; map = map->next) 319 if (map->op == op) 320 return 1; 321 return 0; 322} 323 324static void dump_unbound (FILE *f, 325 const struct binding_t *funcs, 326 struct keymap_t *map, 327 struct keymap_t *aux) 328{ 329 int i; 330 331 for (i = 0; funcs[i].name; i++) 332 { 333 if (! is_bound (map, funcs[i].op) && 334 (!aux || ! is_bound (aux, funcs[i].op))) 335 format_line (f, 0, funcs[i].name, "", _(HelpStrings[funcs[i].op])); 336 } 337} 338 339void mutt_help (int menu) 340{ 341 BUFFER *t = NULL; 342 char buf[SHORT_STRING]; 343 const char *desc; 344 FILE *f; 345 const struct binding_t *funcs; 346 347 /* We don't use the buffer pool because of the extended lifetime of t */ 348 t = mutt_buffer_new (); 349 mutt_buffer_mktemp (t); 350 351 funcs = km_get_table (menu); 352 desc = mutt_getnamebyvalue (menu, Menus); 353 if (!desc) 354 desc = _("<UNKNOWN>"); 355 356 do 357 { 358 if ((f = safe_fopen (mutt_b2s (t), "w")) == NULL) 359 { 360 mutt_perror (mutt_b2s (t)); 361 goto cleanup; 362 } 363 364 dump_menu (f, menu); 365 if (menu != MENU_EDITOR && menu != MENU_PAGER) 366 { 367 fputs (_("\nGeneric bindings:\n\n"), f); 368 dump_menu (f, MENU_GENERIC); 369 } 370 371 fputs (_("\nUnbound functions:\n\n"), f); 372 if (funcs) 373 dump_unbound (f, funcs, Keymaps[menu], NULL); 374 if (menu != MENU_PAGER) 375 dump_unbound (f, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu]); 376 377 safe_fclose (&f); 378 379 snprintf (buf, sizeof (buf), _("Help for %s"), desc); 380 } 381 while 382 (mutt_do_pager (buf, mutt_b2s (t), 383 MUTT_PAGER_RETWINCH | MUTT_PAGER_MARKER | MUTT_PAGER_NSKIP | MUTT_PAGER_NOWRAP, 384 NULL) 385 == OP_REFORMAT_WINCH); 386 387cleanup: 388 mutt_buffer_free (&t); 389}