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