01751f14a8e5d987eb363a84ee5c672150d8af46
[apps/madmutt.git] / curs_lib.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 2004 g10 Code GmbH
5  *
6  * Parts were written/modified by:
7  * Nico Golde <nico@ngolde.de>
8  *
9  * This file is part of mutt-ng, see http://www.muttng.org/.
10  * It's licensed under the GNU General Public License,
11  * please see the file GPL in the top level source directory.
12  */
13
14 #if HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include "mutt.h"
19 #include "mutt_menu.h"
20 #include "mutt_curses.h"
21 #include "pager.h"
22 #include "mbyte.h"
23
24 #include <termios.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32
33 #ifdef HAVE_LANGINFO_YESEXPR
34 #include <langinfo.h>
35 #endif
36
37 /* not possible to unget more than one char under some curses libs, and it
38  * is impossible to unget function keys in SLang, so roll our own input
39  * buffering routines.
40  */
41 size_t UngetCount = 0;
42 static size_t UngetBufLen = 0;
43 static event_t *KeyEvent;
44
45 void mutt_refresh (void)
46 {
47   /* don't refresh when we are waiting for a child. */
48   if (option (OPTKEEPQUIET))
49     return;
50
51   /* don't refresh in the middle of macros unless necessary */
52   if (UngetCount && !option (OPTFORCEREFRESH))
53     return;
54
55   /* else */
56   refresh ();
57 }
58
59 /* Make sure that the next refresh does a full refresh.  This could be
60    optmized by not doing it at all if DISPLAY is set as this might
61    indicate that a GUI based pinentry was used.  Having an option to
62    customize this is of course the Mutt way.  */
63 void mutt_need_hard_redraw (void)
64 {
65   if (!getenv ("DISPLAY")) {
66     keypad (stdscr, TRUE);
67     clearok (stdscr, TRUE);
68     set_option (OPTNEEDREDRAW);
69   }
70 }
71
72 event_t mutt_getch (void)
73 {
74   int ch;
75   event_t err = { -1, OP_NULL }, ret;
76
77   if (!option (OPTUNBUFFEREDINPUT) && UngetCount)
78     return (KeyEvent[--UngetCount]);
79
80   SigInt = 0;
81
82   mutt_allow_interrupt (1);
83 #ifdef KEY_RESIZE
84   /* ncurses 4.2 sends this when the screen is resized */
85   ch = KEY_RESIZE;
86   while (ch == KEY_RESIZE)
87 #endif /* KEY_RESIZE */
88     ch = getch ();
89   mutt_allow_interrupt (0);
90
91   if (SigInt)
92     mutt_query_exit ();
93
94   if (ch == ERR)
95     return err;
96
97   if ((ch & 0x80) && option (OPTMETAKEY)) {
98     /* send ALT-x as ESC-x */
99     ch &= ~0x80;
100     mutt_ungetch (ch, 0);
101     ret.ch = '\033';
102     ret.op = 0;
103     return ret;
104   }
105
106   ret.ch = ch;
107   ret.op = 0;
108   return (ch == ctrl ('G') ? err : ret);
109 }
110
111 int _mutt_get_field ( /* const */ char *field, char *buf, size_t buflen,
112                      int complete, int multiple, char ***files, int *numfiles)
113 {
114   int ret;
115   int x, y;
116
117   ENTER_STATE *es = mutt_new_enter_state ();
118
119   do {
120     CLEARLINE (LINES - 1);
121     addstr (field);
122     mutt_refresh ();
123     getyx (stdscr, y, x);
124     ret =
125       _mutt_enter_string (buf, buflen, y, x, complete, multiple, files,
126                           numfiles, es);
127   }
128   while (ret == 1);
129   CLEARLINE (LINES - 1);
130   mutt_free_enter_state (&es);
131
132   return (ret);
133 }
134
135 int mutt_get_password (char *msg, char *buf, size_t buflen)
136 {
137   int rc;
138
139   CLEARLINE (LINES - 1);
140   addstr (msg);
141   set_option (OPTUNBUFFEREDINPUT);
142   rc = mutt_enter_string (buf, buflen, LINES - 1, mutt_strlen (msg), M_PASS);
143   unset_option (OPTUNBUFFEREDINPUT);
144   CLEARLINE (LINES - 1);
145   return (rc);
146 }
147
148 void mutt_clear_error (void)
149 {
150   Errorbuf[0] = 0;
151   if (!option (OPTNOCURSES))
152     CLEARLINE (LINES - 1);
153 }
154
155 static void fix_end_of_file (const char *data)
156 {
157   FILE *fp;
158   int c;
159
160   if ((fp = safe_fopen (data, "a+")) == NULL)
161     return;
162   fseek (fp, -1, SEEK_END);
163   if ((c = fgetc (fp)) != '\n')
164     fputc ('\n', fp);
165   safe_fclose (&fp);
166 }
167
168 void mutt_edit_file (const char *editor, const char *data)
169 {
170   char cmd[LONG_STRING];
171
172   mutt_endwin (NULL);
173   mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data);
174   if (mutt_system (cmd) == -1)
175     mutt_error (_("Error running \"%s\"!"), cmd);
176   fix_end_of_file (data);
177   keypad (stdscr, TRUE);
178   clearok (stdscr, TRUE);
179 }
180
181 int mutt_yesorno (const char *msg, int def)
182 {
183   event_t ch;
184   char *yes = _("yes");
185   char *no = _("no");
186   char *answer_string;
187   size_t answer_string_len;
188
189 #ifdef HAVE_LANGINFO_YESEXPR
190   char *expr;
191   regex_t reyes;
192   regex_t reno;
193   int reyes_ok;
194   int reno_ok;
195   char answer[2];
196
197   answer[1] = 0;
198
199   reyes_ok = (expr = nl_langinfo (YESEXPR)) && expr[0] == '^' &&
200     !regcomp (&reyes, expr, REG_NOSUB | REG_EXTENDED);
201   reno_ok = (expr = nl_langinfo (NOEXPR)) && expr[0] == '^' &&
202     !regcomp (&reno, expr, REG_NOSUB | REG_EXTENDED);
203 #endif
204
205   CLEARLINE (LINES - 1);
206
207   /*
208    * In order to prevent the default answer to the question to wrapped
209    * around the screen in the even the question is wider than the screen,
210    * ensure there is enough room for the answer and truncate the question
211    * to fit.
212    */
213   answer_string = safe_malloc (COLS + 1);
214   snprintf (answer_string, COLS + 1, " ([%s]/%s): ", def == M_YES ? yes : no,
215             def == M_YES ? no : yes);
216   answer_string_len = strlen (answer_string);
217   printw ("%.*s%s", COLS - answer_string_len, msg, answer_string);
218   FREE (&answer_string);
219
220   FOREVER {
221     mutt_refresh ();
222     ch = mutt_getch ();
223     if (CI_is_return (ch.ch))
224       break;
225     if (ch.ch == -1) {
226       def = -1;
227       break;
228     }
229
230 #ifdef HAVE_LANGINFO_YESEXPR
231     answer[0] = ch.ch;
232     if (reyes_ok ? (regexec (&reyes, answer, 0, 0, 0) == 0) :
233 #else
234     if (
235 #endif
236          (tolower (ch.ch) == 'y')) {
237       def = M_YES;
238       break;
239     }
240     else if (
241 #ifdef HAVE_LANGINFO_YESEXPR
242               reno_ok ? (regexec (&reno, answer, 0, 0, 0) == 0) :
243 #endif
244               (tolower (ch.ch) == 'n')) {
245       def = M_NO;
246       break;
247     }
248     else {
249       BEEP ();
250     }
251   }
252
253 #ifdef HAVE_LANGINFO_YESEXPR
254   if (reyes_ok)
255     regfree (&reyes);
256   if (reno_ok)
257     regfree (&reno);
258 #endif
259
260   if (def != -1) {
261     addstr ((char *) (def == M_YES ? yes : no));
262     mutt_refresh ();
263   }
264   return (def);
265 }
266
267 /* this function is called when the user presses the abort key */
268 void mutt_query_exit (void)
269 {
270   mutt_flushinp ();
271   curs_set (1);
272   if (Timeout)
273     timeout (-1);               /* restore blocking operation */
274   if (mutt_yesorno (_("Exit Mutt-ng?"), M_YES) == M_YES) {
275     endwin ();
276     exit (1);
277   }
278   mutt_clear_error ();
279   mutt_curs_set (-1);
280   SigInt = 0;
281 }
282
283 void mutt_curses_error (const char *fmt, ...)
284 {
285   char TmpErrorbuf[STRING];
286   va_list ap;
287
288   va_start (ap, fmt);
289   vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
290   va_end (ap);
291
292   dprint (1, (debugfile, "%s\n", Errorbuf));
293   mutt_format_string (TmpErrorbuf, sizeof (TmpErrorbuf),
294                       0, COLS - 2, 0, 0, Errorbuf, sizeof (Errorbuf), 0);
295   snprintf (Errorbuf, sizeof (Errorbuf), "%s", TmpErrorbuf);    /* overkill */
296
297   if (!option (OPTKEEPQUIET)) {
298     BEEP ();
299     SETCOLOR (MT_COLOR_ERROR);
300     mvaddstr (LINES - 1, 0, Errorbuf);
301     clrtoeol ();
302     SETCOLOR (MT_COLOR_NORMAL);
303     mutt_refresh ();
304   }
305
306   set_option (OPTMSGERR);
307 }
308
309 void mutt_curses_message (const char *fmt, ...)
310 {
311   char TmpErrorbuf[STRING];
312   va_list ap;
313
314   va_start (ap, fmt);
315   vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
316   va_end (ap);
317
318   mutt_format_string (TmpErrorbuf, sizeof (TmpErrorbuf),
319                       0, COLS - 2, 0, 0, Errorbuf, sizeof (Errorbuf), 0);
320   snprintf (Errorbuf, sizeof (Errorbuf), "%s", TmpErrorbuf);    /* overkill */
321
322   if (!option (OPTKEEPQUIET)) {
323     SETCOLOR (MT_COLOR_MESSAGE);
324     mvaddstr (LINES - 1, 0, Errorbuf);
325     clrtoeol ();
326     SETCOLOR (MT_COLOR_NORMAL);
327     mutt_refresh ();
328   }
329
330   unset_option (OPTMSGERR);
331 }
332
333 void mutt_show_error (void)
334 {
335   if (option (OPTKEEPQUIET))
336     return;
337
338   SETCOLOR (option (OPTMSGERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
339   CLEARLINE (LINES - 1);
340   addstr (Errorbuf);
341   SETCOLOR (MT_COLOR_NORMAL);
342 }
343
344 void mutt_endwin (const char *msg)
345 {
346   if (!option (OPTNOCURSES)) {
347     CLEARLINE (LINES - 1);
348
349     attrset (A_NORMAL);
350     mutt_refresh ();
351     endwin ();
352   }
353
354   if (msg && *msg) {
355     puts (msg);
356     fflush (stdout);
357   }
358 }
359
360 void mutt_perror (const char *s)
361 {
362   char *p = strerror (errno);
363
364   dprint (1, (debugfile, "%s: %s (errno = %d)\n", s,
365               p ? p : "unknown error", errno));
366   mutt_error ("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
367 }
368
369 int mutt_any_key_to_continue (const char *s)
370 {
371   struct termios t;
372   struct termios old;
373   int f, ch;
374
375   f = open ("/dev/tty", O_RDONLY);
376   tcgetattr (f, &t);
377   memcpy ((void *) &old, (void *) &t, sizeof (struct termios)); /* save original state */
378   t.c_lflag &= ~(ICANON | ECHO);
379   t.c_cc[VMIN] = 1;
380   t.c_cc[VTIME] = 0;
381   tcsetattr (f, TCSADRAIN, &t);
382   fflush (stdout);
383   if (s)
384     fputs (s, stdout);
385   else
386     fputs (_("Press any key to continue..."), stdout);
387   fflush (stdout);
388   ch = fgetc (stdin);
389   fflush (stdin);
390   tcsetattr (f, TCSADRAIN, &old);
391   close (f);
392   fputs ("\r\n", stdout);
393   mutt_clear_error ();
394   return (ch);
395 }
396
397 int mutt_do_pager (const char *banner,
398                    const char *tempfile, int do_color, pager_t * info)
399 {
400   int rc;
401
402   if (!Pager || mutt_strcmp (Pager, "builtin") == 0)
403     rc = mutt_pager (banner, tempfile, do_color, info);
404   else {
405     char cmd[STRING];
406
407     mutt_endwin (NULL);
408     mutt_expand_file_fmt (cmd, sizeof (cmd), Pager, tempfile);
409     if (mutt_system (cmd) == -1) {
410       mutt_error (_("Error running \"%s\"!"), cmd);
411       rc = -1;
412     }
413     else
414       rc = 0;
415     mutt_unlink (tempfile);
416   }
417
418   return rc;
419 }
420
421 int _mutt_enter_fname (const char *prompt, char *buf, size_t blen,
422                        int *redraw, int buffy, int multiple, char ***files,
423                        int *numfiles)
424 {
425   event_t ch;
426
427   mvaddstr (LINES - 1, 0, (char *) prompt);
428   addstr (_(" ('?' for list): "));
429   if (buf[0])
430     addstr (buf);
431   clrtoeol ();
432   mutt_refresh ();
433
434   ch = mutt_getch ();
435   if (ch.ch == -1) {
436     CLEARLINE (LINES - 1);
437     return (-1);
438   }
439   else if (ch.ch == '?') {
440     mutt_refresh ();
441     buf[0] = 0;
442     _mutt_select_file (buf, blen, M_SEL_FOLDER | (multiple ? M_SEL_MULTI : 0),
443                        files, numfiles);
444     *redraw = REDRAW_FULL;
445   }
446   else {
447     char *pc = safe_malloc (mutt_strlen (prompt) + 3);
448
449     sprintf (pc, "%s: ", prompt);       /* __SPRINTF_CHECKED__ */
450     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
451     if (_mutt_get_field
452         (pc, buf, blen, (buffy ? M_EFILE : M_FILE) | M_CLEAR, multiple, files,
453          numfiles)
454         != 0)
455       buf[0] = 0;
456     MAYBE_REDRAW (*redraw);
457     FREE (&pc);
458   }
459
460   return 0;
461 }
462
463 void mutt_ungetch (int ch, int op)
464 {
465   event_t tmp;
466
467   tmp.ch = ch;
468   tmp.op = op;
469
470   if (UngetCount >= UngetBufLen)
471     safe_realloc (&KeyEvent, (UngetBufLen += 128) * sizeof (event_t));
472
473   KeyEvent[UngetCount++] = tmp;
474 }
475
476 void mutt_flushinp (void)
477 {
478   UngetCount = 0;
479   flushinp ();
480 }
481
482 #if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
483 /* The argument can take 3 values:
484  * -1: restore the value of the last call
485  *  0: make the cursor invisible
486  *  1: make the cursor visible
487  */
488 void mutt_curs_set (int cursor)
489 {
490   static int SavedCursor = 1;
491
492   if (cursor < 0)
493     cursor = SavedCursor;
494   else
495     SavedCursor = cursor;
496
497   if (curs_set (cursor) == ERR) {
498     if (cursor == 1)            /* cnorm */
499       curs_set (2);             /* cvvis */
500   }
501 }
502 #endif
503
504 int mutt_multi_choice (char *prompt, char *letters)
505 {
506   event_t ch;
507   int choice;
508   char *p;
509
510   mvaddstr (LINES - 1, 0, prompt);
511   clrtoeol ();
512   FOREVER {
513     mutt_refresh ();
514     ch = mutt_getch ();
515     if (ch.ch == -1 || CI_is_return (ch.ch)) {
516       choice = -1;
517       break;
518     }
519     else {
520       p = strchr (letters, ch.ch);
521       if (p) {
522         choice = p - letters + 1;
523         break;
524       }
525       else if (ch.ch <= '9' && ch.ch > '0') {
526         choice = ch.ch - '0';
527         if (choice <= mutt_strlen (letters))
528           break;
529       }
530     }
531     BEEP ();
532   }
533   CLEARLINE (LINES - 1);
534   mutt_refresh ();
535   return choice;
536 }
537
538 /*
539  * addwch would be provided by an up-to-date curses library
540  */
541
542 int mutt_addwch (wchar_t wc)
543 {
544   char buf[MB_LEN_MAX * 2];
545   mbstate_t mbstate;
546   size_t n1, n2;
547
548   memset (&mbstate, 0, sizeof (mbstate));
549   if ((n1 = wcrtomb (buf, wc, &mbstate)) == (size_t) (-1) ||
550       (n2 = wcrtomb (buf + n1, 0, &mbstate)) == (size_t) (-1))
551     return -1;                  /* ERR */
552   else
553     return addstr (buf);
554 }
555
556
557 /*
558  * This formats a string, a bit like
559  * snprintf (dest, destlen, "%-*.*s", min_width, max_width, s),
560  * except that the widths refer to the number of character cells
561  * when printed.
562  */
563
564 void mutt_format_string (char *dest, size_t destlen,
565                          int min_width, int max_width,
566                          int right_justify, char m_pad_char,
567                          const char *s, size_t n, int arboreal)
568 {
569   char *p;
570   wchar_t wc;
571   int w;
572   size_t k, k2;
573   char scratch[MB_LEN_MAX];
574   mbstate_t mbstate1, mbstate2;
575
576   memset (&mbstate1, 0, sizeof (mbstate1));
577   memset (&mbstate2, 0, sizeof (mbstate2));
578   --destlen;
579   p = dest;
580   for (; n && (k = mbrtowc (&wc, s, n, &mbstate1)); s += k, n -= k) {
581     if (k == (size_t) (-1) || k == (size_t) (-2)) {
582       k = (k == (size_t) (-1)) ? 1 : n;
583       wc = replacement_char ();
584     }
585     if (arboreal && wc < M_TREE_MAX)
586       w = 1;                    /* hack */
587     else {
588       if (!IsWPrint (wc))
589         wc = '?';
590       w = wcwidth (wc);
591     }
592     if (w >= 0) {
593       if (w > max_width || (k2 = wcrtomb (scratch, wc, &mbstate2)) > destlen)
594         break;
595       min_width -= w;
596       max_width -= w;
597       strncpy (p, scratch, k2);
598       p += k2;
599       destlen -= k2;
600     }
601   }
602   w = (int) destlen < min_width ? destlen : min_width;
603   if (w <= 0)
604     *p = '\0';
605   else if (right_justify) {
606     p[w] = '\0';
607     while (--p >= dest)
608       p[w] = *p;
609     while (--w >= 0)
610       dest[w] = m_pad_char;
611   }
612   else {
613     while (--w >= 0)
614       *p++ = m_pad_char;
615     *p = '\0';
616   }
617 }
618
619 /*
620  * This formats a string rather like
621  *   snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
622  *   snprintf (dest, destlen, fmt, s);
623  * except that the numbers in the conversion specification refer to
624  * the number of character cells when printed.
625  */
626
627 static void mutt_format_s_x (char *dest,
628                              size_t destlen,
629                              const char *prefix, const char *s, int arboreal)
630 {
631   int right_justify = 1;
632   char *p;
633   int min_width;
634   int max_width = INT_MAX;
635
636   if (*prefix == '-')
637     ++prefix, right_justify = 0;
638   min_width = strtol (prefix, &p, 10);
639   if (*p == '.') {
640     prefix = p + 1;
641     max_width = strtol (prefix, &p, 10);
642     if (p <= prefix)
643       max_width = INT_MAX;
644   }
645
646   mutt_format_string (dest, destlen, min_width, max_width,
647                       right_justify, ' ', s, mutt_strlen (s), arboreal);
648 }
649
650 void mutt_format_s (char *dest,
651                     size_t destlen, const char *prefix, const char *s)
652 {
653   mutt_format_s_x (dest, destlen, prefix, s, 0);
654 }
655
656 void mutt_format_s_tree (char *dest,
657                          size_t destlen, const char *prefix, const char *s)
658 {
659   mutt_format_s_x (dest, destlen, prefix, s, 1);
660 }
661
662 /*
663  * mutt_paddstr (n, s) is almost equivalent to
664  * mutt_format_string (bigbuf, big, n, n, 0, ' ', s, big, 0), addstr (bigbuf)
665  */
666
667 void mutt_paddstr (int n, const char *s)
668 {
669   wchar_t wc;
670   int w;
671   size_t k;
672   size_t len = mutt_strlen (s);
673   mbstate_t mbstate;
674
675   memset (&mbstate, 0, sizeof (mbstate));
676   for (; len && (k = mbrtowc (&wc, s, len, &mbstate)); s += k, len -= k) {
677     if (k == (size_t) (-1) || k == (size_t) (-2)) {
678       k = (k == (size_t) (-1)) ? 1 : len;
679       wc = replacement_char ();
680     }
681     if (!IsWPrint (wc))
682       wc = '?';
683     w = wcwidth (wc);
684     if (w >= 0) {
685       if (w > n)
686         break;
687       addnstr ((char *) s, k);
688       n -= w;
689     }
690   }
691   while (n-- > 0)
692     addch (' ');
693 }
694
695 /*
696  * mutt_strwidth is like mutt_strlen except that it returns the width
697  * refering to the number of characters cells.
698  */
699
700 int mutt_strwidth (const char *s)
701 {
702   wchar_t wc;
703   int w;
704   size_t k, n;
705   mbstate_t mbstate;
706
707   if (!s)
708     return 0;
709
710   n = mutt_strlen (s);
711
712   memset (&mbstate, 0, sizeof (mbstate));
713   for (w = 0; n && (k = mbrtowc (&wc, s, n, &mbstate)); s += k, n -= k) {
714     if (k == (size_t) (-1) || k == (size_t) (-2)) {
715       k = (k == (size_t) (-1)) ? 1 : n;
716       wc = replacement_char ();
717     }
718     if (!IsWPrint (wc))
719       wc = '?';
720     w += wcwidth (wc);
721   }
722   return w;
723 }