missing includes, intializers, consts
[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 "enter.h"
20 #include "mutt_menu.h"
21 #include "mutt_curses.h"
22 #include "pager.h"
23 #include "mbyte.h"
24
25 #include "lib/mem.h"
26 #include "lib/intl.h"
27 #include "lib/str.h"
28 #include "lib/debug.h"
29
30 #include <wchar.h>
31 #include <termios.h>
32 #include <sys/types.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <ctype.h>
39
40 #ifdef HAVE_LANGINFO_YESEXPR
41 #include <langinfo.h>
42 #endif
43
44 /* not possible to unget more than one char under some curses libs, and it
45  * is impossible to unget function keys in SLang, so roll our own input
46  * buffering routines.
47  */
48 size_t UngetCount = 0;
49 static size_t UngetBufLen = 0;
50 static event_t *KeyEvent;
51
52 void mutt_refresh (void)
53 {
54   /* don't refresh when we are waiting for a child. */
55   if (option (OPTKEEPQUIET))
56     return;
57
58   /* don't refresh in the middle of macros unless necessary */
59   if (UngetCount && !option (OPTFORCEREFRESH))
60     return;
61
62   /* else */
63   refresh ();
64 }
65
66 /* Make sure that the next refresh does a full refresh.  This could be
67    optmized by not doing it at all if DISPLAY is set as this might
68    indicate that a GUI based pinentry was used.  Having an option to
69    customize this is of course the Mutt way.  */
70 void mutt_need_hard_redraw (void)
71 {
72   if (!getenv ("DISPLAY")) {
73     keypad (stdscr, TRUE);
74     clearok (stdscr, TRUE);
75     set_option (OPTNEEDREDRAW);
76   }
77 }
78
79 event_t mutt_getch (void)
80 {
81   int ch;
82   event_t err = { -1, OP_NULL }, ret;
83
84   if (!option (OPTUNBUFFEREDINPUT) && UngetCount)
85     return (KeyEvent[--UngetCount]);
86
87   SigInt = 0;
88
89   mutt_allow_interrupt (1);
90 #ifdef KEY_RESIZE
91   /* ncurses 4.2 sends this when the screen is resized */
92   ch = KEY_RESIZE;
93   while (ch == KEY_RESIZE)
94 #endif /* KEY_RESIZE */
95     ch = getch ();
96   mutt_allow_interrupt (0);
97
98   if (SigInt)
99     mutt_query_exit ();
100
101   if (ch == ERR)
102     return err;
103
104   if ((ch & 0x80) && option (OPTMETAKEY)) {
105     /* send ALT-x as ESC-x */
106     ch &= ~0x80;
107     mutt_ungetch (ch, 0);
108     ret.ch = '\033';
109     ret.op = 0;
110     return ret;
111   }
112
113   ret.ch = ch;
114   ret.op = 0;
115   return (ch == ctrl ('G') ? err : ret);
116 }
117
118 int _mutt_get_field ( const char *field, char *buf, size_t buflen,
119                      int complete, int multiple, char ***files, int *numfiles)
120 {
121   int ret;
122   int x, y;
123
124   ENTER_STATE *es = mutt_new_enter_state ();
125
126   do {
127     CLEARLINE (LINES - 1);
128     addstr (field);
129     mutt_refresh ();
130     getyx (stdscr, y, x);
131     ret =
132       _mutt_enter_string (buf, buflen, y, x, complete, multiple, files,
133                           numfiles, es);
134   }
135   while (ret == 1);
136   CLEARLINE (LINES - 1);
137   mutt_free_enter_state (&es);
138
139   return (ret);
140 }
141
142 int mutt_get_field_unbuffered (char *msg, char *buf, size_t buflen, int flags)
143 {
144   int rc;
145
146   set_option (OPTUNBUFFEREDINPUT);
147   rc = mutt_get_field (msg, buf, buflen, flags);
148   unset_option (OPTUNBUFFEREDINPUT);
149
150   return (rc);
151 }
152
153 void mutt_clear_error (void)
154 {
155   Errorbuf[0] = 0;
156   if (!option (OPTNOCURSES))
157     CLEARLINE (LINES - 1);
158 }
159
160 void mutt_edit_file (const char *editor, const char *data)
161 {
162   char cmd[LONG_STRING];
163
164   mutt_endwin (NULL);
165   mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data);
166   if (mutt_system (cmd) == -1)
167     mutt_error (_("Error running \"%s\"!"), cmd);
168   keypad (stdscr, TRUE);
169   clearok (stdscr, TRUE);
170 }
171
172 int mutt_yesorno (const char *msg, int def)
173 {
174   event_t ch;
175   char *yes = _("yes");
176   char *no = _("no");
177   char *answer_string;
178   size_t answer_string_len;
179
180 #ifdef HAVE_LANGINFO_YESEXPR
181   char *expr;
182   regex_t reyes;
183   regex_t reno;
184   int reyes_ok;
185   int reno_ok;
186   char answer[2];
187
188   answer[1] = 0;
189
190   reyes_ok = (expr = nl_langinfo (YESEXPR)) && expr[0] == '^' &&
191     !regcomp (&reyes, expr, REG_NOSUB | REG_EXTENDED);
192   reno_ok = (expr = nl_langinfo (NOEXPR)) && expr[0] == '^' &&
193     !regcomp (&reno, expr, REG_NOSUB | REG_EXTENDED);
194 #endif
195
196   CLEARLINE (LINES - 1);
197
198   /*
199    * In order to prevent the default answer to the question to wrapped
200    * around the screen in the even the question is wider than the screen,
201    * ensure there is enough room for the answer and truncate the question
202    * to fit.
203    */
204   answer_string = mem_malloc (COLS + 1);
205   snprintf (answer_string, COLS + 1, " ([%s]/%s): ", def == M_YES ? yes : no,
206             def == M_YES ? no : yes);
207   answer_string_len = str_len (answer_string);
208   printw ("%.*s%s", COLS - answer_string_len, msg, answer_string);
209   mem_free (&answer_string);
210
211   FOREVER {
212     mutt_refresh ();
213     ch = mutt_getch ();
214     if (CI_is_return (ch.ch))
215       break;
216     if (ch.ch == -1) {
217       def = -1;
218       break;
219     }
220
221 #ifdef HAVE_LANGINFO_YESEXPR
222     answer[0] = ch.ch;
223     if (reyes_ok ? (regexec (&reyes, answer, 0, 0, 0) == 0) :
224 #else
225     if (
226 #endif
227          (tolower (ch.ch) == *yes)) {
228       def = M_YES;
229       break;
230     }
231     else if (
232 #ifdef HAVE_LANGINFO_YESEXPR
233               reno_ok ? (regexec (&reno, answer, 0, 0, 0) == 0) :
234 #endif
235               (tolower (ch.ch) == *no)) {
236       def = M_NO;
237       break;
238     }
239     else {
240       BEEP ();
241     }
242   }
243
244 #ifdef HAVE_LANGINFO_YESEXPR
245   if (reyes_ok)
246     regfree (&reyes);
247   if (reno_ok)
248     regfree (&reno);
249 #endif
250
251   if (def != -1) {
252     addstr ((char *) (def == M_YES ? yes : no));
253     mutt_refresh ();
254   }
255   return (def);
256 }
257
258 /* this function is called when the user presses the abort key */
259 void mutt_query_exit (void)
260 {
261   mutt_flushinp ();
262   curs_set (1);
263   if (Timeout)
264     timeout (-1);               /* restore blocking operation */
265   if (mutt_yesorno (_("Exit Mutt-ng?"), M_YES) == M_YES) {
266     mutt_endwin (NULL);
267     exit (1);
268   }
269   mutt_clear_error ();
270   mutt_curs_set (-1);
271   SigInt = 0;
272 }
273
274 void mutt_curses_error (const char *fmt, ...)
275 {
276   char TmpErrorbuf[STRING];
277   va_list ap;
278
279   va_start (ap, fmt);
280   vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
281   va_end (ap);
282
283   debug_print (1, ("%s\n", Errorbuf));
284   mutt_format_string (TmpErrorbuf, sizeof (TmpErrorbuf),
285                       0, COLS - 2, 0, 0, Errorbuf, sizeof (Errorbuf), 0);
286   snprintf (Errorbuf, sizeof (Errorbuf), "%s", TmpErrorbuf);    /* overkill */
287
288   if (!option (OPTKEEPQUIET)) {
289     BEEP ();
290     SETCOLOR (MT_COLOR_ERROR);
291     mvaddstr (LINES - 1, 0, Errorbuf);
292     clrtoeol ();
293     SETCOLOR (MT_COLOR_NORMAL);
294     mutt_refresh ();
295   }
296
297   set_option (OPTMSGERR);
298 }
299
300 #ifdef USE_SOCKET
301 void mutt_progress_bar (progress_t* progress, long pos) {
302   char posstr[SHORT_STRING];
303
304   if (!pos) {
305     if (!NetInc)
306       mutt_message (progress->msg);
307     else {
308       if (progress->size)
309         mutt_pretty_size (progress->sizestr, sizeof (progress->sizestr),
310                           progress->size);
311       progress->pos = 0;
312     }
313   }
314
315   if (!NetInc)
316     return;
317
318   if (pos >= progress->pos + (NetInc << 10)) {
319     progress->pos = pos;
320     pos = pos / (NetInc << 10) * (NetInc << 10);
321     mutt_pretty_size (posstr, sizeof (posstr), pos);
322     if (progress->size)
323       mutt_message ("%s %s/%s", progress->msg, posstr, progress->sizestr);
324     else
325       mutt_message ("%s %s", progress->msg, posstr);
326   }
327 }
328 #endif
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 || str_cmp (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 = mem_malloc (str_len (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     mem_free (&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     mem_realloc (&KeyEvent, (UngetBufLen += 128) * sizeof (event_t));
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   FOREVER {
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 <= str_len (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   memset (&mbstate, 0, sizeof (mbstate));
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, size_t destlen,
585                          int min_width, int max_width,
586                          int right_justify, char m_pad_char,
587                          const char *s, size_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   memset (&mbstate1, 0, sizeof (mbstate1));
597   memset (&mbstate2, 0, sizeof (mbstate2));
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 = (int) 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, str_len (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 = str_len (s);
693   mbstate_t mbstate;
694
695   memset (&mbstate, 0, sizeof (mbstate));
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 str_len except 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 = str_len (s);
731
732   memset (&mbstate, 0, sizeof (mbstate));
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 }