mutt stable branch with some hacks
at master 1202 lines 28 kB view raw
1/* 2 * Copyright (C) 1996-2002,2010,2012-2013 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 52/* These are used for macros and exec/push commands. 53 * They can be temporarily ignored by setting OPTIGNOREMACROEVENTS 54 */ 55static size_t MacroBufferCount = 0; 56static size_t MacroBufferLen = 0; 57static event_t *MacroEvents; 58 59/* These are used in all other "normal" situations, and are not 60 * ignored when setting OPTIGNOREMACROEVENTS 61 */ 62static size_t UngetCount = 0; 63static size_t UngetLen = 0; 64static event_t *UngetKeyEvents; 65 66mutt_window_t *MuttHelpWindow = NULL; 67mutt_window_t *MuttIndexWindow = NULL; 68mutt_window_t *MuttStatusWindow = NULL; 69mutt_window_t *MuttMessageWindow = NULL; 70#ifdef USE_SIDEBAR 71mutt_window_t *MuttSidebarWindow = NULL; 72#endif 73 74void mutt_refresh (void) 75{ 76 /* don't refresh when we are waiting for a child. */ 77 if (option (OPTKEEPQUIET)) 78 return; 79 80 /* don't refresh in the middle of macros unless necessary */ 81 if (UngetCount && !option (OPTFORCEREFRESH)) 82 return; 83 84 /* else */ 85 refresh (); 86} 87 88/* Make sure that the next refresh does a full refresh. This could be 89 optimized by not doing it at all if DISPLAY is set as this might 90 indicate that a GUI based pinentry was used. Having an option to 91 customize this is of course the Mutt way. */ 92void mutt_need_hard_redraw (void) 93{ 94 keypad (stdscr, TRUE); 95 clearok (stdscr, TRUE); 96 set_option (OPTNEEDREDRAW); 97} 98 99event_t mutt_getch (void) 100{ 101 int ch; 102 event_t err = {-1, OP_NULL }, ret; 103 event_t timeout = {-2, OP_NULL}; 104 105 if (UngetCount) 106 return (UngetKeyEvents[--UngetCount]); 107 108 if (!option(OPTIGNOREMACROEVENTS) && MacroBufferCount) 109 return (MacroEvents[--MacroBufferCount]); 110 111 SigInt = 0; 112 113 mutt_allow_interrupt (1); 114#ifdef KEY_RESIZE 115 /* ncurses 4.2 sends this when the screen is resized */ 116 ch = KEY_RESIZE; 117 while (ch == KEY_RESIZE) 118#endif /* KEY_RESIZE */ 119 ch = getch (); 120 mutt_allow_interrupt (0); 121 122 if (SigInt) 123 { 124 mutt_query_exit (); 125 return err; 126 } 127 128 /* either timeout, a sigwinch (if timeout is set), or the terminal 129 * has been lost */ 130 if (ch == ERR) 131 { 132 if (!isatty (0)) 133 { 134 endwin (); 135 exit (1); 136 } 137 return timeout; 138 } 139 140 if ((ch & 0x80) && option (OPTMETAKEY)) 141 { 142 /* send ALT-x as ESC-x */ 143 ch &= ~0x80; 144 mutt_unget_event (ch, 0); 145 ret.ch = '\033'; 146 ret.op = 0; 147 return ret; 148 } 149 150 ret.ch = ch; 151 ret.op = 0; 152 return (ch == ctrl ('G') ? err : ret); 153} 154 155int _mutt_get_field (const char *field, char *buf, size_t buflen, int complete, int multiple, char ***files, int *numfiles) 156{ 157 int ret; 158 int x; 159 160 ENTER_STATE *es = mutt_new_enter_state(); 161 162 do 163 { 164 mutt_window_clearline (MuttMessageWindow, 0); 165 SETCOLOR (MT_COLOR_PROMPT); 166 addstr ((char *)field); /* cast to get around bad prototypes */ 167 NORMAL_COLOR; 168 mutt_refresh (); 169 mutt_window_getyx (MuttMessageWindow, NULL, &x); 170 ret = _mutt_enter_string (buf, buflen, x, complete, multiple, files, numfiles, es); 171 } 172 while (ret == 1); 173 mutt_window_clearline (MuttMessageWindow, 0); 174 mutt_free_enter_state (&es); 175 176 return (ret); 177} 178 179int mutt_get_field_unbuffered (char *msg, char *buf, size_t buflen, int flags) 180{ 181 int rc; 182 183 set_option (OPTIGNOREMACROEVENTS); 184 rc = mutt_get_field (msg, buf, buflen, flags); 185 unset_option (OPTIGNOREMACROEVENTS); 186 187 return (rc); 188} 189 190void mutt_clear_error (void) 191{ 192 Errorbuf[0] = 0; 193 if (!option(OPTNOCURSES)) 194 mutt_window_clearline (MuttMessageWindow, 0); 195} 196 197void mutt_edit_file (const char *editor, const char *data) 198{ 199 char cmd[LONG_STRING]; 200 201 mutt_endwin (NULL); 202 mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data); 203 if (mutt_system (cmd)) 204 { 205 mutt_error (_("Error running \"%s\"!"), cmd); 206 mutt_sleep (2); 207 } 208#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) 209 /* the terminal may have been resized while the editor owned it */ 210 mutt_resize_screen (); 211#endif 212 keypad (stdscr, TRUE); 213 clearok (stdscr, TRUE); 214} 215 216int mutt_yesorno (const char *msg, int def) 217{ 218 event_t ch; 219 char *yes = _("yes"); 220 char *no = _("no"); 221 char *answer_string; 222 size_t answer_string_len; 223 size_t msglen; 224 225#ifdef HAVE_LANGINFO_YESEXPR 226 char *expr; 227 regex_t reyes; 228 regex_t reno; 229 int reyes_ok; 230 int reno_ok; 231 char answer[2]; 232 233 answer[1] = 0; 234 235 reyes_ok = (expr = nl_langinfo (YESEXPR)) && expr[0] == '^' && 236 !REGCOMP (&reyes, expr, REG_NOSUB); 237 reno_ok = (expr = nl_langinfo (NOEXPR)) && expr[0] == '^' && 238 !REGCOMP (&reno, expr, REG_NOSUB); 239#endif 240 241 mutt_window_clearline (MuttMessageWindow, 0); 242 243 /* 244 * In order to prevent the default answer to the question to wrapped 245 * around the screen in the even the question is wider than the screen, 246 * ensure there is enough room for the answer and truncate the question 247 * to fit. 248 */ 249 safe_asprintf (&answer_string, " ([%s]/%s): ", def == MUTT_YES ? yes : no, def == MUTT_YES ? no : yes); 250 answer_string_len = mutt_strwidth (answer_string); 251 /* maxlen here is sort of arbitrary, so pick a reasonable upper bound */ 252 msglen = mutt_wstr_trunc (msg, 4*MuttMessageWindow->cols, MuttMessageWindow->cols - answer_string_len, NULL); 253 SETCOLOR (MT_COLOR_PROMPT); 254 addnstr (msg, msglen); 255 addstr (answer_string); 256 NORMAL_COLOR; 257 FREE (&answer_string); 258 259 FOREVER 260 { 261 mutt_refresh (); 262 ch = mutt_getch (); 263 if (CI_is_return (ch.ch)) 264 break; 265 if (ch.ch < 0) 266 { 267 def = -1; 268 break; 269 } 270 271#ifdef HAVE_LANGINFO_YESEXPR 272 answer[0] = ch.ch; 273 if (reyes_ok ? 274 (regexec (& reyes, answer, 0, 0, 0) == 0) : 275#else 276 if ( 277#endif 278 (tolower (ch.ch) == 'y')) 279 { 280 def = MUTT_YES; 281 break; 282 } 283 else if ( 284#ifdef HAVE_LANGINFO_YESEXPR 285 reno_ok ? 286 (regexec (& reno, answer, 0, 0, 0) == 0) : 287#endif 288 (tolower (ch.ch) == 'n')) 289 { 290 def = MUTT_NO; 291 break; 292 } 293 else 294 { 295 BEEP(); 296 } 297 } 298 299#ifdef HAVE_LANGINFO_YESEXPR 300 if (reyes_ok) 301 regfree (& reyes); 302 if (reno_ok) 303 regfree (& reno); 304#endif 305 306 if (def != -1) 307 { 308 addstr ((char *) (def == MUTT_YES ? yes : no)); 309 mutt_refresh (); 310 } 311 else 312 { 313 /* when the users cancels with ^G, clear the message stored with 314 * mutt_message() so it isn't displayed when the screen is refreshed. */ 315 mutt_clear_error(); 316 } 317 return (def); 318} 319 320/* this function is called when the user presses the abort key */ 321void mutt_query_exit (void) 322{ 323 mutt_flushinp (); 324 curs_set (1); 325 if (Timeout) 326 timeout (-1); /* restore blocking operation */ 327 if (mutt_yesorno (_("Exit Mutt?"), MUTT_YES) == MUTT_YES) 328 { 329 endwin (); 330 exit (1); 331 } 332 mutt_clear_error(); 333 mutt_curs_set (-1); 334 SigInt = 0; 335} 336 337static void curses_message (int error, const char *fmt, va_list ap) 338{ 339 char scratch[LONG_STRING]; 340 341 vsnprintf (scratch, sizeof (scratch), fmt, ap); 342 343 dprint (1, (debugfile, "%s\n", scratch)); 344 mutt_format_string (Errorbuf, sizeof (Errorbuf), 345 0, MuttMessageWindow->cols, FMT_LEFT, 0, scratch, sizeof (scratch), 0); 346 347 if (!option (OPTKEEPQUIET)) 348 { 349 if (error) 350 BEEP (); 351 SETCOLOR (error ? MT_COLOR_ERROR : MT_COLOR_MESSAGE); 352 mutt_window_mvaddstr (MuttMessageWindow, 0, 0, Errorbuf); 353 NORMAL_COLOR; 354 mutt_window_clrtoeol (MuttMessageWindow); 355 mutt_refresh (); 356 } 357 358 if (error) 359 set_option (OPTMSGERR); 360 else 361 unset_option (OPTMSGERR); 362} 363 364void mutt_curses_error (const char *fmt, ...) 365{ 366 va_list ap; 367 368 va_start (ap, fmt); 369 curses_message (1, fmt, ap); 370 va_end (ap); 371} 372 373void mutt_curses_message (const char *fmt, ...) 374{ 375 va_list ap; 376 377 va_start (ap, fmt); 378 curses_message (0, fmt, ap); 379 va_end (ap); 380} 381 382void mutt_progress_init (progress_t* progress, const char *msg, 383 unsigned short flags, unsigned short inc, 384 long size) 385{ 386 struct timeval tv = { 0, 0 }; 387 388 if (!progress) 389 return; 390 if (option(OPTNOCURSES)) 391 return; 392 393 memset (progress, 0, sizeof (progress_t)); 394 progress->inc = inc; 395 progress->flags = flags; 396 progress->msg = msg; 397 progress->size = size; 398 if (progress->size) { 399 if (progress->flags & MUTT_PROGRESS_SIZE) 400 mutt_pretty_size (progress->sizestr, sizeof (progress->sizestr), 401 progress->size); 402 else 403 snprintf (progress->sizestr, sizeof (progress->sizestr), "%ld", 404 progress->size); 405 } 406 if (!inc) 407 { 408 if (size) 409 mutt_message ("%s (%s)", msg, progress->sizestr); 410 else 411 mutt_message (msg); 412 return; 413 } 414 if (gettimeofday (&tv, NULL) < 0) 415 dprint (1, (debugfile, "gettimeofday failed: %d\n", errno)); 416 /* if timestamp is 0 no time-based suppression is done */ 417 if (TimeInc) 418 progress->timestamp = ((unsigned int) tv.tv_sec * 1000) 419 + (unsigned int) (tv.tv_usec / 1000); 420 mutt_progress_update (progress, 0, 0); 421} 422 423void mutt_progress_update (progress_t* progress, long pos, int percent) 424{ 425 char posstr[SHORT_STRING]; 426 short update = 0; 427 struct timeval tv = { 0, 0 }; 428 unsigned int now = 0; 429 430 if (option(OPTNOCURSES)) 431 return; 432 433 if (!progress->inc) 434 goto out; 435 436 /* refresh if size > inc */ 437 if (progress->flags & MUTT_PROGRESS_SIZE && 438 (pos >= progress->pos + (progress->inc << 10))) 439 update = 1; 440 else if (pos >= progress->pos + progress->inc) 441 update = 1; 442 443 /* skip refresh if not enough time has passed */ 444 if (update && progress->timestamp && !gettimeofday (&tv, NULL)) { 445 now = ((unsigned int) tv.tv_sec * 1000) 446 + (unsigned int) (tv.tv_usec / 1000); 447 if (now && now - progress->timestamp < TimeInc) 448 update = 0; 449 } 450 451 /* always show the first update */ 452 if (!pos) 453 update = 1; 454 455 if (update) 456 { 457 if (progress->flags & MUTT_PROGRESS_SIZE) 458 { 459 pos = pos / (progress->inc << 10) * (progress->inc << 10); 460 mutt_pretty_size (posstr, sizeof (posstr), pos); 461 } 462 else 463 snprintf (posstr, sizeof (posstr), "%ld", pos); 464 465 dprint (5, (debugfile, "updating progress: %s\n", posstr)); 466 467 progress->pos = pos; 468 if (now) 469 progress->timestamp = now; 470 471 if (progress->size > 0) 472 { 473 mutt_message ("%s %s/%s (%d%%)", progress->msg, posstr, progress->sizestr, 474 percent > 0 ? percent : 475 (int) (100.0 * (double) progress->pos / progress->size)); 476 } 477 else 478 { 479 if (percent > 0) 480 mutt_message ("%s %s (%d%%)", progress->msg, posstr, percent); 481 else 482 mutt_message ("%s %s", progress->msg, posstr); 483 } 484 } 485 486out: 487 if (pos >= progress->size) 488 mutt_clear_error (); 489} 490 491void mutt_init_windows () 492{ 493 MuttHelpWindow = safe_calloc (sizeof (mutt_window_t), 1); 494 MuttIndexWindow = safe_calloc (sizeof (mutt_window_t), 1); 495 MuttStatusWindow = safe_calloc (sizeof (mutt_window_t), 1); 496 MuttMessageWindow = safe_calloc (sizeof (mutt_window_t), 1); 497#ifdef USE_SIDEBAR 498 MuttSidebarWindow = safe_calloc (sizeof (mutt_window_t), 1); 499#endif 500} 501 502void mutt_free_windows () 503{ 504 FREE (&MuttHelpWindow); 505 FREE (&MuttIndexWindow); 506 FREE (&MuttStatusWindow); 507 FREE (&MuttMessageWindow); 508#ifdef USE_SIDEBAR 509 FREE (&MuttSidebarWindow); 510#endif 511} 512 513void mutt_reflow_windows (void) 514{ 515 if (option (OPTNOCURSES)) 516 return; 517 518 dprint (2, (debugfile, "In mutt_reflow_windows\n")); 519 520 MuttStatusWindow->rows = 1; 521 MuttStatusWindow->cols = COLS; 522 MuttStatusWindow->row_offset = option (OPTSTATUSONTOP) ? 0 : LINES - 2; 523 MuttStatusWindow->col_offset = 0; 524 525 memcpy (MuttHelpWindow, MuttStatusWindow, sizeof (mutt_window_t)); 526 if (! option (OPTHELP)) 527 MuttHelpWindow->rows = 0; 528 else 529 MuttHelpWindow->row_offset = option (OPTSTATUSONTOP) ? LINES - 2 : 0; 530 531 memcpy (MuttMessageWindow, MuttStatusWindow, sizeof (mutt_window_t)); 532 MuttMessageWindow->row_offset = LINES - 1; 533 534 memcpy (MuttIndexWindow, MuttStatusWindow, sizeof (mutt_window_t)); 535 MuttIndexWindow->rows = MAX(LINES - MuttStatusWindow->rows - 536 MuttHelpWindow->rows - MuttMessageWindow->rows, 0); 537 MuttIndexWindow->row_offset = option (OPTSTATUSONTOP) ? MuttStatusWindow->rows : 538 MuttHelpWindow->rows; 539 540#ifdef USE_SIDEBAR 541 if (option (OPTSIDEBAR)) 542 { 543 memcpy (MuttSidebarWindow, MuttIndexWindow, sizeof (mutt_window_t)); 544 MuttSidebarWindow->cols = SidebarWidth; 545 546 MuttIndexWindow->cols -= SidebarWidth; 547 MuttIndexWindow->col_offset += SidebarWidth; 548 } 549#endif 550} 551 552int mutt_window_move (mutt_window_t *win, int row, int col) 553{ 554 return move (win->row_offset + row, win->col_offset + col); 555} 556 557int mutt_window_mvaddch (mutt_window_t *win, int row, int col, const chtype ch) 558{ 559 return mvaddch (win->row_offset + row, win->col_offset + col, ch); 560} 561 562int mutt_window_mvaddstr (mutt_window_t *win, int row, int col, const char *str) 563{ 564 return mvaddstr (win->row_offset + row, win->col_offset + col, str); 565} 566 567#ifdef USE_SLANG_CURSES 568static int vw_printw (SLcurses_Window_Type *win, const char *fmt, va_list ap) 569{ 570 char buf[LONG_STRING]; 571 572 (void) SLvsnprintf (buf, sizeof (buf), (char *)fmt, ap); 573 SLcurses_waddnstr (win, buf, -1); 574 return 0; 575} 576#endif 577 578int mutt_window_mvprintw (mutt_window_t *win, int row, int col, const char *fmt, ...) 579{ 580 va_list ap; 581 int rv; 582 583 if ((rv = mutt_window_move (win, row, col) != ERR)) 584 { 585 va_start (ap, fmt); 586 rv = vw_printw (stdscr, fmt, ap); 587 va_end (ap); 588 } 589 590 return rv; 591} 592 593/* Assumes the cursor has already been positioned within the 594 * window. 595 */ 596void mutt_window_clrtoeol (mutt_window_t *win) 597{ 598 int row, col, curcol; 599 600 if (win->col_offset + win->cols == COLS) 601 clrtoeol (); 602 else 603 { 604 getyx (stdscr, row, col); 605 curcol = col; 606 while (curcol < win->col_offset + win->cols) 607 { 608 addch (' '); 609 curcol++; 610 } 611 move (row, col); 612 } 613} 614 615void mutt_window_clearline (mutt_window_t *win, int row) 616{ 617 mutt_window_move (win, row, 0); 618 mutt_window_clrtoeol (win); 619} 620 621/* Assumes the current position is inside the window. 622 * Otherwise it will happily return negative or values outside 623 * the window boundaries 624 */ 625void mutt_window_getyx (mutt_window_t *win, int *y, int *x) 626{ 627 int row, col; 628 629 getyx (stdscr, row, col); 630 if (y) 631 *y = row - win->row_offset; 632 if (x) 633 *x = col - win->col_offset; 634} 635 636 637void mutt_show_error (void) 638{ 639 if (option (OPTKEEPQUIET)) 640 return; 641 642 SETCOLOR (option (OPTMSGERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE); 643 mutt_window_mvaddstr (MuttMessageWindow, 0, 0, Errorbuf); 644 NORMAL_COLOR; 645 mutt_window_clrtoeol(MuttMessageWindow); 646} 647 648void mutt_endwin (const char *msg) 649{ 650 int e = errno; 651 652 if (!option (OPTNOCURSES)) 653 { 654 /* at least in some situations (screen + xterm under SuSE11/12) endwin() 655 * doesn't properly flush the screen without an explicit call. 656 */ 657 mutt_refresh(); 658 endwin (); 659 } 660 661 if (msg && *msg) 662 { 663 puts (msg); 664 fflush (stdout); 665 } 666 667 errno = e; 668} 669 670void mutt_perror (const char *s) 671{ 672 char *p = strerror (errno); 673 674 dprint (1, (debugfile, "%s: %s (errno = %d)\n", s, 675 p ? p : "unknown error", errno)); 676 mutt_error ("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno); 677} 678 679int mutt_any_key_to_continue (const char *s) 680{ 681 struct termios t; 682 struct termios old; 683 int f, ch; 684 685 f = open ("/dev/tty", O_RDONLY); 686 tcgetattr (f, &t); 687 memcpy ((void *)&old, (void *)&t, sizeof(struct termios)); /* save original state */ 688 t.c_lflag &= ~(ICANON | ECHO); 689 t.c_cc[VMIN] = 1; 690 t.c_cc[VTIME] = 0; 691 tcsetattr (f, TCSADRAIN, &t); 692 fflush (stdout); 693 if (s) 694 fputs (s, stdout); 695 else 696 fputs (_("Press any key to continue..."), stdout); 697 fflush (stdout); 698 ch = fgetc (stdin); 699 fflush (stdin); 700 tcsetattr (f, TCSADRAIN, &old); 701 close (f); 702 fputs ("\r\n", stdout); 703 mutt_clear_error (); 704 return (ch); 705} 706 707int mutt_do_pager (const char *banner, 708 const char *tempfile, 709 int do_color, 710 pager_t *info) 711{ 712 int rc; 713 714 if (!Pager || mutt_strcmp (Pager, "builtin") == 0) 715 rc = mutt_pager (banner, tempfile, do_color, info); 716 else 717 { 718 char cmd[STRING]; 719 720 mutt_endwin (NULL); 721 mutt_expand_file_fmt (cmd, sizeof(cmd), Pager, tempfile); 722 if (mutt_system (cmd) == -1) 723 { 724 mutt_error (_("Error running \"%s\"!"), cmd); 725 rc = -1; 726 } 727 else 728 rc = 0; 729 mutt_unlink (tempfile); 730 } 731 732 return rc; 733} 734 735int _mutt_enter_fname (const char *prompt, char *buf, size_t blen, int *redraw, int buffy, int multiple, char ***files, int *numfiles) 736{ 737 event_t ch; 738 739 SETCOLOR (MT_COLOR_PROMPT); 740 mutt_window_mvaddstr (MuttMessageWindow, 0, 0, (char *) prompt); 741 addstr (_(" ('?' for list): ")); 742 NORMAL_COLOR; 743 if (buf[0]) 744 addstr (buf); 745 mutt_window_clrtoeol (MuttMessageWindow); 746 mutt_refresh (); 747 748 ch = mutt_getch(); 749 if (ch.ch < 0) 750 { 751 mutt_window_clearline (MuttMessageWindow, 0); 752 return (-1); 753 } 754 else if (ch.ch == '?') 755 { 756 mutt_refresh (); 757 buf[0] = 0; 758 _mutt_select_file (buf, blen, MUTT_SEL_FOLDER | (multiple ? MUTT_SEL_MULTI : 0), 759 files, numfiles); 760 *redraw = REDRAW_FULL; 761 } 762 else 763 { 764 char *pc = safe_malloc (mutt_strlen (prompt) + 3); 765 766 sprintf (pc, "%s: ", prompt); /* __SPRINTF_CHECKED__ */ 767 mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0); 768 if (_mutt_get_field (pc, buf, blen, (buffy ? MUTT_EFILE : MUTT_FILE) | MUTT_CLEAR, multiple, files, numfiles) 769 != 0) 770 buf[0] = 0; 771 MAYBE_REDRAW (*redraw); 772 FREE (&pc); 773 } 774 775 return 0; 776} 777 778void mutt_unget_event (int ch, int op) 779{ 780 event_t tmp; 781 782 tmp.ch = ch; 783 tmp.op = op; 784 785 if (UngetCount >= UngetLen) 786 safe_realloc (&UngetKeyEvents, (UngetLen += 16) * sizeof(event_t)); 787 788 UngetKeyEvents[UngetCount++] = tmp; 789} 790 791void mutt_unget_string (char *s) 792{ 793 char *p = s + mutt_strlen (s) - 1; 794 795 while (p >= s) 796 { 797 mutt_unget_event ((unsigned char)*p--, 0); 798 } 799} 800 801/* 802 * Adds the ch/op to the macro buffer. 803 * This should be used for macros, push, and exec commands only. 804 */ 805void mutt_push_macro_event (int ch, int op) 806{ 807 event_t tmp; 808 809 tmp.ch = ch; 810 tmp.op = op; 811 812 if (MacroBufferCount >= MacroBufferLen) 813 safe_realloc (&MacroEvents, (MacroBufferLen += 128) * sizeof(event_t)); 814 815 MacroEvents[MacroBufferCount++] = tmp; 816} 817 818void mutt_flush_macro_to_endcond (void) 819{ 820 UngetCount = 0; 821 while (MacroBufferCount > 0) 822 { 823 if (MacroEvents[--MacroBufferCount].op == OP_END_COND) 824 return; 825 } 826} 827 828void mutt_flushinp (void) 829{ 830 UngetCount = 0; 831 MacroBufferCount = 0; 832 flushinp (); 833} 834 835#if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET)) 836/* The argument can take 3 values: 837 * -1: restore the value of the last call 838 * 0: make the cursor invisible 839 * 1: make the cursor visible 840 */ 841void mutt_curs_set (int cursor) 842{ 843 static int SavedCursor = 1; 844 845 if (cursor < 0) 846 cursor = SavedCursor; 847 else 848 SavedCursor = cursor; 849 850 if (curs_set (cursor) == ERR) { 851 if (cursor == 1) /* cnorm */ 852 curs_set (2); /* cvvis */ 853 } 854} 855#endif 856 857int mutt_multi_choice (char *prompt, char *letters) 858{ 859 event_t ch; 860 int choice; 861 char *p; 862 863 SETCOLOR (MT_COLOR_PROMPT); 864 mutt_window_mvaddstr (MuttMessageWindow, 0, 0, prompt); 865 NORMAL_COLOR; 866 mutt_window_clrtoeol (MuttMessageWindow); 867 FOREVER 868 { 869 mutt_refresh (); 870 ch = mutt_getch (); 871 /* (ch.ch == 0) is technically possible. Treat the same as < 0 (abort) */ 872 if (ch.ch <= 0 || CI_is_return (ch.ch)) 873 { 874 choice = -1; 875 break; 876 } 877 else 878 { 879 p = strchr (letters, ch.ch); 880 if (p) 881 { 882 choice = p - letters + 1; 883 break; 884 } 885 else if (ch.ch <= '9' && ch.ch > '0') 886 { 887 choice = ch.ch - '0'; 888 if (choice <= mutt_strlen (letters)) 889 break; 890 } 891 } 892 BEEP (); 893 } 894 mutt_window_clearline (MuttMessageWindow, 0); 895 mutt_refresh (); 896 return choice; 897} 898 899/* 900 * addwch would be provided by an up-to-date curses library 901 */ 902 903int mutt_addwch (wchar_t wc) 904{ 905 char buf[MB_LEN_MAX*2]; 906 mbstate_t mbstate; 907 size_t n1, n2; 908 909 memset (&mbstate, 0, sizeof (mbstate)); 910 if ((n1 = wcrtomb (buf, wc, &mbstate)) == (size_t)(-1) || 911 (n2 = wcrtomb (buf + n1, 0, &mbstate)) == (size_t)(-1)) 912 return -1; /* ERR */ 913 else 914 return addstr (buf); 915} 916 917 918/* 919 * This formats a string, a bit like 920 * snprintf (dest, destlen, "%-*.*s", min_width, max_width, s), 921 * except that the widths refer to the number of character cells 922 * when printed. 923 */ 924 925void mutt_format_string (char *dest, size_t destlen, 926 int min_width, int max_width, 927 int justify, char m_pad_char, 928 const char *s, size_t n, 929 int arboreal) 930{ 931 char *p; 932 wchar_t wc; 933 int w; 934 size_t k, k2; 935 char scratch[MB_LEN_MAX]; 936 mbstate_t mbstate1, mbstate2; 937 938 memset(&mbstate1, 0, sizeof (mbstate1)); 939 memset(&mbstate2, 0, sizeof (mbstate2)); 940 --destlen; 941 p = dest; 942 for (; n && (k = mbrtowc (&wc, s, n, &mbstate1)); s += k, n -= k) 943 { 944 if (k == (size_t)(-1) || k == (size_t)(-2)) 945 { 946 if (k == (size_t)(-1) && errno == EILSEQ) 947 memset (&mbstate1, 0, sizeof (mbstate1)); 948 949 k = (k == (size_t)(-1)) ? 1 : n; 950 wc = replacement_char (); 951 } 952 if (arboreal && wc < MUTT_TREE_MAX) 953 w = 1; /* hack */ 954 else 955 { 956#ifdef HAVE_ISWBLANK 957 if (iswblank (wc)) 958 wc = ' '; 959 else 960#endif 961 if (!IsWPrint (wc)) 962 wc = '?'; 963 w = wcwidth (wc); 964 } 965 if (w >= 0) 966 { 967 if (w > max_width || (k2 = wcrtomb (scratch, wc, &mbstate2)) > destlen) 968 break; 969 min_width -= w; 970 max_width -= w; 971 strncpy (p, scratch, k2); 972 p += k2; 973 destlen -= k2; 974 } 975 } 976 w = (int)destlen < min_width ? destlen : min_width; 977 if (w <= 0) 978 *p = '\0'; 979 else if (justify == FMT_RIGHT) /* right justify */ 980 { 981 p[w] = '\0'; 982 while (--p >= dest) 983 p[w] = *p; 984 while (--w >= 0) 985 dest[w] = m_pad_char; 986 } 987 else if (justify == FMT_CENTER) /* center */ 988 { 989 char *savedp = p; 990 int half = (w+1) / 2; /* half of cushion space */ 991 992 p[w] = '\0'; 993 994 /* move str to center of buffer */ 995 while (--p >= dest) 996 p[half] = *p; 997 998 /* fill rhs */ 999 p = savedp + half; 1000 while (--w >= half) 1001 *p++ = m_pad_char; 1002 1003 /* fill lhs */ 1004 while (half--) 1005 dest[half] = m_pad_char; 1006 } 1007 else /* left justify */ 1008 { 1009 while (--w >= 0) 1010 *p++ = m_pad_char; 1011 *p = '\0'; 1012 } 1013} 1014 1015/* 1016 * This formats a string rather like 1017 * snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 1018 * snprintf (dest, destlen, fmt, s); 1019 * except that the numbers in the conversion specification refer to 1020 * the number of character cells when printed. 1021 */ 1022 1023static void mutt_format_s_x (char *dest, 1024 size_t destlen, 1025 const char *prefix, 1026 const char *s, 1027 int arboreal) 1028{ 1029 int justify = FMT_RIGHT; 1030 char *p; 1031 int min_width; 1032 int max_width = INT_MAX; 1033 1034 if (*prefix == '-') 1035 ++prefix, justify = FMT_LEFT; 1036 else if (*prefix == '=') 1037 ++prefix, justify = FMT_CENTER; 1038 min_width = strtol (prefix, &p, 10); 1039 if (*p == '.') 1040 { 1041 prefix = p + 1; 1042 max_width = strtol (prefix, &p, 10); 1043 if (p <= prefix) 1044 max_width = INT_MAX; 1045 } 1046 1047 mutt_format_string (dest, destlen, min_width, max_width, 1048 justify, ' ', s, mutt_strlen (s), arboreal); 1049} 1050 1051void mutt_format_s (char *dest, 1052 size_t destlen, 1053 const char *prefix, 1054 const char *s) 1055{ 1056 mutt_format_s_x (dest, destlen, prefix, s, 0); 1057} 1058 1059void mutt_format_s_tree (char *dest, 1060 size_t destlen, 1061 const char *prefix, 1062 const char *s) 1063{ 1064 mutt_format_s_x (dest, destlen, prefix, s, 1); 1065} 1066 1067/* 1068 * mutt_paddstr (n, s) is almost equivalent to 1069 * mutt_format_string (bigbuf, big, n, n, FMT_LEFT, ' ', s, big, 0), addstr (bigbuf) 1070 */ 1071 1072void mutt_paddstr (int n, const char *s) 1073{ 1074 wchar_t wc; 1075 int w; 1076 size_t k; 1077 size_t len = mutt_strlen (s); 1078 mbstate_t mbstate; 1079 1080 memset (&mbstate, 0, sizeof (mbstate)); 1081 for (; len && (k = mbrtowc (&wc, s, len, &mbstate)); s += k, len -= k) 1082 { 1083 if (k == (size_t)(-1) || k == (size_t)(-2)) 1084 { 1085 if (k == (size_t) (-1)) 1086 memset (&mbstate, 0, sizeof (mbstate)); 1087 k = (k == (size_t)(-1)) ? 1 : len; 1088 wc = replacement_char (); 1089 } 1090 if (!IsWPrint (wc)) 1091 wc = '?'; 1092 w = wcwidth (wc); 1093 if (w >= 0) 1094 { 1095 if (w > n) 1096 break; 1097 addnstr ((char *)s, k); 1098 n -= w; 1099 } 1100 } 1101 while (n-- > 0) 1102 addch (' '); 1103} 1104 1105/* See how many bytes to copy from string so its at most maxlen bytes 1106 * long and maxwid columns wide */ 1107size_t mutt_wstr_trunc (const char *src, size_t maxlen, size_t maxwid, size_t *width) 1108{ 1109 wchar_t wc; 1110 size_t n, w = 0, l = 0, cl; 1111 int cw; 1112 mbstate_t mbstate; 1113 1114 if (!src) 1115 goto out; 1116 1117 n = mutt_strlen (src); 1118 1119 memset (&mbstate, 0, sizeof (mbstate)); 1120 for (w = 0; n && (cl = mbrtowc (&wc, src, n, &mbstate)); src += cl, n -= cl) 1121 { 1122 if (cl == (size_t)(-1) || cl == (size_t)(-2)) 1123 { 1124 if (cl == (size_t)(-1)) 1125 memset (&mbstate, 0, sizeof (mbstate)); 1126 cl = (cl == (size_t)(-1)) ? 1 : n; 1127 wc = replacement_char (); 1128 } 1129 cw = wcwidth (wc); 1130 /* hack because MUTT_TREE symbols aren't turned into characters 1131 * until rendered by print_enriched_string (#3364) */ 1132 if (cw < 0 && cl == 1 && src[0] && src[0] < MUTT_TREE_MAX) 1133 cw = 1; 1134 else if (cw < 0) 1135 cw = 0; /* unprintable wchar */ 1136 if (cl + l > maxlen || cw + w > maxwid) 1137 break; 1138 l += cl; 1139 w += cw; 1140 } 1141out: 1142 if (width) 1143 *width = w; 1144 return l; 1145} 1146 1147/* 1148 * returns the number of bytes the first (multibyte) character 1149 * of input consumes: 1150 * < 0 ... conversion error 1151 * = 0 ... end of input 1152 * > 0 ... length (bytes) 1153 */ 1154int mutt_charlen (const char *s, int *width) 1155{ 1156 wchar_t wc; 1157 mbstate_t mbstate; 1158 size_t k, n; 1159 1160 if (!s || !*s) 1161 return 0; 1162 1163 n = mutt_strlen (s); 1164 memset (&mbstate, 0, sizeof (mbstate)); 1165 k = mbrtowc (&wc, s, n, &mbstate); 1166 if (width) 1167 *width = wcwidth (wc); 1168 return (k == (size_t)(-1) || k == (size_t)(-2)) ? -1 : k; 1169} 1170 1171/* 1172 * mutt_strwidth is like mutt_strlen except that it returns the width 1173 * referring to the number of character cells. 1174 */ 1175 1176int mutt_strwidth (const char *s) 1177{ 1178 wchar_t wc; 1179 int w; 1180 size_t k, n; 1181 mbstate_t mbstate; 1182 1183 if (!s) return 0; 1184 1185 n = mutt_strlen (s); 1186 1187 memset (&mbstate, 0, sizeof (mbstate)); 1188 for (w=0; n && (k = mbrtowc (&wc, s, n, &mbstate)); s += k, n -= k) 1189 { 1190 if (k == (size_t)(-1) || k == (size_t)(-2)) 1191 { 1192 if (k == (size_t)(-1)) 1193 memset (&mbstate, 0, sizeof (mbstate)); 1194 k = (k == (size_t)(-1)) ? 1 : n; 1195 wc = replacement_char (); 1196 } 1197 if (!IsWPrint (wc)) 1198 wc = '?'; 1199 w += wcwidth (wc); 1200 } 1201 return w; 1202}