~ubuntu-branches/debian/squeeze/mutt/squeeze

1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
1
/*
2
 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3
 * Copyright (C) 2004 g10 Code GmbH
4
 * 
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.
9
 * 
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.
14
 * 
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.
18
 */ 
19
20
#if HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include "mutt.h"
25
#include "mutt_menu.h"
26
#include "mutt_curses.h"
27
#include "pager.h"
28
#include "mbyte.h"
29
30
#include <termios.h>
31
#include <sys/types.h>
32
#include <fcntl.h>
33
#include <stdlib.h>
34
#include <unistd.h>
35
#include <string.h>
36
#include <errno.h>
37
#include <ctype.h>
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
38
#ifdef HAVE_SYS_TIME_H
39
# include <sys/time.h>
40
#endif
41
#include <time.h>
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
42
43
#ifdef HAVE_LANGINFO_YESEXPR
44
#include <langinfo.h>
45
#endif
46
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
49
 * buffering routines.
50
 */
51
size_t UngetCount = 0;
52
static size_t UngetBufLen = 0;
53
static event_t *KeyEvent;
54
55
void mutt_refresh (void)
56
{
57
  /* don't refresh when we are waiting for a child. */
58
  if (option (OPTKEEPQUIET))
59
    return;
60
61
  /* don't refresh in the middle of macros unless necessary */
62
  if (UngetCount && !option (OPTFORCEREFRESH))
63
    return;
64
65
  /* else */
66
  refresh ();
67
}
68
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)
74
{
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
75
  keypad (stdscr, TRUE);
76
  clearok (stdscr, TRUE);
77
  set_option (OPTNEEDREDRAW);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
78
}
79
80
event_t mutt_getch (void)
81
{
82
  int ch;
83
  event_t err = {-1, OP_NULL }, ret;
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
84
  event_t timeout = {-2, OP_NULL};
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
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)
1.1.7 by Christoph Berg
Import upstream version 1.5.17
104
  {
105
    /* either timeout or the terminal has been lost */
106
    if (!isatty (0))
107
    {
108
      endwin ();
109
      exit (1);
110
    }
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
111
    return timeout;
1.1.7 by Christoph Berg
Import upstream version 1.5.17
112
  }
113
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
114
  if ((ch & 0x80) && option (OPTMETAKEY))
115
  {
116
    /* send ALT-x as ESC-x */
117
    ch &= ~0x80;
118
    mutt_ungetch (ch, 0);
119
    ret.ch = '\033';
120
    ret.op = 0;
121
    return ret;
122
  }
123
124
  ret.ch = ch;
125
  ret.op = 0;
126
  return (ch == ctrl ('G') ? err : ret);
127
}
128
129
int _mutt_get_field (/* const */ char *field, char *buf, size_t buflen, int complete, int multiple, char ***files, int *numfiles)
130
{
131
  int ret;
132
  int x, y;
133
134
  ENTER_STATE *es = mutt_new_enter_state();
135
  
136
  do
137
  {
138
    CLEARLINE (LINES-1);
139
    addstr (field);
140
    mutt_refresh ();
141
    getyx (stdscr, y, x);
142
    ret = _mutt_enter_string (buf, buflen, y, x, complete, multiple, files, numfiles, es);
143
  }
144
  while (ret == 1);
145
  CLEARLINE (LINES-1);
146
  mutt_free_enter_state (&es);
147
  
148
  return (ret);
149
}
150
151
int mutt_get_field_unbuffered (char *msg, char *buf, size_t buflen, int flags)
152
{
153
  int rc;
154
155
  set_option (OPTUNBUFFEREDINPUT);
156
  rc = mutt_get_field (msg, buf, buflen, flags);
157
  unset_option (OPTUNBUFFEREDINPUT);
158
159
  return (rc);
160
}
161
162
void mutt_clear_error (void)
163
{
164
  Errorbuf[0] = 0;
165
  if (!option(OPTNOCURSES))
166
    CLEARLINE (LINES-1);
167
}
168
169
void mutt_edit_file (const char *editor, const char *data)
170
{
171
  char cmd[LONG_STRING];
172
  
173
  mutt_endwin (NULL);
174
  mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data);
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
175
  if (mutt_system (cmd))
