NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
curs_lib.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <stddef.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <langinfo.h>
36 #include <limits.h>
37 #include <regex.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <termios.h>
43 #include <unistd.h>
44 #include <wchar.h>
45 #include "mutt/mutt.h"
46 #include "config/lib.h"
47 #include "mutt.h"
48 #include "curs_lib.h"
49 #include "browser.h"
50 #include "enter_state.h"
51 #include "globals.h"
52 #include "mutt_curses.h"
53 #include "mutt_logging.h"
54 #include "mutt_menu.h"
55 #include "mutt_window.h"
56 #include "opcodes.h"
57 #include "options.h"
58 #include "pager.h"
59 #include "protos.h"
60 #ifdef HAVE_ISWBLANK
61 #include <wctype.h>
62 #endif
63 #ifdef USE_INOTIFY
64 #include "monitor.h"
65 #endif
66 
67 /* These Config Variables are only used in curs_lib.c */
68 bool C_MetaKey;
69 
70 /* not possible to unget more than one char under some curses libs, and it
71  * is impossible to unget function keys in SLang, so roll our own input
72  * buffering routines.
73  */
74 
75 /* These are used for macros and exec/push commands.
76  * They can be temporarily ignored by setting OptIgnoreMacroEvents
77  */
78 static size_t MacroBufferCount = 0;
79 static size_t MacroBufferLen = 0;
80 static struct KeyEvent *MacroEvents;
81 
82 /* These are used in all other "normal" situations, and are not
83  * ignored when setting OptIgnoreMacroEvents
84  */
85 static size_t UngetCount = 0;
86 static size_t UngetLen = 0;
87 static struct KeyEvent *UngetKeyEvents;
88 
90 
94 void mutt_refresh(void)
95 {
96  /* don't refresh when we are waiting for a child. */
97  if (OptKeepQuiet)
98  return;
99 
100  /* don't refresh in the middle of macros unless necessary */
102  return;
103 
104  /* else */
105  refresh();
106 }
107 
117 {
118  keypad(stdscr, true);
119  clearok(stdscr, true);
121 }
122 
132 void mutt_getch_timeout(int delay)
133 {
134  MuttGetchTimeout = delay;
135  timeout(delay);
136 }
137 
138 #ifdef USE_INOTIFY
139 
144 static int mutt_monitor_getch(void)
145 {
146  /* ncurses has its own internal buffer, so before we perform a poll,
147  * we need to make sure there isn't a character waiting */
148  timeout(0);
149  int ch = getch();
150  timeout(MuttGetchTimeout);
151  if (ch == ERR)
152  {
153  if (mutt_monitor_poll() != 0)
154  ch = ERR;
155  else
156  ch = getch();
157  }
158  return ch;
159 }
160 #endif /* USE_INOTIFY */
161 
175 struct KeyEvent mutt_getch(void)
176 {
177  int ch;
178  struct KeyEvent err = { -1, OP_NULL }, ret;
179  struct KeyEvent timeout = { -2, OP_NULL };
180 
181  if (UngetCount)
182  return UngetKeyEvents[--UngetCount];
183 
185  return MacroEvents[--MacroBufferCount];
186 
187  SigInt = 0;
188 
190 #ifdef KEY_RESIZE
191  /* ncurses 4.2 sends this when the screen is resized */
192  ch = KEY_RESIZE;
193  while (ch == KEY_RESIZE)
194 #endif /* KEY_RESIZE */
195 #ifdef USE_INOTIFY
196  ch = mutt_monitor_getch();
197 #else
198  ch = getch();
199 #endif /* USE_INOTIFY */
201 
202  if (SigInt)
203  {
204  mutt_query_exit();
205  return err;
206  }
207 
208  /* either timeout, a sigwinch (if timeout is set), or the terminal
209  * has been lost */
210  if (ch == ERR)
211  {
212  if (!isatty(0))
213  mutt_exit(1);
214 
215  return timeout;
216  }
217 
218  if ((ch & 0x80) && C_MetaKey)
219  {
220  /* send ALT-x as ESC-x */
221  ch &= ~0x80;
222  mutt_unget_event(ch, 0);
223  ret.ch = '\033'; // Escape
224  ret.op = 0;
225  return ret;
226  }
227 
228  ret.ch = ch;
229  ret.op = 0;
230  return (ch == ctrl('G')) ? err : ret;
231 }
232 
246 int mutt_get_field_full(const char *field, char *buf, size_t buflen, CompletionFlags complete,
247  bool multiple, char ***files, int *numfiles)
248 {
249  int ret;
250  int x;
251 
252  struct EnterState *es = mutt_enter_state_new();
253 
254  do
255  {
256  if (SigWinch)
257  {
258  SigWinch = 0;
260  clearok(stdscr, true);
262  }
265  addstr(field);
266  NORMAL_COLOR;
267  mutt_refresh();
269  ret = mutt_enter_string_full(buf, buflen, x, complete, multiple, files, numfiles, es);
270  } while (ret == 1);
273 
274  return ret;
275 }
276 
287 int mutt_get_field_unbuffered(const char *msg, char *buf, size_t buflen, CompletionFlags flags)
288 {
289  bool reset_ignoremacro = false;
290 
292  {
293  OptIgnoreMacroEvents = true;
294  reset_ignoremacro = true;
295  }
296  int rc = mutt_get_field(msg, buf, buflen, flags);
297  if (reset_ignoremacro)
298  OptIgnoreMacroEvents = false;
299 
300  return rc;
301 }
302 
308 void mutt_edit_file(const char *editor, const char *file)
309 {
310  struct Buffer *cmd = mutt_buffer_pool_get();
311 
312  mutt_endwin();
313  mutt_buffer_file_expand_fmt_quote(cmd, editor, file);
314  if (mutt_system(mutt_b2s(cmd)) != 0)
315  {
316  mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
317  }
318  /* the terminal may have been resized while the editor owned it */
320  keypad(stdscr, true);
321  clearok(stdscr, true);
322 
324 }
325 
332 enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
333 {
334  struct KeyEvent ch;
335  char *yes = _("yes");
336  char *no = _("no");
337  char *answer_string = NULL;
338  int answer_string_wid, msg_wid;
339  size_t trunc_msg_len;
340  bool redraw = true;
341  int prompt_lines = 1;
342 
343  char *expr = NULL;
344  regex_t reyes;
345  regex_t reno;
346  char answer[2];
347 
348  answer[1] = '\0';
349 
350  bool reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
351  (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
352  bool reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
353  (REG_COMP(&reno, expr, REG_NOSUB) == 0);
354 
355  /* In order to prevent the default answer to the question to wrapped
356  * around the screen in the even the question is wider than the screen,
357  * ensure there is enough room for the answer and truncate the question
358  * to fit. */
359  mutt_str_asprintf(&answer_string, " ([%s]/%s): ", (def == MUTT_YES) ? yes : no,
360  (def == MUTT_YES) ? no : yes);
361  answer_string_wid = mutt_strwidth(answer_string);
362  msg_wid = mutt_strwidth(msg);
363 
364  while (true)
365  {
366  if (redraw || SigWinch)
367  {
368  redraw = false;
369  if (SigWinch)
370  {
371  SigWinch = 0;
373  clearok(stdscr, true);
375  }
376  if (MuttMessageWindow->cols)
377  {
378  prompt_lines = (msg_wid + answer_string_wid + MuttMessageWindow->cols - 1) /
380  prompt_lines = MAX(1, MIN(3, prompt_lines));
381  }
382  if (prompt_lines != MuttMessageWindow->rows)
383  {
384  mutt_window_reflow_message_rows(prompt_lines);
386  }
387 
388  /* maxlen here is sort of arbitrary, so pick a reasonable upper bound */
389  trunc_msg_len = mutt_wstr_trunc(
390  msg, 4 * prompt_lines * MuttMessageWindow->cols,
391  prompt_lines * MuttMessageWindow->cols - answer_string_wid, NULL);
392 
395  addnstr(msg, trunc_msg_len);
396  addstr(answer_string);
397  NORMAL_COLOR;
399  }
400 
401  mutt_refresh();
402  /* SigWinch is not processed unless timeout is set */
403  mutt_getch_timeout(30 * 1000);
404  ch = mutt_getch();
405  mutt_getch_timeout(-1);
406  if (ch.ch == -2)
407  continue;
408  if (CI_is_return(ch.ch))
409  break;
410  if (ch.ch < 0)
411  {
412  def = MUTT_ABORT;
413  break;
414  }
415 
416  answer[0] = ch.ch;
417  if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'y'))
418  {
419  def = MUTT_YES;
420  break;
421  }
422  else if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'n'))
423  {
424  def = MUTT_NO;
425  break;
426  }
427  else
428  {
429  BEEP();
430  }
431  }
432 
433  FREE(&answer_string);
434 
435  if (reyes_ok)
436  regfree(&reyes);
437  if (reno_ok)
438  regfree(&reno);
439 
440  if (MuttMessageWindow->rows != 1)
441  {
444  }
445  else
447 
448  if (def != MUTT_ABORT)
449  {
450  addstr((char *) ((def == MUTT_YES) ? yes : no));
451  mutt_refresh();
452  }
453  else
454  {
455  /* when the users cancels with ^G, clear the message stored with
456  * mutt_message() so it isn't displayed when the screen is refreshed. */
458  }
459  return def;
460 }
461 
467 void mutt_query_exit(void)
468 {
469  mutt_flushinp();
470  curs_set(1);
471  if (C_Timeout)
472  mutt_getch_timeout(-1); /* restore blocking operation */
473  if (mutt_yesorno(_("Exit NeoMutt?"), MUTT_YES) == MUTT_YES)
474  {
475  mutt_exit(1);
476  }
478  mutt_curs_set(-1);
479  SigInt = 0;
480 }
481 
485 void mutt_show_error(void)
486 {
488  return;
489 
492  NORMAL_COLOR;
494 }
495 
499 void mutt_endwin(void)
500 {
501  if (OptNoCurses)
502  return;
503 
504  int e = errno;
505 
506  /* at least in some situations (screen + xterm under SuSE11/12) endwin()
507  * doesn't properly flush the screen without an explicit call. */
508  mutt_refresh();
509  endwin();
510 
511  errno = e;
512 }
513 
518 void mutt_perror_debug(const char *s)
519 {
520  char *p = strerror(errno);
521 
522  mutt_debug(LL_DEBUG1, "%s: %s (errno = %d)\n", s, p ? p : "unknown error", errno);
523  mutt_error("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
524 }
525 
532 int mutt_any_key_to_continue(const char *s)
533 {
534  struct termios term;
535  struct termios old;
536 
537  int fd = open("/dev/tty", O_RDONLY);
538  if (fd < 0)
539  return EOF;
540 
541  tcgetattr(fd, &old); // Save the current tty settings
542 
543  term = old;
544  term.c_lflag &= ~(ICANON | ECHO); // Canonical (not line-buffered), don't echo the characters
545  term.c_cc[VMIN] = 1; // Wait for at least one character
546  term.c_cc[VTIME] = 255; // Wait for 25.5s
547  tcsetattr(fd, TCSANOW, &term);
548 
549  if (s)
550  fputs(s, stdout);
551  else
552  fputs(_("Press any key to continue..."), stdout);
553  fflush(stdout);
554 
555  char ch = '\0';
556  // Wait for a character. This might timeout, so loop.
557  while (read(fd, &ch, 1) == 0)
558  ;
559 
560  // Change the tty settings to be non-blocking
561  term.c_cc[VMIN] = 0; // Returning with zero characters is acceptable
562  term.c_cc[VTIME] = 0; // Don't wait
563  tcsetattr(fd, TCSANOW, &term);
564 
565  char buf[64];
566  while (read(fd, buf, sizeof(buf)) > 0) // Mop up any remaining chars
567  ;
568 
569  tcsetattr(fd, TCSANOW, &old); // Restore the previous tty settings
570  close(fd);
571 
572  fputs("\r\n", stdout);
574  return (ch >= 0) ? ch : EOF;
575 }
576 
586 int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color,
587  struct Pager *info)
588 {
589  int rc;
590 
591  if (!C_Pager || (mutt_str_strcmp(C_Pager, "builtin") == 0))
592  rc = mutt_pager(banner, tempfile, do_color, info);
593  else
594  {
595  struct Buffer *cmd = mutt_buffer_pool_get();
596 
597  mutt_endwin();
599  if (mutt_system(mutt_b2s(cmd)) == -1)
600  {
601  mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
602  rc = -1;
603  }
604  else
605  rc = 0;
606  mutt_file_unlink(tempfile);
608  }
609 
610  return rc;
611 }
612 
626 int mutt_enter_fname_full(const char *prompt, char *buf, size_t buflen, bool mailbox,
627  bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
628 {
629  struct Buffer *fname = mutt_buffer_pool_get();
630 
631  mutt_buffer_addstr(fname, NONULL(buf));
632  int rc = mutt_buffer_enter_fname_full(prompt, fname, mailbox, multiple, files,
633  numfiles, flags);
634  mutt_str_strfcpy(buf, mutt_b2s(fname), buflen);
635 
636  mutt_buffer_pool_release(&fname);
637  return rc;
638 }
639 
652 int mutt_buffer_enter_fname_full(const char *prompt, struct Buffer *fname,
653  bool mailbox, bool multiple, char ***files,
654  int *numfiles, SelectFileFlags flags)
655 {
656  struct KeyEvent ch;
657 
660  addstr(_(" ('?' for list): "));
661  NORMAL_COLOR;
662  if (!mutt_buffer_is_empty(fname))
663  addstr(mutt_b2s(fname));
665  mutt_refresh();
666 
667  do
668  {
669  ch = mutt_getch();
670  } while (ch.ch == -2);
671  if (ch.ch < 0)
672  {
674  return -1;
675  }
676  else if (ch.ch == '?')
677  {
678  mutt_refresh();
679  mutt_buffer_reset(fname);
680 
681  if (flags == MUTT_SEL_NO_FLAGS)
682  flags = MUTT_SEL_FOLDER;
683  if (multiple)
684  flags |= MUTT_SEL_MULTI;
685  if (mailbox)
686  flags |= MUTT_SEL_MAILBOX;
687  mutt_buffer_select_file(fname, flags, files, numfiles);
688  }
689  else
690  {
691  char *pc = mutt_mem_malloc(mutt_str_strlen(prompt) + 3);
692 
693  sprintf(pc, "%s: ", prompt);
694  if (ch.op == OP_NULL)
695  mutt_unget_event(ch.ch, 0);
696  else
697  mutt_unget_event(0, ch.op);
698 
699  mutt_buffer_alloc(fname, 1024);
700  if (mutt_get_field_full(pc, fname->data, fname->dsize,
701  (mailbox ? MUTT_EFILE : MUTT_FILE) | MUTT_CLEAR,
702  multiple, files, numfiles) != 0)
703  mutt_buffer_reset(fname);
704  else
705  mutt_buffer_fix_dptr(fname);
706  FREE(&pc);
707  }
708 
709  return 0;
710 }
711 
719 void mutt_unget_event(int ch, int op)
720 {
721  struct KeyEvent tmp;
722 
723  tmp.ch = ch;
724  tmp.op = op;
725 
726  if (UngetCount >= UngetLen)
727  mutt_mem_realloc(&UngetKeyEvents, (UngetLen += 16) * sizeof(struct KeyEvent));
728 
729  UngetKeyEvents[UngetCount++] = tmp;
730 }
731 
738 void mutt_unget_string(const char *s)
739 {
740  const char *p = s + mutt_str_strlen(s) - 1;
741 
742  while (p >= s)
743  {
744  mutt_unget_event((unsigned char) *p--, 0);
745  }
746 }
747 
757 {
758  struct KeyEvent tmp;
759 
760  tmp.ch = ch;
761  tmp.op = op;
762 
764  mutt_mem_realloc(&MacroEvents, (MacroBufferLen += 128) * sizeof(struct KeyEvent));
765 
766  MacroEvents[MacroBufferCount++] = tmp;
767 }
768 
776 {
777  UngetCount = 0;
778  while (MacroBufferCount > 0)
779  {
780  if (MacroEvents[--MacroBufferCount].op == OP_END_COND)
781  return;
782  }
783 }
784 
793 {
794  while (UngetCount > 0)
795  {
796  if (UngetKeyEvents[--UngetCount].op == OP_END_COND)
797  return;
798  }
799 }
800 
804 void mutt_flushinp(void)
805 {
806  UngetCount = 0;
807  MacroBufferCount = 0;
808  flushinp();
809 }
810 
811 #if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
812 
819 void mutt_curs_set(int cursor)
820 {
821  static int SavedCursor = 1;
822 
823  if (cursor < 0)
824  cursor = SavedCursor;
825  else
826  SavedCursor = cursor;
827 
828  if (curs_set(cursor) == ERR)
829  {
830  if (cursor == 1) /* cnorm */
831  curs_set(2); /* cvvis */
832  }
833 }
834 #endif
835 
843 int mutt_multi_choice(const char *prompt, const char *letters)
844 {
845  struct KeyEvent ch;
846  int choice;
847  bool redraw = true;
848  int prompt_lines = 1;
849 
850  bool opt_cols = ((ColorDefs[MT_COLOR_OPTIONS] != 0) &&
852 
853  while (true)
854  {
855  if (redraw || SigWinch)
856  {
857  redraw = false;
858  if (SigWinch)
859  {
860  SigWinch = 0;
862  clearok(stdscr, true);
864  }
865  if (MuttMessageWindow->cols)
866  {
867  int width = mutt_strwidth(prompt) + 2; // + '?' + space
868  /* If we're going to colour the options,
869  * make an assumption about the modified prompt size. */
870  if (opt_cols)
871  width -= 2 * mutt_str_strlen(letters);
872 
873  prompt_lines = (width + MuttMessageWindow->cols - 1) / MuttMessageWindow->cols;
874  prompt_lines = MAX(1, MIN(3, prompt_lines));
875  }
876  if (prompt_lines != MuttMessageWindow->rows)
877  {
878  mutt_window_reflow_message_rows(prompt_lines);
880  }
881 
883 
884  if ((ColorDefs[MT_COLOR_OPTIONS] != 0) &&
885  (ColorDefs[MT_COLOR_OPTIONS] != ColorDefs[MT_COLOR_PROMPT]))
886  {
887  char *cur = NULL;
888 
889  while ((cur = strchr(prompt, '(')))
890  {
891  // write the part between prompt and cur using MT_COLOR_PROMPT
892  SET_COLOR(MT_COLOR_PROMPT);
893  addnstr(prompt, cur - prompt);
894 
895  if (isalnum(cur[1]) && (cur[2] == ')'))
896  {
897  // we have a single letter within parentheses
899  addch(cur[1]);
900  prompt = cur + 3;
901  }
902  else
903  {
904  // we have a parenthesis followed by something else
905  addch(cur[0]);
906  prompt = cur + 1;
907  }
908  }
909  }
910 
911  SET_COLOR(MT_COLOR_PROMPT);
912  addstr(prompt);
913  NORMAL_COLOR;
914 
915  addch(' ');
917  }
918 
919  mutt_refresh();
920  /* SigWinch is not processed unless timeout is set */
921  mutt_getch_timeout(30 * 1000);
922  ch = mutt_getch();
923  mutt_getch_timeout(-1);
924  if (ch.ch == -2)
925  continue;
926  /* (ch.ch == 0) is technically possible. Treat the same as < 0 (abort) */
927  if ((ch.ch <= 0) || CI_is_return(ch.ch))
928  {
929  choice = -1;
930  break;
931  }
932  else
933  {
934  char *p = strchr(letters, ch.ch);
935  if (p)
936  {
937  choice = p - letters + 1;
938  break;
939  }
940  else if ((ch.ch <= '9') && (ch.ch > '0'))
941  {
942  choice = ch.ch - '0';
943  if (choice <= mutt_str_strlen(letters))
944  break;
945  }
946  }
947  BEEP();
948  }
949  if (MuttMessageWindow->rows != 1)
950  {
953  }
954  else
956  mutt_refresh();
957  return choice;
958 }
959 
966 int mutt_addwch(wchar_t wc)
967 {
968  char buf[MB_LEN_MAX * 2];
969  mbstate_t mbstate;
970  size_t n1, n2;
971 
972  memset(&mbstate, 0, sizeof(mbstate));
973  if (((n1 = wcrtomb(buf, wc, &mbstate)) == (size_t)(-1)) ||
974  ((n2 = wcrtomb(buf + n1, 0, &mbstate)) == (size_t)(-1)))
975  {
976  return -1; /* ERR */
977  }
978  else
979  {
980  return addstr(buf);
981  }
982 }
983 
1000 void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width,
1001  enum FormatJustify justify, char pad_char,
1002  const char *s, size_t n, bool arboreal)
1003 {
1004  wchar_t wc;
1005  int w;
1006  size_t k, k2;
1007  char scratch[MB_LEN_MAX];
1008  mbstate_t mbstate1, mbstate2;
1009  bool escaped = false;
1010 
1011  memset(&mbstate1, 0, sizeof(mbstate1));
1012  memset(&mbstate2, 0, sizeof(mbstate2));
1013  buflen--;
1014  char *p = buf;
1015  for (; n && (k = mbrtowc(&wc, s, n, &mbstate1)); s += k, n -= k)
1016  {
1017  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
1018  {
1019  if ((k == (size_t)(-1)) && (errno == EILSEQ))
1020  memset(&mbstate1, 0, sizeof(mbstate1));
1021 
1022  k = (k == (size_t)(-1)) ? 1 : n;
1023  wc = ReplacementChar;
1024  }
1025  if (escaped)
1026  {
1027  escaped = false;
1028  w = 0;
1029  }
1030  else if (arboreal && (wc == MUTT_SPECIAL_INDEX))
1031  {
1032  escaped = true;
1033  w = 0;
1034  }
1035  else if (arboreal && (wc < MUTT_TREE_MAX))
1036  {
1037  w = 1; /* hack */
1038  }
1039  else
1040  {
1041 #ifdef HAVE_ISWBLANK
1042  if (iswblank(wc))
1043  wc = ' ';
1044  else
1045 #endif
1046  if (!IsWPrint(wc))
1047  wc = '?';
1048  w = wcwidth(wc);
1049  }
1050  if (w >= 0)
1051  {
1052  if ((w > max_width) || ((k2 = wcrtomb(scratch, wc, &mbstate2)) > buflen))
1053  continue;
1054  min_width -= w;
1055  max_width -= w;
1056  strncpy(p, scratch, k2);
1057  p += k2;
1058  buflen -= k2;
1059  }
1060  }
1061  w = ((int) buflen < min_width) ? buflen : min_width;
1062  if (w <= 0)
1063  *p = '\0';
1064  else if (justify == JUSTIFY_RIGHT) /* right justify */
1065  {
1066  p[w] = '\0';
1067  while (--p >= buf)
1068  p[w] = *p;
1069  while (--w >= 0)
1070  buf[w] = pad_char;
1071  }
1072  else if (justify == JUSTIFY_CENTER) /* center */
1073  {
1074  char *savedp = p;
1075  int half = (w + 1) / 2; /* half of cushion space */
1076 
1077  p[w] = '\0';
1078 
1079  /* move str to center of buffer */
1080  while (--p >= buf)
1081  p[half] = *p;
1082 
1083  /* fill rhs */
1084  p = savedp + half;
1085  while (--w >= half)
1086  *p++ = pad_char;
1087 
1088  /* fill lhs */
1089  while (half--)
1090  buf[half] = pad_char;
1091  }
1092  else /* left justify */
1093  {
1094  while (--w >= 0)
1095  *p++ = pad_char;
1096  *p = '\0';
1097  }
1098 }
1099 
1114 void mutt_format_s_x(char *buf, size_t buflen, const char *prec, const char *s, bool arboreal)
1115 {
1116  enum FormatJustify justify = JUSTIFY_RIGHT;
1117  char *p = NULL;
1118  int min_width;
1119  int max_width = INT_MAX;
1120 
1121  if (*prec == '-')
1122  {
1123  prec++;
1124  justify = JUSTIFY_LEFT;
1125  }
1126  else if (*prec == '=')
1127  {
1128  prec++;
1129  justify = JUSTIFY_CENTER;
1130  }
1131  min_width = strtol(prec, &p, 10);
1132  if (*p == '.')
1133  {
1134  prec = p + 1;
1135  max_width = strtol(prec, &p, 10);
1136  if (p <= prec)
1137  max_width = INT_MAX;
1138  }
1139 
1140  mutt_simple_format(buf, buflen, min_width, max_width, justify, ' ', s,
1141  mutt_str_strlen(s), arboreal);
1142 }
1143 
1151 void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
1152 {
1153  mutt_format_s_x(buf, buflen, prec, s, false);
1154 }
1155 
1163 void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s)
1164 {
1165  mutt_format_s_x(buf, buflen, prec, s, true);
1166 }
1167 
1173 void mutt_paddstr(int n, const char *s)
1174 {
1175  wchar_t wc;
1176  size_t k;
1177  size_t len = mutt_str_strlen(s);
1178  mbstate_t mbstate;
1179 
1180  memset(&mbstate, 0, sizeof(mbstate));
1181  for (; len && (k = mbrtowc(&wc, s, len, &mbstate)); s += k, len -= k)
1182  {
1183  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
1184  {
1185  if (k == (size_t)(-1))
1186  memset(&mbstate, 0, sizeof(mbstate));
1187  k = (k == (size_t)(-1)) ? 1 : len;
1188  wc = ReplacementChar;
1189  }
1190  if (!IsWPrint(wc))
1191  wc = '?';
1192  const int w = wcwidth(wc);
1193  if (w >= 0)
1194  {
1195  if (w > n)
1196  break;
1197  addnstr((char *) s, k);
1198  n -= w;
1199  }
1200  }
1201  while (n-- > 0)
1202  addch(' ');
1203 }
1204 
1216 size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
1217 {
1218  wchar_t wc;
1219  size_t n, w = 0, l = 0, cl;
1220  int cw;
1221  mbstate_t mbstate;
1222 
1223  if (!src)
1224  goto out;
1225 
1226  n = mutt_str_strlen(src);
1227 
1228  memset(&mbstate, 0, sizeof(mbstate));
1229  for (w = 0; n && (cl = mbrtowc(&wc, src, n, &mbstate)); src += cl, n -= cl)
1230  {
1231  if ((cl == (size_t)(-1)) || (cl == (size_t)(-2)))
1232  {
1233  if (cl == (size_t)(-1))
1234  memset(&mbstate, 0, sizeof(mbstate));
1235  cl = (cl == (size_t)(-1)) ? 1 : n;
1236  wc = ReplacementChar;
1237  }
1238  cw = wcwidth(wc);
1239  /* hack because MUTT_TREE symbols aren't turned into characters
1240  * until rendered by print_enriched_string() */
1241  if ((cw < 0) && (src[0] == MUTT_SPECIAL_INDEX))
1242  {
1243  cl = 2; /* skip the index coloring sequence */
1244  cw = 0;
1245  }
1246  else if ((cw < 0) && (cl == 1) && (src[0] != '\0') && (src[0] < MUTT_TREE_MAX))
1247  cw = 1;
1248  else if (cw < 0)
1249  cw = 0; /* unprintable wchar */
1250  if ((cl + l > maxlen) || (cw + w > maxwid))
1251  break;
1252  l += cl;
1253  w += cw;
1254  }
1255 out:
1256  if (width)
1257  *width = w;
1258  return l;
1259 }
1260 
1266 int mutt_strwidth(const char *s)
1267 {
1268  if (!s)
1269  return 0;
1270 
1271  wchar_t wc;
1272  int w;
1273  size_t k;
1274  mbstate_t mbstate;
1275 
1276  size_t n = mutt_str_strlen(s);
1277 
1278  memset(&mbstate, 0, sizeof(mbstate));
1279  for (w = 0; n && (k = mbrtowc(&wc, s, n, &mbstate)); s += k, n -= k)
1280  {
1281  if (*s == MUTT_SPECIAL_INDEX)
1282  {
1283  s += 2; /* skip the index coloring sequence */
1284  k = 0;
1285  continue;
1286  }
1287 
1288  if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
1289  {
1290  if (k == (size_t)(-1))
1291  memset(&mbstate, 0, sizeof(mbstate));
1292  k = (k == (size_t)(-1)) ? 1 : n;
1293  wc = ReplacementChar;
1294  }
1295  if (!IsWPrint(wc))
1296  wc = '?';
1297  w += wcwidth(wc);
1298  }
1299  return w;
1300 }
void mutt_perror_debug(const char *s)
Show the user an &#39;errno&#39; message.
Definition: curs_lib.c:518
#define CI_is_return(ch)
Definition: mutt_curses.h:102
#define NONULL(x)
Definition: string2.h:37
Define wrapper functions around Curses/Slang.
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
Options in prompt.
Definition: mutt_curses.h:173
Left justify the text.
Definition: curs_lib.h:46
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:70
FormatJustify
Alignment for mutt_simple_format()
Definition: curs_lib.h:44
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:100
#define MIN(a, b)
Definition: memory.h:31
#define IsWPrint(wc)
Definition: mbyte.h:39
Window management.
GUI miscellaneous curses (window drawing) routines.
uint16_t CompletionFlags
Flags for mutt_enter_string_full(), e.g. MUTT_ALIAS.
Definition: mutt.h:63
void mutt_buffer_select_file(struct Buffer *file, SelectFileFlags flags, char ***files, int *numfiles)
Let the user select a file.
Definition: browser.c:1148
static struct KeyEvent * MacroEvents
Definition: curs_lib.c:80
#define curs_set(x)
Definition: mutt_curses.h:88
User aborted the question (with Ctrl-G)
Definition: quad.h:37
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
void mutt_push_macro_event(int ch, int op)
Add the character/operation to the macro buffer.
Definition: curs_lib.c:756
static size_t MacroBufferCount
Definition: curs_lib.c:78
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.h:84
#define NORMAL_COLOR
Definition: mutt_curses.h:239
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:804
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:107
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:98
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:111
Right justify the text.
Definition: curs_lib.h:48
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:192
String manipulation buffer.
Definition: buffer.h:33
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:719
#define _(a)
Definition: message.h:28
Question/user input.
Definition: mutt_curses.h:143
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:46
int mutt_enter_string_full(char *buf, size_t buflen, int col, CompletionFlags flags, bool multiple, char ***files, int *numfiles, struct EnterState *state)
Ask the user for a string.
Definition: enter.c:178
int op
function op
Definition: mutt_curses.h:111
int ch
raw key pressed
Definition: mutt_curses.h:110
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: browser.h:44
WHERE bool OptIgnoreMacroEvents
(pseudo) don&#39;t process macro/push/exec events while set
Definition: options.h:36
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
void mutt_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:467
An email being displayed.
Definition: pager.h:65
All user-callable functions.
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:86
Convenience wrapper for the config headers.
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1173
GUI component for displaying/selecting items from a list.
#define MUTT_SEL_FOLDER
Select a local directory.
Definition: browser.h:47
Hundreds of global variables to back the user variables.
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:59
static size_t UngetCount
Definition: curs_lib.c:85
#define MAX(a, b)
Definition: memory.h:30
size_t dsize
Length of data.
Definition: buffer.h:37
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:332
int mutt_enter_fname_full(const char *prompt, char *buf, size_t buflen, bool mailbox, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file.
Definition: curs_lib.c:626
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: curs_lib.c:775
Centre the text.
Definition: curs_lib.h:47
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:95
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:52
#define ctrl(ch)
Definition: mutt_curses.h:97
WHERE SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: globals.h:85
Struct to store the cursor position when entering text.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:109
int mutt_monitor_poll(void)
Check for filesystem changes.
Definition: monitor.c:381
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
void mutt_window_getxy(struct MuttWindow *win, int *x, int *y)
Get the cursor position in the Window.
Definition: mutt_window.c:152
int mutt_buffer_enter_fname_full(const char *prompt, struct Buffer *fname, bool mailbox, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file.
Definition: curs_lib.c:652
void mutt_window_reflow_message_rows(int mw_rows)
Resize the Message Window.
Definition: mutt_window.c:306
Keep our place when entering a string.
Definition: enter_state.h:31
#define mutt_b2s(buf)
Definition: buffer.h:41
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:843
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:94
WHERE bool OptForceRefresh
(pseudo) refresh even during macros
Definition: options.h:35
Prototypes for many functions.
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:499
static struct KeyEvent * UngetKeyEvents
Definition: curs_lib.c:87
Informational message.
Definition: mutt_curses.h:137
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1266
void mutt_format_s_x(char *buf, size_t buflen, const char *prec, const char *s, bool arboreal)
Format a string like snprintf()
Definition: curs_lib.c:1114
#define BEEP()
Definition: mutt_curses.h:80
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_enter_state_free(struct EnterState **ptr)
Free an EnterState.
Definition: enter.c:823
WHERE char ErrorBuf[256]
Copy of the last error message.
Definition: globals.h:45
char * data
Pointer to data.
Definition: buffer.h:35
WHERE short C_Timeout
Config: Time to wait for user input in menus.
Definition: globals.h:156
uint16_t PagerFlags
Flags for mutt_pager(), e.g. MUTT_SHOWFLAT.
Definition: pager.h:42
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:1216
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
WHERE bool ErrorBufMessage
true if the last message was an error
Definition: globals.h:44
GUI present the user with a selectable list.
An event such as a keypress.
Definition: mutt_curses.h:108
struct KeyEvent mutt_getch(void)
Read a character from the input buffer.
Definition: curs_lib.c:175
int ColorDefs[MT_COLOR_MAX]
Array of all fixed colours, see enum ColorId.
Definition: color.c:55
#define SET_COLOR(X)
Definition: mutt_curses.h:224
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: browser.h:46
int mutt_window_mvaddstr(struct MuttWindow *win, int row, int col, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:202
#define MUTT_FILE
Do file completion.
Definition: mutt.h:66
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:188
int mutt_get_field_unbuffered(const char *msg, char *buf, size_t buflen, CompletionFlags flags)
Ask the user for a string (ignoring macro buffer)
Definition: curs_lib.c:287
#define MUTT_SEL_MAILBOX
Select a mailbox.
Definition: browser.h:45
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:210
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:532
static size_t MacroBufferLen
Definition: curs_lib.c:79
Colour indicator.
Definition: mutt_menu.h:76
GUI display a file/email/help in a viewport with paging.
#define MUTT_EFILE
Do file completion, plus incoming folders.
Definition: mutt.h:67
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:1151
Log at debug level 1.
Definition: logging.h:56
#define mutt_curs_set(x)
Definition: mutt_curses.h:94
int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color, struct Pager *info)
Display some page-able text to the user.
Definition: curs_lib.c:586
#define mutt_error(...)
Definition: logging.h:84
void mutt_flush_unget_to_endcond(void)
Clear entries from UngetKeyEvents.
Definition: curs_lib.c:792
void mutt_getch_timeout(int delay)
Set the getch() timeout.
Definition: curs_lib.c:132
void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1417
#define FREE(x)
Definition: memory.h:40
Error message.
Definition: mutt_curses.h:132
WHERE bool OptMsgErr
(pseudo) used by mutt_error/mutt_message
Definition: options.h:39
Monitor files for changes.
#define EILSEQ
Definition: charset.c:50
void mutt_unget_string(const char *s)
Return a string to the input buffer.
Definition: curs_lib.c:738
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:485
WHERE char * C_Pager
Config: External command for viewing messages, or &#39;builtin&#39; to use NeoMutt&#39;s.
Definition: globals.h:137
struct MuttWindow * MuttMessageWindow
Message Window.
Definition: mutt_window.c:42
struct EnterState * mutt_enter_state_new(void)
Create a new EnterState.
Definition: enter.c:127
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1195
int mutt_pager(const char *banner, const char *fname, PagerFlags flags, struct Pager *extra)
Display a file, or help, in a window.
Definition: pager.c:2241
WHERE bool OptKeepQuiet
(pseudo) shut up the message and refresh functions while we are executing an external program...
Definition: options.h:37
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
void mutt_need_hard_redraw(void)
Force a hard refresh.
Definition: curs_lib.c:116
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:308
Convenience wrapper for the library headers.
bool C_MetaKey
Config: Interpret &#39;ALT-x&#39; as &#39;ESC-x&#39;.
Definition: curs_lib.c:68
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
int MuttGetchTimeout
Timeout in ms for mutt_getch()
Definition: curs_lib.c:89
void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string with tree characters.
Definition: curs_lib.c:1163
static size_t UngetLen
Definition: curs_lib.c:86
void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width, enum FormatJustify justify, char pad_char, const char *s, size_t n, bool arboreal)
Format a string, like snprintf()
Definition: curs_lib.c:1000
int mutt_addwch(wchar_t wc)
addwch would be provided by an up-to-date curses library
Definition: curs_lib.c:966
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
int mutt_get_field_full(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:246
uint8_t SelectFileFlags
Flags for mutt_select_file(), e.g. MUTT_SEL_MAILBOX.
Definition: browser.h:43