Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 962 lines (837 sloc) 20.963 kb
14c29200 » Antonio Radici
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 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
75 keypad (stdscr, TRUE);
76 clearok (stdscr, TRUE);
77 set_option (OPTNEEDREDRAW);
14c29200 » Antonio Radici
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 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
84 event_t timeout = {-2, OP_NULL};
14c29200 » Antonio Radici
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)
101 mutt_query_exit ();
102
103 if(ch == ERR)
104 {
105 /* either timeout or the terminal has been lost */
106 if (!isatty (0))
107 {
108 endwin ();
109 exit (1);
110 }
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
111 return timeout;
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
112 }
113
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);
175 if (mutt_system (cmd))
176 {
177 mutt_error (_("Error running \"%s\"!"), cmd);
178 mutt_sleep (2);
179 }
19304f7c » Antonio Radici
2009-05-24 Imported 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
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
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;
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
232 if (ch.ch < 0)
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
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;
301 char scratch[LONG_STRING];
302
303 va_start (ap, fmt);
304 vsnprintf (scratch, sizeof (scratch), fmt, ap);
305 va_end (ap);
306
307 dprint (1, (debugfile, "%s\n", scratch));
308 mutt_format_string (Errorbuf, sizeof (Errorbuf),
309 0, COLS-2, FMT_LEFT, 0, scratch, sizeof (scratch), 0);
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;
327 char scratch[LONG_STRING];
328
329 va_start (ap, fmt);
330 vsnprintf (scratch, sizeof (scratch), fmt, ap);
331 va_end (ap);
332
333 mutt_format_string (Errorbuf, sizeof (Errorbuf),
334 0, COLS-2, FMT_LEFT, 0, scratch, sizeof (scratch), 0);
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
348 void mutt_progress_init (progress_t* progress, const char *msg,
349 unsigned short flags, unsigned short inc,
350 long size)
351 {
352 struct timeval tv = { 0, 0 };
353
354 if (!progress)
355 return;
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
356 if (option(OPTNOCURSES))
357 return;
358
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
359 memset (progress, 0, sizeof (progress_t));
360 progress->inc = inc;
361 progress->flags = flags;
362 progress->msg = msg;
363 progress->size = size;
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)
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
384 progress->timestamp = ((unsigned int) tv.tv_sec * 1000)
385 + (unsigned int) (tv.tv_usec / 1000);
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
386 mutt_progress_update (progress, 0, 0);
387 }
388
389 void mutt_progress_update (progress_t* progress, long pos, int percent)
390 {
391 char posstr[SHORT_STRING];
392 short update = 0;
393 struct timeval tv = { 0, 0 };
394 unsigned int now = 0;
395
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
396 if (option(OPTNOCURSES))
397 return;
398
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
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)) {
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
411 now = ((unsigned int) tv.tv_sec * 1000)
412 + (unsigned int) (tv.tv_usec / 1000);
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
413 if (now && now - progress->timestamp < TimeInc)
414 update = 0;
415 }
416
417 /* always show the first update */
418 if (!pos)
419 update = 1;
420
421 if (update)
422 {
423 if (progress->flags & M_PROGRESS_SIZE)
424 {
425 pos = pos / (progress->inc << 10) * (progress->inc << 10);
426 mutt_pretty_size (posstr, sizeof (posstr), pos);
427 }
428 else
429 snprintf (posstr, sizeof (posstr), "%ld", pos);
647ac544 » Antonio Radici
2009-06-14 Imported Upstream version 1.5.20
430
431 dprint (5, (debugfile, "updating progress: %s\n", posstr));
432
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
433 progress->pos = pos;
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,
440 percent > 0 ? percent :
441 (int) (100.0 * (double) progress->pos / progress->size));
442 }
443 else
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 }
450 }
451
452 out:
453 if (pos >= progress->size)
454 mutt_clear_error ();
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 {
470 int e = errno;
471
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 }
486
487 errno = e;
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();
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
567 if (ch.ch < 0)
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
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 ();
19304f7c » Antonio Radici
2009-05-24 Imported Upstream version 1.5.19
649 if (ch.ch < 0 || CI_is_return (ch.ch))
14c29200 » Antonio Radici
2009-05-24 Imported Upstream version 1.5.18
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,
704 int justify, char m_pad_char,
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 {
723 if (k == (size_t)(-1) && errno == EILSEQ)
724 memset (&mbstate1, 0, sizeof (mbstate1));
725
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';
751 else if (justify == FMT_RIGHT) /* right justify */
752 {
753 p[w] = '\0';
754 while (--p >= dest)
755 p[w] = *p;
756 while (--w >= 0)
757 dest[w] = m_pad_char;
758 }
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 */
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 {
801 int justify = FMT_RIGHT;
802 char *p;
803 int min_width;
804 int max_width = INT_MAX;
805
806 if (*prefix == '-')
807 ++prefix, justify = FMT_LEFT;
808 else if (*prefix == '=')
809 ++prefix, justify = FMT_CENTER;
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,
820 justify, ' ', s, mutt_strlen (s), arboreal);
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
841 * mutt_format_string (bigbuf, big, n, n, FMT_LEFT, ' ', s, big, 0), addstr (bigbuf)
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 {
857 if (k == (size_t) (-1))
858 memset (&mbstate, 0, sizeof (mbstate));
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
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
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 }
Something went wrong with that request. Please try again.