Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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