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