176
  {
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
177
    mutt_error (_("Error running \"%s\"!"), cmd);
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
178
    mutt_sleep (2);
179
  }
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
180
#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
181
  /* the terminal may have been resized while the editor owned it */
182
  mutt_resize_screen ();
183
#endif
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
184
  keypad (stdscr, TRUE);
185
  clearok (stdscr, TRUE);
186
}
187
188
int mutt_yesorno (const char *msg, int def)
189
{
190
  event_t ch;
191
  char *yes = _("yes");
192
  char *no = _("no");
193
  char *answer_string;
194
  size_t answer_string_len;
195
196
#ifdef HAVE_LANGINFO_YESEXPR
197
  char *expr;
198
  regex_t reyes;
199
  regex_t reno;
200
  int reyes_ok;
201
  int reno_ok;
202
  char answer[2];
203
204
  answer[1] = 0;
205
  
206
  reyes_ok = (expr = nl_langinfo (YESEXPR)) && expr[0] == '^' &&
207
	     !regcomp (&reyes, expr, REG_NOSUB|REG_EXTENDED);
208
  reno_ok = (expr = nl_langinfo (NOEXPR)) && expr[0] == '^' &&
209
            !regcomp (&reno, expr, REG_NOSUB|REG_EXTENDED);
210
#endif
211
212
  CLEARLINE(LINES-1);
213
214
  /*
215
   * In order to prevent the default answer to the question to wrapped
216
   * around the screen in the even the question is wider than the screen,
217
   * ensure there is enough room for the answer and truncate the question
218
   * to fit.
219
   */
220
  answer_string = safe_malloc (COLS + 1);
221
  snprintf (answer_string, COLS + 1, " ([%s]/%s): ", def == M_YES ? yes : no, def == M_YES ? no : yes);
222
  answer_string_len = strlen (answer_string);
223
  printw ("%.*s%s", COLS - answer_string_len, msg, answer_string);
224
  FREE (&answer_string);
225
226
  FOREVER
227
  {
228
    mutt_refresh ();
229
    ch = mutt_getch ();
230
    if (CI_is_return (ch.ch))
231
      break;
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
232
    if (ch.ch < 0)
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
233
    {
234
      def = -1;
235
      break;
236
    }
237
238
#ifdef HAVE_LANGINFO_YESEXPR
239
    answer[0] = ch.ch;
240
    if (reyes_ok ? 
241
	(regexec (& reyes, answer, 0, 0, 0) == 0) :
242
#else
243
    if (
244
#endif
245
	(tolower (ch.ch) == 'y'))
246
    {
247
      def = M_YES;
248
      break;
249
    }
250
    else if (
251
#ifdef HAVE_LANGINFO_YESEXPR
252
	     reno_ok ?
253
	     (regexec (& reno, answer, 0, 0, 0) == 0) :
254
#endif
255
	     (tolower (ch.ch) == 'n'))
256
    {
257
      def = M_NO;
258
      break;
259
    }
260
    else
261
    {
262
      BEEP();
263
    }
264
  }
265
266
#ifdef HAVE_LANGINFO_YESEXPR    
267
  if (reyes_ok)
268
    regfree (& reyes);
269
  if (reno_ok)
270
    regfree (& reno);
271
#endif
272
273
  if (def != -1)
274
  {
275
    addstr ((char *) (def == M_YES ? yes : no));
276
    mutt_refresh ();
277
  }
278
  return (def);
279
}
280
281
/* this function is called when the user presses the abort key */
282
void mutt_query_exit (void)
283
{
284
  mutt_flushinp ();
285
  curs_set (1);
286
  if (Timeout)
287
    timeout (-1); /* restore blocking operation */
288
  if (mutt_yesorno (_("Exit Mutt?"), M_YES) == M_YES)
289
  {
290
    endwin ();
291
    exit (1);
292
  }
293
  mutt_clear_error();
294
  mutt_curs_set (-1);
295
  SigInt = 0;
296
}
297
298
void mutt_curses_error (const char *fmt, ...)
299
{
300
  va_list ap;
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
301
  char scratch[LONG_STRING];
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
302
303
  va_start (ap, fmt);
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
304
  vsnprintf (scratch, sizeof (scratch), fmt, ap);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
305
  va_end (ap);
306
  
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
307
  dprint (1, (debugfile, "%s\n", scratch));
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
308
  mutt_format_string (Errorbuf, sizeof (Errorbuf),
1.1.7 by Christoph Berg
Import upstream version 1.5.17
309
		      0, COLS-2, FMT_LEFT, 0, scratch, sizeof (scratch), 0);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
310
311
  if (!option (OPTKEEPQUIET))
312
  {
313
    BEEP ();
314
    SETCOLOR (MT_COLOR_ERROR);
315
    mvaddstr (LINES-1, 0, Errorbuf);
316
    clrtoeol ();
317
    SETCOLOR (MT_COLOR_NORMAL);
318
    mutt_refresh ();
319
  }
320
321
  set_option (OPTMSGERR);
322
}
323
324
void mutt_curses_message (const char *fmt, ...)
325
{
326
  va_list ap;
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
327
  char scratch[LONG_STRING];
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
328
329
  va_start (ap, fmt);
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
330
  vsnprintf (scratch, sizeof (scratch), fmt, ap);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
331
  va_end (ap);
332
333
  mutt_format_string (Errorbuf, sizeof (Errorbuf),
1.1.7 by Christoph Berg
Import upstream version 1.5.17
334
		      0, COLS-2, FMT_LEFT, 0, scratch, sizeof (scratch), 0);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
335
336
  if (!option (OPTKEEPQUIET))
337
  {
338
    SETCOLOR (MT_COLOR_MESSAGE);
339
    mvaddstr (LINES - 1, 0, Errorbuf);
340
    clrtoeol ();
341
    SETCOLOR (MT_COLOR_NORMAL);
342
    mutt_refresh ();
343
  }
344
345
  unset_option (OPTMSGERR);
346
}
347
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
348
void mutt_progress_init (progress_t* progress, const char *msg,
349
			 unsigned short flags, unsigned short inc,
350
			 long size)
351
{
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
352
  struct timeval tv = { 0, 0 };
353
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
354
  if (!progress)
355
    return;
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
356
  if (option(OPTNOCURSES))
357
    return;
358
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
359
  memset (progress, 0, sizeof (progress_t));
360
  progress->inc = inc;
361
  progress->flags = flags;
362
  progress->msg = msg;
363
  progress->size = size;
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
364
  if (progress->size) {
365
    if (progress->flags & M_PROGRESS_SIZE)
366
      mutt_pretty_size (progress->sizestr, sizeof (progress->sizestr),
367
			progress->size);
368
    else
369
      snprintf (progress->sizestr, sizeof (progress->sizestr), "%ld",
370
		progress->size);
371
  }
372
  if (!inc)
373
  {
374
    if (size)
375
      mutt_message ("%s (%s)", msg, progress->sizestr);
376
    else
377
      mutt_message (msg);
378
    return;
379
  }
380
  if (gettimeofday (&tv, NULL) < 0)
381
    dprint (1, (debugfile, "gettimeofday failed: %d\n", errno));
382
  /* if timestamp is 0 no time-based suppression is done */
383
  if (TimeInc)
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
384
    progress->timestamp = ((unsigned int) tv.tv_sec * 1000)
385
        + (unsigned int) (tv.tv_usec / 1000);
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
386
  mutt_progress_update (progress, 0, 0);
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
387
}
388
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
389
void mutt_progress_update (progress_t* progress, long pos, int percent)
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
390
{
391
  char posstr[SHORT_STRING];
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
392
  short update = 0;
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
393
  struct timeval tv = { 0, 0 };
394
  unsigned int now = 0;
395
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
396
  if (option(OPTNOCURSES))
397
    return;
398
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
399
  if (!progress->inc)
400
    goto out;
401
402
  /* refresh if size > inc */
403
  if (progress->flags & M_PROGRESS_SIZE &&
404
      (pos >= progress->pos + (progress->inc << 10)))
405
    update = 1;
406
  else if (pos >= progress->pos + progress->inc)
407
    update = 1;
408
409
  /* skip refresh if not enough time has passed */
410
  if (update && progress->timestamp && !gettimeofday (&tv, NULL)) {
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
411
    now = ((unsigned int) tv.tv_sec * 1000)
412
          + (unsigned int) (tv.tv_usec / 1000);
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
413
    if (now && now - progress->timestamp < TimeInc)
414
      update = 0;
415
  }
416
417
  /* always show the first update */
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
418
  if (!pos)
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
419
    update = 1;
420
421
  if (update)
422
  {
423
    if (progress->flags & M_PROGRESS_SIZE)
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
424
    {
425
      pos = pos / (progress->inc << 10) * (progress->inc << 10);
426
      mutt_pretty_size (posstr, sizeof (posstr), pos);
427
    }
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
428
    else
429
      snprintf (posstr, sizeof (posstr), "%ld", pos);
1.3.1 by Antonio Radici
Import upstream version 1.5.20
430
431
    dprint (5, (debugfile, "updating progress: %s\n", posstr));
432
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
433
    progress->pos = pos;
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
434
    if (now)
435
      progress->timestamp = now;
436
437
    if (progress->size > 0)
438
    {
439
      mutt_message ("%s %s/%s (%d%%)", progress->msg, posstr, progress->sizestr,
1.1.9 by Michael Vogt
Import upstream version 1.5.18
440
		    percent > 0 ? percent :
441
		   	(int) (100.0 * (double) progress->pos / progress->size));
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
442
    }
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
443
    else
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
444
    {
445
      if (percent > 0)
446
	mutt_message ("%s %s (%d%%)", progress->msg, posstr, percent);
447
      else
448
	mutt_message ("%s %s", progress->msg, posstr);
449
    }
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
450
  }
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
451
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
452
out:
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
453
  if (pos >= progress->size)
454
    mutt_clear_error ();
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
455
}
456
457
void mutt_show_error (void)
458
{
459
  if (option (OPTKEEPQUIET))
460
    return;
461
  
462
  SETCOLOR (option (OPTMSGERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
463
  CLEARLINE (LINES-1);
464
  addstr (Errorbuf);
465
  SETCOLOR (MT_COLOR_NORMAL);
466
}
467
468
void mutt_endwin (const char *msg)
469
{
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
470
  int e = errno;
471
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
472
  if (!option (OPTNOCURSES))
473
  {
474
    CLEARLINE (LINES - 1);
475
    
476
    attrset (A_NORMAL);
477
    mutt_refresh ();
478
    endwin ();
479
  }
480
  
481
  if (msg && *msg)
482
  {
483
    puts (msg);
484
    fflush (stdout);
485
  }
1.1.8 by Christoph Berg
Import upstream version 1.5.17+20080114
486
487
  errno = e;
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
488
}
489
490
void mutt_perror (const char *s)
491
{
492
  char *p = strerror (errno);
493
494
  dprint (1, (debugfile, "%s: %s (errno = %d)\n", s, 
495
      p ? p : "unknown error", errno));
496
  mutt_error ("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
497
}
498
499
int mutt_any_key_to_continue (const char *s)
500
{
501
  struct termios t;
502
  struct termios old;
503
  int f, ch;
504
505
  f = open ("/dev/tty", O_RDONLY);
506
  tcgetattr (f, &t);
507
  memcpy ((void *)&old, (void *)&t, sizeof(struct termios)); /* save original state */
508
  t.c_lflag &= ~(ICANON | ECHO);
509
  t.c_cc[VMIN] = 1;
510
  t.c_cc[VTIME] = 0;
511
  tcsetattr (f, TCSADRAIN, &t);
512
  fflush (stdout);
513
  if (s)
514
    fputs (s, stdout);
515
  else
516
    fputs (_("Press any key to continue..."), stdout);
517
  fflush (stdout);
518
  ch = fgetc (stdin);
519
  fflush (stdin);
520
  tcsetattr (f, TCSADRAIN, &old);
521
  close (f);
522
  fputs ("\r\n", stdout);
523
  mutt_clear_error ();
524
  return (ch);
525
}
526
527
int mutt_do_pager (const char *banner,
528
		   const char *tempfile,
529
		   int do_color,
530
		   pager_t *info)
531
{
532
  int rc;
533
  
534
  if (!Pager || mutt_strcmp (Pager, "builtin") == 0)
535
    rc = mutt_pager (banner, tempfile, do_color, info);
536
  else
537
  {
538
    char cmd[STRING];
539
    
540
    mutt_endwin (NULL);
541
    mutt_expand_file_fmt (cmd, sizeof(cmd), Pager, tempfile);
542
    if (mutt_system (cmd) == -1)
543
    {
544
      mutt_error (_("Error running \"%s\"!"), cmd);
545
      rc = -1;
546
    }
547
    else
548
      rc = 0;
549
    mutt_unlink (tempfile);
550
  }
551
552
  return rc;
553
}
554
555
int _mutt_enter_fname (const char *prompt, char *buf, size_t blen, int *redraw, int buffy, int multiple, char ***files, int *numfiles)
556
{
557
  event_t ch;
558
559
  mvaddstr (LINES-1, 0, (char *) prompt);
560
  addstr (_(" ('?' for list): "));
561
  if (buf[0])
562
    addstr (buf);
563
  clrtoeol ();
564
  mutt_refresh ();
565
566
  ch = mutt_getch();
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
567
  if (ch.ch < 0)
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
568
  {
569
    CLEARLINE (LINES-1);
570
    return (-1);
571
  }
572
  else if (ch.ch == '?')
573
  {
574
    mutt_refresh ();
575
    buf[0] = 0;
576
    _mutt_select_file (buf, blen, M_SEL_FOLDER | (multiple ? M_SEL_MULTI : 0), 
577
		       files, numfiles);
578
    *redraw = REDRAW_FULL;
579
  }
580
  else
581
  {
582
    char *pc = safe_malloc (mutt_strlen (prompt) + 3);
583
584
    sprintf (pc, "%s: ", prompt);	/* __SPRINTF_CHECKED__ */
585
    mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
586
    if (_mutt_get_field (pc, buf, blen, (buffy ? M_EFILE : M_FILE) | M_CLEAR, multiple, files, numfiles)
587
	!= 0)
588
      buf[0] = 0;
589
    MAYBE_REDRAW (*redraw);
590
    FREE (&pc);
591
  }
592
593
  return 0;
594
}
595
596
void mutt_ungetch (int ch, int op)
597
{
598
  event_t tmp;
599
600
  tmp.ch = ch;
601
  tmp.op = op;
602
603
  if (UngetCount >= UngetBufLen)
604
    safe_realloc (&KeyEvent, (UngetBufLen += 128) * sizeof(event_t));
605
606
  KeyEvent[UngetCount++] = tmp;
607
}
608
609
void mutt_flushinp (void)
610
{
611
  UngetCount = 0;
612
  flushinp ();
613
}
614
615
#if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
616
/* The argument can take 3 values:
617
 * -1: restore the value of the last call
618
 *  0: make the cursor invisible
619
 *  1: make the cursor visible
620
 */
621
void mutt_curs_set (int cursor)
622
{
623
  static int SavedCursor = 1;
624
  
625
  if (cursor < 0)
626
    cursor = SavedCursor;
627
  else
628
    SavedCursor = cursor;
629
  
630
  if (curs_set (cursor) == ERR) {
631
    if (cursor == 1)	/* cnorm */
632
      curs_set (2);	/* cvvis */
633
  }
634
}
635
#endif
636
637
int mutt_multi_choice (char *prompt, char *letters)
638
{
639
  event_t ch;
640
  int choice;
641
  char *p;
642
643
  mvaddstr (LINES - 1, 0, prompt);
644
  clrtoeol ();
645
  FOREVER
646
  {
647
    mutt_refresh ();
648
    ch  = mutt_getch ();
1.1.10 by Bhavani Shankar
Import upstream version 1.5.19
649
    if (ch.ch < 0 || CI_is_return (ch.ch))
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
650
    {
651
      choice = -1;
652
      break;
653
    }
654
    else
655
    {
656
      p = strchr (letters, ch.ch);
657
      if (p)
658
      {
659
	choice = p - letters + 1;
660
	break;
661
      }
662
      else if (ch.ch <= '9' && ch.ch > '0')
663
      {
664
	choice = ch.ch - '0';
665
	if (choice <= mutt_strlen (letters))
666
	  break;
667
      }
668
    }
669
    BEEP ();
670
  }
671
  CLEARLINE (LINES - 1);
672
  mutt_refresh ();
673
  return choice;
674
}
675
676
/*
677
 * addwch would be provided by an up-to-date curses library
678
 */
679
680
int mutt_addwch (wchar_t wc)
681
{
682
  char buf[MB_LEN_MAX*2];
683
  mbstate_t mbstate;
684
  size_t n1, n2;
685
686
  memset (&mbstate, 0, sizeof (mbstate));
687
  if ((n1 = wcrtomb (buf, wc, &mbstate)) == (size_t)(-1) ||
688
      (n2 = wcrtomb (buf + n1, 0, &mbstate)) == (size_t)(-1))
689
    return -1; /* ERR */
690
  else
691
    return addstr (buf);
692
}
693
694
695
/*
696
 * This formats a string, a bit like
697
 * snprintf (dest, destlen, "%-*.*s", min_width, max_width, s),
698
 * except that the widths refer to the number of character cells
699
 * when printed.
700
 */
701
702
void mutt_format_string (char *dest, size_t destlen,
703
			 int min_width, int max_width,
1.1.7 by Christoph Berg
Import upstream version 1.5.17
704
			 int justify, char m_pad_char,
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
705
			 const char *s, size_t n,
706
			 int arboreal)
707
{
708
  char *p;
709
  wchar_t wc;
710
  int w;
711
  size_t k, k2;
712
  char scratch[MB_LEN_MAX];
713
  mbstate_t mbstate1, mbstate2;
714
715
  memset(&mbstate1, 0, sizeof (mbstate1));
716
  memset(&mbstate2, 0, sizeof (mbstate2));
717
  --destlen;
718
  p = dest;
719
  for (; n && (k = mbrtowc (&wc, s, n, &mbstate1)); s += k, n -= k)
720
  {
721
    if (k == (size_t)(-1) || k == (size_t)(-2))
722
    {
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
723
      if (k == (size_t)(-1) && errno == EILSEQ)
724
	memset (&mbstate1, 0, sizeof (mbstate1));
725
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
726
      k = (k == (size_t)(-1)) ? 1 : n;
727
      wc = replacement_char ();
728
    }
729
    if (arboreal && wc < M_TREE_MAX)
730
      w = 1; /* hack */
731
    else
732
    {
733
      if (!IsWPrint (wc))
734
	wc = '?';
735
      w = wcwidth (wc);
736
    }
737
    if (w >= 0)
738
    {
739
      if (w > max_width || (k2 = wcrtomb (scratch, wc, &mbstate2)) > destlen)
740
	break;
741
      min_width -= w;
742
      max_width -= w;
743
      strncpy (p, scratch, k2);
744
      p += k2;            
745
      destlen -= k2;
746
    }
747
  }
748
  w = (int)destlen < min_width ? destlen : min_width;
749
  if (w <= 0)
750
    *p = '\0';
1.1.7 by Christoph Berg
Import upstream version 1.5.17
751
  else if (justify == FMT_RIGHT)	/* right justify */
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
752
  {
753
    p[w] = '\0';
754
    while (--p >= dest)
755
      p[w] = *p;
756
    while (--w >= 0)
757
      dest[w] = m_pad_char;
758
  }
1.1.7 by Christoph Berg
Import upstream version 1.5.17
759
  else if (justify == FMT_CENTER)	/* center */
760
  {
761
    char *savedp = p;
762
    int half = (w+1) / 2; /* half of cushion space */
763
764
    p[w] = '\0';
765
766
    /* move str to center of buffer */
767
    while (--p >= dest)
768
      p[half] = *p;
769
770
    /* fill rhs */
771
    p = savedp + half;
772
    while (--w >= half)
773
      *p++ = m_pad_char;
774
775
    /* fill lhs */
776
    while (half--)
777
      dest[half] = m_pad_char;
778
  }
779
  else					/* left justify */
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
780
  {
781
    while (--w >= 0)
782
      *p++ = m_pad_char;
783
    *p = '\0';
784
  }
785
}
786
787
/*
788
 * This formats a string rather like
789
 *   snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
790
 *   snprintf (dest, destlen, fmt, s);
791
 * except that the numbers in the conversion specification refer to
792
 * the number of character cells when printed.
793
 */
794
795
static void mutt_format_s_x (char *dest,
796
			     size_t destlen,
797
			     const char *prefix,
798
			     const char *s,
799
			     int arboreal)
800
{
1.1.7 by Christoph Berg
Import upstream version 1.5.17
801
  int justify = FMT_RIGHT;
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
802
  char *p;
803
  int min_width;
804
  int max_width = INT_MAX;
805
806
  if (*prefix == '-')
1.1.7 by Christoph Berg
Import upstream version 1.5.17
807
    ++prefix, justify = FMT_LEFT;
808
  else if (*prefix == '=')
809
    ++prefix, justify = FMT_CENTER;
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
810
  min_width = strtol (prefix, &p, 10);
811
  if (*p == '.')
812
  {
813
    prefix = p + 1;
814
    max_width = strtol (prefix, &p, 10);
815
    if (p <= prefix)
816
      max_width = INT_MAX;
817
  }
818
819
  mutt_format_string (dest, destlen, min_width, max_width,
1.1.7 by Christoph Berg
Import upstream version 1.5.17
820
		      justify, ' ', s, mutt_strlen (s), arboreal);
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
821
}
822
823
void mutt_format_s (char *dest,
824
		    size_t destlen,
825
		    const char *prefix,
826
		    const char *s)
827
{
828
  mutt_format_s_x (dest, destlen, prefix, s, 0);
829
}
830
831
void mutt_format_s_tree (char *dest,
832
			 size_t destlen,
833
			 const char *prefix,
834
			 const char *s)
835
{
836
  mutt_format_s_x (dest, destlen, prefix, s, 1);
837
}
838
839
/*
840
 * mutt_paddstr (n, s) is almost equivalent to
1.1.7 by Christoph Berg
Import upstream version 1.5.17
841
 * mutt_format_string (bigbuf, big, n, n, FMT_LEFT, ' ', s, big, 0), addstr (bigbuf)
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
842
 */
843
844
void mutt_paddstr (int n, const char *s)
845
{
846
  wchar_t wc;
847
  int w;
848
  size_t k;
849
  size_t len = mutt_strlen (s);
850
  mbstate_t mbstate;
851
852
  memset (&mbstate, 0, sizeof (mbstate));
853
  for (; len && (k = mbrtowc (&wc, s, len, &mbstate)); s += k, len -= k)
854
  {
855
    if (k == (size_t)(-1) || k == (size_t)(-2))
856
    {
1.1.5 by Christoph Berg
Import upstream version 1.5.15+20070412
857
      if (k == (size_t) (-1))
858
	memset (&mbstate, 0, sizeof (mbstate));
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
859
      k = (k == (size_t)(-1)) ? 1 : len;
860
      wc = replacement_char ();
861
    }
862
    if (!IsWPrint (wc))
863
      wc = '?';
864
    w = wcwidth (wc);
865
    if (w >= 0)
866
    {
867
      if (w > n)
868
	break;
869
      addnstr ((char *)s, k);
870
      n -= w;
871
    }
872
  }
873
  while (n-- > 0)
874
    addch (' ');
875
}
876
1.1.7 by Christoph Berg
Import upstream version 1.5.17
877
/* See how many bytes to copy from string so its at most maxlen bytes
878
 * long and maxwid columns wide */
879
int mutt_wstr_trunc (const char *src, size_t maxlen, size_t maxwid, size_t *width)
880
{
881
  wchar_t wc;
882
  int w = 0, l = 0, cl;
883
  size_t cw, n;
884
  mbstate_t mbstate;
885
886
  if (!src)
887
    goto out;
888
889
  n = mutt_strlen (src);
890
891
  memset (&mbstate, 0, sizeof (mbstate));
892
  for (w = 0; n && (cl = mbrtowc (&wc, src, n, &mbstate)); src += cl, n -= cl)
893
  {
894
    if (cl == (size_t)(-1) || cl == (size_t)(-2))
895
      cw = cl = 1;
896
    else
897
      cw = wcwidth (wc);
898
    if (cl + l > maxlen || cw + w > maxwid)
899
      break;
900
    l += cl;
901
    w += cw;
902
  }
903
out:
904
  if (width)
905
    *width = w;
906
  return l;
907
}
908
909
/*
910
 * returns the number of bytes the first (multibyte) character
911
 * of input consumes:
912
 * 	< 0 ... conversion error
913
 * 	= 0 ... end of input
914
 * 	> 0 ... length (bytes)
915
 */
916
int mutt_charlen (const char *s, int *width)
917
{
918
  wchar_t wc;
919
  mbstate_t mbstate;
920
  size_t k, n;
921
922
  if (!s || !*s)
923
    return 0;
924
925
  n = mutt_strlen (s);
926
  memset (&mbstate, 0, sizeof (mbstate));
927
  k = mbrtowc (&wc, s, n, &mbstate);
928
  if (width)
929
    *width = wcwidth (wc);
930
  return (k == (size_t)(-1) || k == (size_t)(-2)) ? -1 : k;
931
}
932
1.1.2 by Martin Pitt
Import upstream version 1.5.11+cvs20060403
933
/*
934
 * mutt_strwidth is like mutt_strlen except that it returns the width
935
 * refering to the number of characters cells.
936
 */
937
938
int mutt_strwidth (const char *s)
939
{
940
  wchar_t wc;
941
  int w;
942
  size_t k, n;
943
  mbstate_t mbstate;
944
945
  if (!s) return 0;
946
947
  n = mutt_strlen (s);
948
949
  memset (&mbstate, 0, sizeof (mbstate));
950
  for (w=0; n && (k = mbrtowc (&wc, s, n, &mbstate)); s += k, n -= k)
951
  {
952
    if (k == (size_t)(-1) || k == (size_t)(-2))
953
    {
954
      k = (k == (size_t)(-1)) ? 1 : n;
955
      wc = replacement_char ();
956
    }
957
    if (!IsWPrint (wc))
958
      wc = '?';
959
    w += wcwidth (wc);
960
  }
961
  return w;
962
}