Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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