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