2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2004 g10 Code GmbH
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "mutt_menu.h"
26 #include "mutt_curses.h"
31 #include <sys/types.h>
38 #ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
43 #ifdef HAVE_LANGINFO_YESEXPR
47 /* not possible to unget more than one char under some curses libs, and it
48 * is impossible to unget function keys in SLang, so roll our own input
51 size_t UngetCount = 0;
52 static size_t UngetBufLen = 0;
53 static event_t *KeyEvent;
55 void mutt_refresh (void)
57 /* don't refresh when we are waiting for a child. */
58 if (option (OPTKEEPQUIET))
61 /* don't refresh in the middle of macros unless necessary */
62 if (UngetCount && !option (OPTFORCEREFRESH))
69 /* Make sure that the next refresh does a full refresh. This could be
70 optmized by not doing it at all if DISPLAY is set as this might
71 indicate that a GUI based pinentry was used. Having an option to
72 customize this is of course the Mutt way. */
73 void mutt_need_hard_redraw (void)
75 keypad (stdscr, TRUE);
76 clearok (stdscr, TRUE);
77 set_option (OPTNEEDREDRAW);
80 event_t mutt_getch (void)
83 event_t err = {-1, OP_NULL }, ret;
84 event_t timeout = {-2, OP_NULL};
86 if (!option(OPTUNBUFFEREDINPUT) && UngetCount)
87 return (KeyEvent[--UngetCount]);
91 mutt_allow_interrupt (1);
93 /* ncurses 4.2 sends this when the screen is resized */
95 while (ch == KEY_RESIZE)
96 #endif /* KEY_RESIZE */
98 mutt_allow_interrupt (0);
108 /* either timeout or the terminal has been lost */
117 if ((ch & 0x80) && option (OPTMETAKEY))
119 /* send ALT-x as ESC-x */
121 mutt_ungetch (ch, 0);
129 return (ch == ctrl ('G') ? err : ret);
132 int _mutt_get_field (/* const */ char *field, char *buf, size_t buflen, int complete, int multiple, char ***files, int *numfiles)
137 ENTER_STATE *es = mutt_new_enter_state();
144 getyx (stdscr, y, x);
145 ret = _mutt_enter_string (buf, buflen, y, x, complete, multiple, files, numfiles, es);
149 mutt_free_enter_state (&es);
154 int mutt_get_field_unbuffered (char *msg, char *buf, size_t buflen, int flags)
158 set_option (OPTUNBUFFEREDINPUT);
159 rc = mutt_get_field (msg, buf, buflen, flags);
160 unset_option (OPTUNBUFFEREDINPUT);
165 void mutt_clear_error (void)
168 if (!option(OPTNOCURSES))
172 void mutt_edit_file (const char *editor, const char *data)
174 char cmd[LONG_STRING];
177 mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data);
178 if (mutt_system (cmd))
180 mutt_error (_("Error running \"%s\"!"), cmd);
183 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
184 /* the terminal may have been resized while the editor owned it */
185 mutt_resize_screen ();
187 keypad (stdscr, TRUE);
188 clearok (stdscr, TRUE);
191 int mutt_yesorno (const char *msg, int def)
194 char *yes = _("yes");
197 size_t answer_string_len;
199 #ifdef HAVE_LANGINFO_YESEXPR
209 reyes_ok = (expr = nl_langinfo (YESEXPR)) && expr[0] == '^' &&
210 !REGCOMP (&reyes, expr, REG_NOSUB);
211 reno_ok = (expr = nl_langinfo (NOEXPR)) && expr[0] == '^' &&
212 !REGCOMP (&reno, expr, REG_NOSUB);
218 * In order to prevent the default answer to the question to wrapped
219 * around the screen in the even the question is wider than the screen,
220 * ensure there is enough room for the answer and truncate the question
223 answer_string = safe_malloc (COLS + 1);
224 snprintf (answer_string, COLS + 1, " ([%s]/%s): ", def == M_YES ? yes : no, def == M_YES ? no : yes);
225 answer_string_len = strlen (answer_string);
226 mutt_message ("%.*s%s", COLS - answer_string_len, msg, answer_string);
227 FREE (&answer_string);
233 if (CI_is_return (ch.ch))
241 #ifdef HAVE_LANGINFO_YESEXPR
244 (regexec (& reyes, answer, 0, 0, 0) == 0) :
248 (tolower (ch.ch) == 'y'))
254 #ifdef HAVE_LANGINFO_YESEXPR
256 (regexec (& reno, answer, 0, 0, 0) == 0) :
258 (tolower (ch.ch) == 'n'))
269 #ifdef HAVE_LANGINFO_YESEXPR
278 addstr ((char *) (def == M_YES ? yes : no));
283 /* when the users cancels with ^G, clear the message stored with
284 * mutt_message() so it isn't displayed when the screen is refreshed. */
290 /* this function is called when the user presses the abort key */
291 void mutt_query_exit (void)
296 timeout (-1); /* restore blocking operation */
297 if (mutt_yesorno (_("Exit Mutt?"), M_YES) == M_YES)
307 static void curses_message (int error, const char *fmt, va_list ap)
309 char scratch[LONG_STRING];
311 vsnprintf (scratch, sizeof (scratch), fmt, ap);
313 dprint (1, (debugfile, "%s\n", scratch));
314 mutt_format_string (Errorbuf, sizeof (Errorbuf),
315 0, COLS, FMT_LEFT, 0, scratch, sizeof (scratch), 0);
317 if (!option (OPTKEEPQUIET))
321 SETCOLOR (error ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
322 mvaddstr (LINES-1, 0, Errorbuf);
324 SETCOLOR (MT_COLOR_NORMAL);
329 set_option (OPTMSGERR);
331 unset_option (OPTMSGERR);
334 void mutt_curses_error (const char *fmt, ...)
339 curses_message (1, fmt, ap);
343 void mutt_curses_message (const char *fmt, ...)
348 curses_message (0, fmt, ap);
352 void mutt_progress_init (progress_t* progress, const char *msg,
353 unsigned short flags, unsigned short inc,
356 struct timeval tv = { 0, 0 };
360 if (option(OPTNOCURSES))
363 memset (progress, 0, sizeof (progress_t));
365 progress->flags = flags;
367 progress->size = size;
368 if (progress->size) {
369 if (progress->flags & M_PROGRESS_SIZE)
370 mutt_pretty_size (progress->sizestr, sizeof (progress->sizestr),
373 snprintf (progress->sizestr, sizeof (progress->sizestr), "%ld",
379 mutt_message ("%s (%s)", msg, progress->sizestr);
384 if (gettimeofday (&tv, NULL) < 0)
385 dprint (1, (debugfile, "gettimeofday failed: %d\n", errno));
386 /* if timestamp is 0 no time-based suppression is done */
388 progress->timestamp = ((unsigned int) tv.tv_sec * 1000)
389 + (unsigned int) (tv.tv_usec / 1000);
390 mutt_progress_update (progress, 0, 0);
393 void mutt_progress_update (progress_t* progress, long pos, int percent)
395 char posstr[SHORT_STRING];
397 struct timeval tv = { 0, 0 };
398 unsigned int now = 0;
400 if (option(OPTNOCURSES))
406 /* refresh if size > inc */
407 if (progress->flags & M_PROGRESS_SIZE &&
408 (pos >= progress->pos + (progress->inc << 10)))
410 else if (pos >= progress->pos + progress->inc)
413 /* skip refresh if not enough time has passed */
414 if (update && progress->timestamp && !gettimeofday (&tv, NULL)) {
415 now = ((unsigned int) tv.tv_sec * 1000)
416 + (unsigned int) (tv.tv_usec / 1000);
417 if (now && now - progress->timestamp < TimeInc)
421 /* always show the first update */
427 if (progress->flags & M_PROGRESS_SIZE)
429 pos = pos / (progress->inc << 10) * (progress->inc << 10);
430 mutt_pretty_size (posstr, sizeof (posstr), pos);
433 snprintf (posstr, sizeof (posstr), "%ld", pos);
435 dprint (5, (debugfile, "updating progress: %s\n", posstr));
439 progress->timestamp = now;
441 if (progress->size > 0)
443 mutt_message ("%s %s/%s (%d%%)", progress->msg, posstr, progress->sizestr,
444 percent > 0 ? percent :
445 (int) (100.0 * (double) progress->pos / progress->size));
450 mutt_message ("%s %s (%d%%)", progress->msg, posstr, percent);
452 mutt_message ("%s %s", progress->msg, posstr);
457 if (pos >= progress->size)
461 void mutt_show_error (void)
463 if (option (OPTKEEPQUIET))
466 SETCOLOR (option (OPTMSGERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
469 SETCOLOR (MT_COLOR_NORMAL);
472 void mutt_endwin (const char *msg)
476 if (!option (OPTNOCURSES))
478 CLEARLINE (LINES - 1);
494 void mutt_perror (const char *s)
496 char *p = strerror (errno);
498 dprint (1, (debugfile, "%s: %s (errno = %d)\n", s,
499 p ? p : "unknown error", errno));
500 mutt_error ("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
503 int mutt_any_key_to_continue (const char *s)
509 f = open ("/dev/tty", O_RDONLY);
511 memcpy ((void *)&old, (void *)&t, sizeof(struct termios)); /* save original state */
512 t.c_lflag &= ~(ICANON | ECHO);
515 tcsetattr (f, TCSADRAIN, &t);
520 fputs (_("Press any key to continue..."), stdout);
524 tcsetattr (f, TCSADRAIN, &old);
526 fputs ("\r\n", stdout);
531 int mutt_do_pager (const char *banner,
532 const char *tempfile,
538 if (!Pager || mutt_strcmp (Pager, "builtin") == 0)
539 rc = mutt_pager (banner, tempfile, do_color, info);
545 mutt_expand_file_fmt (cmd, sizeof(cmd), Pager, tempfile);
546 if (mutt_system (cmd) == -1)
548 mutt_error (_("Error running \"%s\"!"), cmd);
553 mutt_unlink (tempfile);
559 int _mutt_enter_fname (const char *prompt, char *buf, size_t blen, int *redraw, int buffy, int multiple, char ***files, int *numfiles)
563 mvaddstr (LINES-1, 0, (char *) prompt);
564 addstr (_(" ('?' for list): "));
576 else if (ch.ch == '?')
580 _mutt_select_file (buf, blen, M_SEL_FOLDER | (multiple ? M_SEL_MULTI : 0),
582 *redraw = REDRAW_FULL;
586 char *pc = safe_malloc (mutt_strlen (prompt) + 3);
588 sprintf (pc, "%s: ", prompt); /* __SPRINTF_CHECKED__ */
589 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
590 if (_mutt_get_field (pc, buf, blen, (buffy ? M_EFILE : M_FILE) | M_CLEAR, multiple, files, numfiles)
593 MAYBE_REDRAW (*redraw);
600 void mutt_ungetch (int ch, int op)
607 if (UngetCount >= UngetBufLen)
608 safe_realloc (&KeyEvent, (UngetBufLen += 128) * sizeof(event_t));
610 KeyEvent[UngetCount++] = tmp;
613 void mutt_flushinp (void)
619 #if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
620 /* The argument can take 3 values:
621 * -1: restore the value of the last call
622 * 0: make the cursor invisible
623 * 1: make the cursor visible
625 void mutt_curs_set (int cursor)
627 static int SavedCursor = 1;
630 cursor = SavedCursor;
632 SavedCursor = cursor;
634 if (curs_set (cursor) == ERR) {
635 if (cursor == 1) /* cnorm */
636 curs_set (2); /* cvvis */
641 int mutt_multi_choice (char *prompt, char *letters)
647 mvaddstr (LINES - 1, 0, prompt);
653 if (ch.ch < 0 || CI_is_return (ch.ch))
660 p = strchr (letters, ch.ch);
663 choice = p - letters + 1;
666 else if (ch.ch <= '9' && ch.ch > '0')
668 choice = ch.ch - '0';
669 if (choice <= mutt_strlen (letters))
675 CLEARLINE (LINES - 1);
681 * addwch would be provided by an up-to-date curses library
684 int mutt_addwch (wchar_t wc)
686 char buf[MB_LEN_MAX*2];
690 memset (&mbstate, 0, sizeof (mbstate));
691 if ((n1 = wcrtomb (buf, wc, &mbstate)) == (size_t)(-1) ||
692 (n2 = wcrtomb (buf + n1, 0, &mbstate)) == (size_t)(-1))
700 * This formats a string, a bit like
701 * snprintf (dest, destlen, "%-*.*s", min_width, max_width, s),
702 * except that the widths refer to the number of character cells
706 void mutt_format_string (char *dest, size_t destlen,
707 int min_width, int max_width,
708 int justify, char m_pad_char,
709 const char *s, size_t n,
716 char scratch[MB_LEN_MAX];
717 mbstate_t mbstate1, mbstate2;
719 memset(&mbstate1, 0, sizeof (mbstate1));
720 memset(&mbstate2, 0, sizeof (mbstate2));
723 for (; n && (k = mbrtowc (&wc, s, n, &mbstate1)); s += k, n -= k)
725 if (k == (size_t)(-1) || k == (size_t)(-2))
727 if (k == (size_t)(-1) && errno == EILSEQ)
728 memset (&mbstate1, 0, sizeof (mbstate1));
730 k = (k == (size_t)(-1)) ? 1 : n;
731 wc = replacement_char ();
733 if (arboreal && wc < M_TREE_MAX)
748 if (w > max_width || (k2 = wcrtomb (scratch, wc, &mbstate2)) > destlen)
752 strncpy (p, scratch, k2);
757 w = (int)destlen < min_width ? destlen : min_width;
760 else if (justify == FMT_RIGHT) /* right justify */
766 dest[w] = m_pad_char;
768 else if (justify == FMT_CENTER) /* center */
771 int half = (w+1) / 2; /* half of cushion space */
775 /* move str to center of buffer */
786 dest[half] = m_pad_char;
788 else /* left justify */
797 * This formats a string rather like
798 * snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
799 * snprintf (dest, destlen, fmt, s);
800 * except that the numbers in the conversion specification refer to
801 * the number of character cells when printed.
804 static void mutt_format_s_x (char *dest,
810 int justify = FMT_RIGHT;
813 int max_width = INT_MAX;
816 ++prefix, justify = FMT_LEFT;
817 else if (*prefix == '=')
818 ++prefix, justify = FMT_CENTER;
819 min_width = strtol (prefix, &p, 10);
823 max_width = strtol (prefix, &p, 10);
828 mutt_format_string (dest, destlen, min_width, max_width,
829 justify, ' ', s, mutt_strlen (s), arboreal);
832 void mutt_format_s (char *dest,
837 mutt_format_s_x (dest, destlen, prefix, s, 0);
840 void mutt_format_s_tree (char *dest,
845 mutt_format_s_x (dest, destlen, prefix, s, 1);
849 * mutt_paddstr (n, s) is almost equivalent to
850 * mutt_format_string (bigbuf, big, n, n, FMT_LEFT, ' ', s, big, 0), addstr (bigbuf)
853 void mutt_paddstr (int n, const char *s)
858 size_t len = mutt_strlen (s);
861 memset (&mbstate, 0, sizeof (mbstate));
862 for (; len && (k = mbrtowc (&wc, s, len, &mbstate)); s += k, len -= k)
864 if (k == (size_t)(-1) || k == (size_t)(-2))
866 if (k == (size_t) (-1))
867 memset (&mbstate, 0, sizeof (mbstate));
868 k = (k == (size_t)(-1)) ? 1 : len;
869 wc = replacement_char ();
878 addnstr ((char *)s, k);
886 /* See how many bytes to copy from string so its at most maxlen bytes
887 * long and maxwid columns wide */
888 int mutt_wstr_trunc (const char *src, size_t maxlen, size_t maxwid, size_t *width)
891 int w = 0, l = 0, cl;
898 n = mutt_strlen (src);
900 memset (&mbstate, 0, sizeof (mbstate));
901 for (w = 0; n && (cl = mbrtowc (&wc, src, n, &mbstate)); src += cl, n -= cl)
903 if (cl == (size_t)(-1) || cl == (size_t)(-2))
908 /* hack because M_TREE symbols aren't turned into characters
909 * until rendered by print_enriched_string (#3364) */
910 if (cw < 0 && cl == 1 && src[0] && src[0] < M_TREE_MAX)
913 if (cl + l > maxlen || cw + w > maxwid)
925 * returns the number of bytes the first (multibyte) character
927 * < 0 ... conversion error
928 * = 0 ... end of input
929 * > 0 ... length (bytes)
931 int mutt_charlen (const char *s, int *width)
941 memset (&mbstate, 0, sizeof (mbstate));
942 k = mbrtowc (&wc, s, n, &mbstate);
944 *width = wcwidth (wc);
945 return (k == (size_t)(-1) || k == (size_t)(-2)) ? -1 : k;
949 * mutt_strwidth is like mutt_strlen except that it returns the width
950 * refering to the number of characters cells.
953 int mutt_strwidth (const char *s)
964 memset (&mbstate, 0, sizeof (mbstate));
965 for (w=0; n && (k = mbrtowc (&wc, s, n, &mbstate)); s += k, n -= k)
967 if (k == (size_t)(-1) || k == (size_t)(-2))
969 k = (k == (size_t)(-1)) ? 1 : n;
970 wc = replacement_char ();