NeoMutt  2020-04-24
Teaching an old dog new tricks
DOXYGEN
muttlib.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <ctype.h>
33 #include <errno.h>
34 #include <inttypes.h>
35 #include <limits.h>
36 #include <pwd.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include "mutt/lib.h"
45 #include "address/lib.h"
46 #include "config/lib.h"
47 #include "email/lib.h"
48 #include "core/lib.h"
49 #include "gui/lib.h"
50 #include "mutt.h"
51 #include "muttlib.h"
52 #include "alias.h"
53 #include "format_flags.h"
54 #include "globals.h"
55 #include "hook.h"
56 #include "init.h"
57 #include "mx.h"
58 #include "protos.h"
59 #include "ncrypt/lib.h"
60 #if defined(HAVE_SYSCALL_H)
61 #include <syscall.h>
62 #elif defined(HAVE_SYS_SYSCALL_H)
63 #include <sys/syscall.h>
64 #endif
65 #ifdef USE_IMAP
66 #include "imap/lib.h"
67 #endif
68 
69 /* These Config Variables are only used in muttlib.c */
70 struct Regex *C_GecosMask;
71 
72 static FILE *fp_random;
73 
74 static const unsigned char base32[] = "abcdefghijklmnopqrstuvwxyz234567";
75 
76 static const char *xdg_env_vars[] = {
77  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
78  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
79 };
80 
81 static const char *xdg_defaults[] = {
82  [XDG_CONFIG_HOME] = "~/.config",
83  [XDG_CONFIG_DIRS] = "/etc/xdg",
84 };
85 
94 void mutt_adv_mktemp(struct Buffer *buf)
95 {
96  if (!(buf->data && buf->data[0]))
97  {
98  mutt_buffer_mktemp(buf);
99  }
100  else
101  {
102  struct Buffer *prefix = mutt_buffer_pool_get();
103  mutt_buffer_strcpy(prefix, buf->data);
104  mutt_file_sanitize_filename(prefix->data, true);
105  mutt_buffer_printf(buf, "%s/%s", NONULL(C_Tmpdir), mutt_b2s(prefix));
106 
107  struct stat sb;
108  if ((lstat(mutt_b2s(buf), &sb) == -1) && (errno == ENOENT))
109  goto out;
110 
111  char *suffix = strchr(prefix->data, '.');
112  if (suffix)
113  {
114  *suffix = '\0';
115  suffix++;
116  }
117  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
118 
119  out:
120  mutt_buffer_pool_release(&prefix);
121  }
122 }
123 
132 char *mutt_expand_path(char *buf, size_t buflen)
133 {
134  return mutt_expand_path_regex(buf, buflen, false);
135 }
136 
144 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
145 {
146  const char *s = NULL;
147  const char *tail = "";
148 
149  bool recurse = false;
150 
151  struct Buffer *p = mutt_buffer_pool_get();
152  struct Buffer *q = mutt_buffer_pool_get();
153  struct Buffer *tmp = mutt_buffer_pool_get();
154 
155  do
156  {
157  recurse = false;
158  s = mutt_b2s(buf);
159 
160  switch (*s)
161  {
162  case '~':
163  {
164  if ((s[1] == '/') || (s[1] == '\0'))
165  {
167  tail = s + 1;
168  }
169  else
170  {
171  char *t = strchr(s + 1, '/');
172  if (t)
173  *t = '\0';
174 
175  struct passwd *pw = getpwnam(s + 1);
176  if (pw)
177  {
178  mutt_buffer_strcpy(p, pw->pw_dir);
179  if (t)
180  {
181  *t = '/';
182  tail = t;
183  }
184  else
185  tail = "";
186  }
187  else
188  {
189  /* user not found! */
190  if (t)
191  *t = '/';
193  tail = s;
194  }
195  }
196  break;
197  }
198 
199  case '=':
200  case '+':
201  {
202  enum MailboxType mb_type = mx_path_probe(C_Folder);
203 
204  /* if folder = {host} or imap[s]://host/: don't append slash */
205  if ((mb_type == MUTT_IMAP) && ((C_Folder[strlen(C_Folder) - 1] == '}') ||
206  (C_Folder[strlen(C_Folder) - 1] == '/')))
207  {
209  }
210  else if (mb_type == MUTT_NOTMUCH)
212  else if (C_Folder && (C_Folder[strlen(C_Folder) - 1] == '/'))
214  else
215  mutt_buffer_printf(p, "%s/", NONULL(C_Folder));
216 
217  tail = s + 1;
218  break;
219  }
220 
221  /* elm compatibility, @ expands alias to user name */
222 
223  case '@':
224  {
225  struct AddressList *al = mutt_alias_lookup(s + 1);
226  if (al && !TAILQ_EMPTY(al))
227  {
228  struct Email *e = email_new();
229  e->env = mutt_env_new();
230  mutt_addrlist_copy(&e->env->from, al, false);
231  mutt_addrlist_copy(&e->env->to, al, false);
232 
233  /* TODO: fix mutt_default_save() to use Buffer */
235  mutt_default_save(p->data, p->dsize, e);
237 
238  email_free(&e);
239  /* Avoid infinite recursion if the resulting folder starts with '@' */
240  if (*p->data != '@')
241  recurse = true;
242 
243  tail = "";
244  }
245  break;
246  }
247 
248  case '>':
249  {
251  tail = s + 1;
252  break;
253  }
254 
255  case '<':
256  {
258  tail = s + 1;
259  break;
260  }
261 
262  case '!':
263  {
264  if (s[1] == '!')
265  {
267  tail = s + 2;
268  }
269  else
270  {
272  tail = s + 1;
273  }
274  break;
275  }
276 
277  case '-':
278  {
280  tail = s + 1;
281  break;
282  }
283 
284  case '^':
285  {
287  tail = s + 1;
288  break;
289  }
290 
291  default:
292  {
294  tail = s;
295  }
296  }
297 
298  if (regex && *(mutt_b2s(p)) && !recurse)
299  {
301  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(q), tail);
302  }
303  else
304  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(p), tail);
305 
306  mutt_buffer_copy(buf, tmp);
307  } while (recurse);
308 
312 
313 #ifdef USE_IMAP
314  /* Rewrite IMAP path in canonical form - aids in string comparisons of
315  * folders. May possibly fail, in which case buf should be the same. */
316  if (imap_path_probe(mutt_b2s(buf), NULL) == MUTT_IMAP)
317  imap_expand_path(buf);
318 #endif
319 }
320 
328 {
329  mutt_buffer_expand_path_regex(buf, false);
330 }
331 
341 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
342 {
343  struct Buffer *tmp = mutt_buffer_pool_get();
344 
345  mutt_buffer_addstr(tmp, NONULL(buf));
346  mutt_buffer_expand_path_regex(tmp, regex);
347  mutt_str_strfcpy(buf, mutt_b2s(tmp), buflen);
348 
350 
351  return buf;
352 }
353 
366 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
367 {
368  regmatch_t pat_match[1];
369  size_t pwnl;
370  char *p = NULL;
371 
372  if (!pw || !pw->pw_gecos)
373  return NULL;
374 
375  memset(dest, 0, destlen);
376 
377  if (mutt_regex_capture(C_GecosMask, pw->pw_gecos, 1, pat_match))
378  {
379  mutt_str_strfcpy(dest, pw->pw_gecos + pat_match[0].rm_so,
380  MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
381  }
382  else if ((p = strchr(pw->pw_gecos, ',')))
383  mutt_str_strfcpy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
384  else
385  mutt_str_strfcpy(dest, pw->pw_gecos, destlen);
386 
387  pwnl = strlen(pw->pw_name);
388 
389  for (int idx = 0; dest[idx]; idx++)
390  {
391  if (dest[idx] == '&')
392  {
393  memmove(&dest[idx + pwnl], &dest[idx + 1],
394  MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
395  memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
396  dest[idx] = toupper((unsigned char) dest[idx]);
397  }
398  }
399 
400  return dest;
401 }
402 
409 bool mutt_needs_mailcap(struct Body *m)
410 {
411  switch (m->type)
412  {
413  case TYPE_TEXT:
414  if (mutt_str_strcasecmp("plain", m->subtype) == 0)
415  return false;
416  break;
417  case TYPE_APPLICATION:
419  return false;
421  return false;
422  break;
423 
424  case TYPE_MULTIPART:
425  case TYPE_MESSAGE:
426  return false;
427  }
428 
429  return true;
430 }
431 
437 bool mutt_is_text_part(struct Body *b)
438 {
439  int t = b->type;
440  char *s = b->subtype;
441 
443  return false;
444 
445  if (t == TYPE_TEXT)
446  return true;
447 
448  if (t == TYPE_MESSAGE)
449  {
450  if (mutt_str_strcasecmp("delivery-status", s) == 0)
451  return true;
452  }
453 
454  if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
455  {
456  if (mutt_str_strcasecmp("pgp-keys", s) == 0)
457  return true;
458  }
459 
460  return false;
461 }
462 
470 int mutt_randbuf(void *buf, size_t buflen)
471 {
472  if (buflen > 1048576)
473  {
474  mutt_error(_("mutt_randbuf buflen=%zu"), buflen);
475  return -1;
476  }
477 /* XXX switch to HAVE_GETRANDOM and getrandom() in about 2017 */
478 #if defined(SYS_getrandom) && defined(__linux__)
479  long ret;
480  do
481  {
482  ret = syscall(SYS_getrandom, buf, buflen, 0, 0, 0, 0);
483  } while ((ret == -1) && (errno == EINTR));
484  if (ret == buflen)
485  return 0;
486 #endif
487  /* let's try urandom in case we're on an old kernel, or the user has
488  * configured selinux, seccomp or something to not allow getrandom */
489  if (!fp_random)
490  {
491  fp_random = fopen("/dev/urandom", "rb");
492  if (!fp_random)
493  {
494  mutt_error(_("open /dev/urandom: %s"), strerror(errno));
495  return -1;
496  }
497  setbuf(fp_random, NULL);
498  }
499  if (fread(buf, 1, buflen, fp_random) != buflen)
500  {
501  mutt_error(_("read /dev/urandom: %s"), strerror(errno));
502  return -1;
503  }
504 
505  return 0;
506 }
507 
513 void mutt_rand_base32(void *buf, size_t buflen)
514 {
515  uint8_t *p = buf;
516 
517  if (mutt_randbuf(p, buflen) < 0)
518  mutt_exit(1);
519  for (size_t pos = 0; pos < buflen; pos++)
520  p[pos] = base32[p[pos] % 32];
521 }
522 
527 uint32_t mutt_rand32(void)
528 {
529  uint32_t num = 0;
530 
531  if (mutt_randbuf(&num, sizeof(num)) < 0)
532  mutt_exit(1);
533  return num;
534 }
535 
540 uint64_t mutt_rand64(void)
541 {
542  uint64_t num = 0;
543 
544  if (mutt_randbuf(&num, sizeof(num)) < 0)
545  mutt_exit(1);
546  return num;
547 }
548 
557 void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix,
558  const char *suffix, const char *src, int line)
559 {
560  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
561  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
562  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
563 
564  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, mutt_b2s(buf));
565  if (unlink(mutt_b2s(buf)) && (errno != ENOENT))
566  {
567  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
568  line, mutt_b2s(buf), strerror(errno), errno);
569  }
570 }
571 
583 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
584  const char *suffix, const char *src, int line)
585 {
586  size_t n =
587  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
588  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
589  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
590  if (n >= buflen)
591  {
593  "%s:%d: ERROR: insufficient buffer space to hold temporary "
594  "filename! buflen=%zu but need %zu\n",
595  src, line, buflen, n);
596  }
597  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
598  if (unlink(buf) && (errno != ENOENT))
599  {
600  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
601  line, buf, strerror(errno), errno);
602  }
603 }
604 
612 void mutt_pretty_mailbox(char *buf, size_t buflen)
613 {
614  if (!buf)
615  return;
616 
617  char *p = buf, *q = buf;
618  size_t len;
619  enum UrlScheme scheme;
620  char tmp[PATH_MAX];
621 
622  scheme = url_check_scheme(buf);
623 
624  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
625  {
626  imap_pretty_mailbox(buf, buflen, C_Folder);
627  return;
628  }
629 
630  if (scheme == U_NOTMUCH)
631  return;
632 
633  /* if buf is an url, only collapse path component */
634  if (scheme != U_UNKNOWN)
635  {
636  p = strchr(buf, ':') + 1;
637  if (strncmp(p, "//", 2) == 0)
638  q = strchr(p + 2, '/');
639  if (!q)
640  q = strchr(p, '\0');
641  p = q;
642  }
643 
644  /* cleanup path */
645  if (strstr(p, "//") || strstr(p, "/./"))
646  {
647  /* first attempt to collapse the pathname, this is more
648  * lightweight than realpath() and doesn't resolve links */
649  while (*p)
650  {
651  if ((p[0] == '/') && (p[1] == '/'))
652  {
653  *q++ = '/';
654  p += 2;
655  }
656  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
657  {
658  *q++ = '/';
659  p += 3;
660  }
661  else
662  *q++ = *p++;
663  }
664  *q = '\0';
665  }
666  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
667  realpath(p, tmp))
668  {
669  mutt_str_strfcpy(p, tmp, buflen - (p - buf));
670  }
671 
672  if ((len = mutt_str_startswith(buf, C_Folder, CASE_MATCH)) && (buf[len] == '/'))
673  {
674  *buf++ = '=';
675  memmove(buf, buf + len, mutt_str_strlen(buf + len) + 1);
676  }
677  else if ((len = mutt_str_startswith(buf, HomeDir, CASE_MATCH)) && (buf[len] == '/'))
678  {
679  *buf++ = '~';
680  memmove(buf, buf + len - 1, mutt_str_strlen(buf + len - 1) + 1);
681  }
682 }
683 
689 {
690  if (!buf || !buf->data)
691  return;
692  /* This reduces the size of the Buffer, so we can pass it through.
693  * We adjust the size just to make sure buf->data is not NULL though */
695  mutt_pretty_mailbox(buf->data, buf->dsize);
697 }
698 
710 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
711  enum SaveAttach *opt, char **directory)
712 {
713  struct stat st;
714 
715  mutt_buffer_strcpy(fname, path);
716  if (access(mutt_b2s(fname), F_OK) != 0)
717  return 0;
718  if (stat(mutt_b2s(fname), &st) != 0)
719  return -1;
720  if (S_ISDIR(st.st_mode))
721  {
722  enum QuadOption ans = MUTT_NO;
723  if (directory)
724  {
725  switch (mutt_multi_choice
726  /* L10N: Means "The path you specified as the destination file is a directory."
727  See the msgid "Save to file: " (alias.c, recvattach.c)
728  These three letters correspond to the choices in the string. */
729  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
730  {
731  case 3: /* all */
732  mutt_str_replace(directory, mutt_b2s(fname));
733  break;
734  case 1: /* yes */
735  FREE(directory);
736  break;
737  case -1: /* abort */
738  FREE(directory);
739  return -1;
740  case 2: /* no */
741  FREE(directory);
742  return 1;
743  }
744  }
745  /* L10N: Means "The path you specified as the destination file is a directory."
746  See the msgid "Save to file: " (alias.c, recvattach.c) */
747  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
748  return (ans == MUTT_NO) ? 1 : -1;
749 
750  struct Buffer *tmp = mutt_buffer_pool_get();
752  if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_FILE | MUTT_CLEAR) != 0) ||
754  {
756  return (-1);
757  }
758  mutt_buffer_concat_path(fname, path, mutt_b2s(tmp));
760  }
761 
762  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_b2s(fname), F_OK) == 0))
763  {
764  switch (
765  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
766  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
767  _("oac")))
768  {
769  case -1: /* abort */
770  return -1;
771  case 3: /* cancel */
772  return 1;
773 
774  case 2: /* append */
775  *opt = MUTT_SAVE_APPEND;
776  break;
777  case 1: /* overwrite */
778  *opt = MUTT_SAVE_OVERWRITE;
779  break;
780  }
781  }
782  return 0;
783 }
784 
794 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
795 {
796  if (addr && addr->mailbox)
797  {
798  mutt_str_strfcpy(buf, addr->mailbox, buflen);
799  if (!C_SaveAddress)
800  {
801  char *p = strpbrk(buf, "%@");
802  if (p)
803  *p = '\0';
804  }
805  mutt_str_strlower(buf);
806  }
807  else
808  *buf = '\0';
809 }
810 
816 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
817 {
818  if (a && a->mailbox)
819  {
820  mutt_buffer_strcpy(dest, a->mailbox);
821  if (!C_SaveAddress)
822  {
823  char *p = strpbrk(dest->data, "%@");
824  if (p)
825  {
826  *p = '\0';
827  mutt_buffer_fix_dptr(dest);
828  }
829  }
830  mutt_str_strlower(dest->data);
831  }
832  else
833  mutt_buffer_reset(dest);
834 }
835 
843 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
844 {
845  mutt_buffer_save_path(dest, a);
846  for (char *p = dest->data; *p; p++)
847  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
848  *p = '_';
849 }
850 
862 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
863  format_t *callback, unsigned long data, MuttFormatFlags flags)
864 {
865  char prefix[128], tmp[1024];
866  char *cp = NULL, *wptr = buf;
867  char ch;
868  char if_str[128], else_str[128];
869  size_t wlen, count, len, wid;
870  FILE *fp_filter = NULL;
871  char *recycler = NULL;
872 
873  char src2[256];
874  mutt_str_strfcpy(src2, src, mutt_str_strlen(src) + 1);
875  src = src2;
876 
877  prefix[0] = '\0';
878  buflen--; /* save room for the terminal \0 */
879  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
881  0;
882  col += wlen;
883 
884  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
885  {
886  int off = -1;
887 
888  /* Do not consider filters if no pipe at end */
889  int n = mutt_str_strlen(src);
890  if ((n > 1) && (src[n - 1] == '|'))
891  {
892  /* Scan backwards for backslashes */
893  off = n;
894  while ((off > 0) && (src[off - 2] == '\\'))
895  off--;
896  }
897 
898  /* If number of backslashes is even, the pipe is real. */
899  /* n-off is the number of backslashes. */
900  if ((off > 0) && (((n - off) % 2) == 0))
901  {
902  char srccopy[1024];
903  int i = 0;
904 
905  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
906 
907  strncpy(srccopy, src, n);
908  srccopy[n - 1] = '\0';
909 
910  /* prepare Buffers */
911  struct Buffer srcbuf = mutt_buffer_make(0);
912  mutt_buffer_addstr(&srcbuf, srccopy);
913  /* note: we are resetting dptr and *reading* from the buffer, so we don't
914  * want to use mutt_buffer_reset(). */
915  srcbuf.dptr = srcbuf.data;
916  struct Buffer word = mutt_buffer_make(0);
917  struct Buffer cmd = mutt_buffer_make(0);
918 
919  /* Iterate expansions across successive arguments */
920  do
921  {
922  /* Extract the command name and copy to command line */
923  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
924  if (word.data)
925  *word.data = '\0';
926  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
927  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
928  mutt_buffer_addch(&cmd, '\'');
929  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
930  data, flags | MUTT_FORMAT_NOFILTER);
931  for (char *p = tmp; p && *p; p++)
932  {
933  if (*p == '\'')
934  {
935  /* shell quoting doesn't permit escaping a single quote within
936  * single-quoted material. double-quoting instead will lead
937  * shell variable expansions, so break out of the single-quoted
938  * span, insert a double-quoted single quote, and resume. */
939  mutt_buffer_addstr(&cmd, "'\"'\"'");
940  }
941  else
942  mutt_buffer_addch(&cmd, *p);
943  }
944  mutt_buffer_addch(&cmd, '\'');
945  mutt_buffer_addch(&cmd, ' ');
946  } while (MoreArgs(&srcbuf));
947 
948  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
949 
950  col -= wlen; /* reset to passed in value */
951  wptr = buf; /* reset write ptr */
952  pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
953  if (pid != -1)
954  {
955  int rc;
956 
957  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
958  mutt_file_fclose(&fp_filter);
959  rc = filter_wait(pid);
960  if (rc != 0)
961  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
962  if (n > 0)
963  {
964  buf[n] = '\0';
965  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
966  buf[--n] = '\0';
967  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
968 
969  /* If the result ends with '%', this indicates that the filter
970  * generated %-tokens that neomutt can expand. Eliminate the '%'
971  * marker and recycle the string through mutt_expando_format().
972  * To literally end with "%", use "%%". */
973  if ((n > 0) && (buf[n - 1] == '%'))
974  {
975  n--;
976  buf[n] = '\0'; /* remove '%' */
977  if ((n > 0) && (buf[n - 1] != '%'))
978  {
979  recycler = mutt_str_strdup(buf);
980  if (recycler)
981  {
982  /* buflen is decremented at the start of this function
983  * to save space for the terminal nul char. We can add
984  * it back for the recursive call since the expansion of
985  * format pipes does not try to append a nul itself. */
986  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
987  callback, data, flags);
988  FREE(&recycler);
989  }
990  }
991  }
992  }
993  else
994  {
995  /* read error */
996  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
997  strerror(errno), errno);
998  *wptr = '\0';
999  }
1000  }
1001  else
1002  {
1003  /* Filter failed; erase write buffer */
1004  *wptr = '\0';
1005  }
1006 
1007  mutt_buffer_dealloc(&cmd);
1008  mutt_buffer_dealloc(&srcbuf);
1009  mutt_buffer_dealloc(&word);
1010  return;
1011  }
1012  }
1013 
1014  while (*src && (wlen < buflen))
1015  {
1016  if (*src == '%')
1017  {
1018  if (*++src == '%')
1019  {
1020  *wptr++ = '%';
1021  wlen++;
1022  col++;
1023  src++;
1024  continue;
1025  }
1026 
1027  if (*src == '?')
1028  {
1029  /* change original %? to new %< notation */
1030  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
1031  char *p = (char *) src;
1032  *p = '<';
1033  /* skip over "x" */
1034  for (; *p && *p != '?'; p++)
1035  ;
1036  /* nothing */
1037  if (*p == '?')
1038  p++;
1039  /* fix up the "y&z" section */
1040  for (; *p && *p != '?'; p++)
1041  {
1042  /* escape '<' and '>' to work inside nested-if */
1043  if ((*p == '<') || (*p == '>'))
1044  {
1045  memmove(p + 2, p, mutt_str_strlen(p) + 1);
1046  *p++ = '\\';
1047  *p++ = '\\';
1048  }
1049  }
1050  if (*p == '?')
1051  *p = '>';
1052  }
1053 
1054  if (*src == '<')
1055  {
1056  flags |= MUTT_FORMAT_OPTIONAL;
1057  ch = *(++src); /* save the character to switch on */
1058  src++;
1059  cp = prefix;
1060  count = 0;
1061  while ((count < sizeof(prefix)) && (*src != '?'))
1062  {
1063  *cp++ = *src++;
1064  count++;
1065  }
1066  *cp = '\0';
1067  }
1068  else
1069  {
1070  flags &= ~MUTT_FORMAT_OPTIONAL;
1071 
1072  /* eat the format string */
1073  cp = prefix;
1074  count = 0;
1075  while ((count < sizeof(prefix)) && (isdigit((unsigned char) *src) || (*src == '.') ||
1076  (*src == '-') || (*src == '=')))
1077  {
1078  *cp++ = *src++;
1079  count++;
1080  }
1081  *cp = '\0';
1082 
1083  if (!*src)
1084  break; /* bad format */
1085 
1086  ch = *src++; /* save the character to switch on */
1087  }
1088 
1089  if (flags & MUTT_FORMAT_OPTIONAL)
1090  {
1091  int lrbalance;
1092 
1093  if (*src != '?')
1094  break; /* bad format */
1095  src++;
1096 
1097  /* eat the 'if' part of the string */
1098  cp = if_str;
1099  count = 0;
1100  lrbalance = 1;
1101  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1102  {
1103  if ((src[0] == '%') && (src[1] == '>'))
1104  {
1105  /* This is a padding expando; copy two chars and carry on */
1106  *cp++ = *src++;
1107  *cp++ = *src++;
1108  count += 2;
1109  continue;
1110  }
1111 
1112  if (*src == '\\')
1113  {
1114  src++;
1115  *cp++ = *src++;
1116  }
1117  else if ((src[0] == '%') && (src[1] == '<'))
1118  {
1119  lrbalance++;
1120  }
1121  else if (src[0] == '>')
1122  {
1123  lrbalance--;
1124  }
1125  if (lrbalance == 0)
1126  break;
1127  if ((lrbalance == 1) && (src[0] == '&'))
1128  break;
1129  *cp++ = *src++;
1130  count++;
1131  }
1132  *cp = '\0';
1133 
1134  /* eat the 'else' part of the string (optional) */
1135  if (*src == '&')
1136  src++; /* skip the & */
1137  cp = else_str;
1138  count = 0;
1139  while ((lrbalance > 0) && (count < sizeof(else_str)) && *src)
1140  {
1141  if ((src[0] == '%') && (src[1] == '>'))
1142  {
1143  /* This is a padding expando; copy two chars and carry on */
1144  *cp++ = *src++;
1145  *cp++ = *src++;
1146  count += 2;
1147  continue;
1148  }
1149 
1150  if (*src == '\\')
1151  {
1152  src++;
1153  *cp++ = *src++;
1154  }
1155  else if ((src[0] == '%') && (src[1] == '<'))
1156  {
1157  lrbalance++;
1158  }
1159  else if (src[0] == '>')
1160  {
1161  lrbalance--;
1162  }
1163  if (lrbalance == 0)
1164  break;
1165  if ((lrbalance == 1) && (src[0] == '&'))
1166  break;
1167  *cp++ = *src++;
1168  count++;
1169  }
1170  *cp = '\0';
1171 
1172  if (!*src)
1173  break; /* bad format */
1174 
1175  src++; /* move past the trailing '>' (formerly '?') */
1176  }
1177 
1178  /* handle generic cases first */
1179  if ((ch == '>') || (ch == '*'))
1180  {
1181  /* %>X: right justify to EOL, left takes precedence
1182  * %*X: right justify to EOL, right takes precedence */
1183  int soft = ch == '*';
1184  int pl, pw;
1185  pl = mutt_mb_charlen(src, &pw);
1186  if (pl <= 0)
1187  {
1188  pl = 1;
1189  pw = 1;
1190  }
1191 
1192  /* see if there's room to add content, else ignore */
1193  if (((col < cols) && (wlen < buflen)) || soft)
1194  {
1195  int pad;
1196 
1197  /* get contents after padding */
1198  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1199  len = mutt_str_strlen(tmp);
1200  wid = mutt_strwidth(tmp);
1201 
1202  pad = (cols - col - wid) / pw;
1203  if (pad >= 0)
1204  {
1205  /* try to consume as many columns as we can, if we don't have
1206  * memory for that, use as much memory as possible */
1207  if (wlen + (pad * pl) + len > buflen)
1208  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1209  else
1210  {
1211  /* Add pre-spacing to make multi-column pad characters and
1212  * the contents after padding line up */
1213  while ((col + (pad * pw) + wid < cols) && (wlen + (pad * pl) + len < buflen))
1214  {
1215  *wptr++ = ' ';
1216  wlen++;
1217  col++;
1218  }
1219  }
1220  while (pad-- > 0)
1221  {
1222  memcpy(wptr, src, pl);
1223  wptr += pl;
1224  wlen += pl;
1225  col += pw;
1226  }
1227  }
1228  else if (soft)
1229  {
1230  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
1232  0;
1233  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1234  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1235  *wptr = '\0';
1236  /* make sure right part is at most as wide as display */
1237  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1238  /* truncate left so that right part fits completely in */
1239  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1240  wptr = buf + wlen;
1241  /* Multi-column characters may be truncated in the middle.
1242  * Add spacing so the right hand side lines up. */
1243  while ((col + wid < avail_cols) && (wlen + len < buflen))
1244  {
1245  *wptr++ = ' ';
1246  wlen++;
1247  col++;
1248  }
1249  }
1250  if ((len + wlen) > buflen)
1251  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1252  memcpy(wptr, tmp, len);
1253  wptr += len;
1254  }
1255  break; /* skip rest of input */
1256  }
1257  else if (ch == '|')
1258  {
1259  /* pad to EOL */
1260  int pl, pw;
1261  pl = mutt_mb_charlen(src, &pw);
1262  if (pl <= 0)
1263  {
1264  pl = 1;
1265  pw = 1;
1266  }
1267 
1268  /* see if there's room to add content, else ignore */
1269  if ((col < cols) && (wlen < buflen))
1270  {
1271  int c = (cols - col) / pw;
1272  if ((c > 0) && (wlen + (c * pl) > buflen))
1273  c = ((signed) (buflen - wlen)) / pl;
1274  while (c > 0)
1275  {
1276  memcpy(wptr, src, pl);
1277  wptr += pl;
1278  wlen += pl;
1279  col += pw;
1280  c--;
1281  }
1282  }
1283  break; /* skip rest of input */
1284  }
1285  else
1286  {
1287  bool to_lower = false;
1288  bool no_dots = false;
1289 
1290  while ((ch == '_') || (ch == ':'))
1291  {
1292  if (ch == '_')
1293  to_lower = true;
1294  else if (ch == ':')
1295  no_dots = true;
1296 
1297  ch = *src++;
1298  }
1299 
1300  /* use callback function to handle this case */
1301  *tmp = '\0';
1302  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1303  else_str, data, flags);
1304 
1305  if (to_lower)
1306  mutt_str_strlower(tmp);
1307  if (no_dots)
1308  {
1309  char *p = tmp;
1310  for (; *p; p++)
1311  if (*p == '.')
1312  *p = '_';
1313  }
1314 
1315  len = mutt_str_strlen(tmp);
1316  if ((len + wlen) > buflen)
1317  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1318 
1319  memcpy(wptr, tmp, len);
1320  wptr += len;
1321  wlen += len;
1322  col += mutt_strwidth(tmp);
1323  }
1324  }
1325  else if (*src == '\\')
1326  {
1327  if (!*++src)
1328  break;
1329  switch (*src)
1330  {
1331  case 'f':
1332  *wptr = '\f';
1333  break;
1334  case 'n':
1335  *wptr = '\n';
1336  break;
1337  case 'r':
1338  *wptr = '\r';
1339  break;
1340  case 't':
1341  *wptr = '\t';
1342  break;
1343  case 'v':
1344  *wptr = '\v';
1345  break;
1346  default:
1347  *wptr = *src;
1348  break;
1349  }
1350  src++;
1351  wptr++;
1352  wlen++;
1353  col++;
1354  }
1355  else
1356  {
1357  int bytes, width;
1358  /* in case of error, simply copy byte */
1359  bytes = mutt_mb_charlen(src, &width);
1360  if (bytes < 0)
1361  {
1362  bytes = 1;
1363  width = 1;
1364  }
1365  if ((bytes > 0) && ((wlen + bytes) < buflen))
1366  {
1367  memcpy(wptr, src, bytes);
1368  wptr += bytes;
1369  src += bytes;
1370  wlen += bytes;
1371  col += width;
1372  }
1373  else
1374  {
1375  src += buflen - wlen;
1376  wlen = buflen;
1377  }
1378  }
1379  }
1380  *wptr = '\0';
1381 }
1382 
1393 FILE *mutt_open_read(const char *path, pid_t *thepid)
1394 {
1395  FILE *fp = NULL;
1396  struct stat s;
1397 
1398  size_t len = mutt_str_strlen(path);
1399  if (len == 0)
1400  {
1401  return NULL;
1402  }
1403 
1404  if (path[len - 1] == '|')
1405  {
1406  /* read from a pipe */
1407 
1408  char *p = mutt_str_strdup(path);
1409 
1410  p[len - 1] = 0;
1411  mutt_endwin();
1412  *thepid = filter_create(p, NULL, &fp, NULL);
1413  FREE(&p);
1414  }
1415  else
1416  {
1417  if (stat(path, &s) < 0)
1418  return NULL;
1419  if (S_ISDIR(s.st_mode))
1420  {
1421  errno = EINVAL;
1422  return NULL;
1423  }
1424  fp = fopen(path, "r");
1425  *thepid = -1;
1426  }
1427  return fp;
1428 }
1429 
1438 int mutt_save_confirm(const char *s, struct stat *st)
1439 {
1440  int ret = 0;
1441 
1442  enum MailboxType type = mx_path_probe(s);
1443 
1444 #ifdef USE_POP
1445  if (type == MUTT_POP)
1446  {
1447  mutt_error(_("Can't save message to POP mailbox"));
1448  return 1;
1449  }
1450 #endif
1451 
1452  if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && !mx_access(s, W_OK))
1453  {
1454  if (C_Confirmappend)
1455  {
1456  struct Buffer *tmp = mutt_buffer_pool_get();
1457  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1458  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1459  if (ans == MUTT_NO)
1460  ret = 1;
1461  else if (ans == MUTT_ABORT)
1462  ret = -1;
1464  }
1465  }
1466 
1467 #ifdef USE_NNTP
1468  if (type == MUTT_NNTP)
1469  {
1470  mutt_error(_("Can't save message to news server"));
1471  return 0;
1472  }
1473 #endif
1474 
1475  if (stat(s, st) != -1)
1476  {
1477  if (type == MUTT_MAILBOX_ERROR)
1478  {
1479  mutt_error(_("%s is not a mailbox"), s);
1480  return 1;
1481  }
1482  }
1483  else if (type != MUTT_IMAP)
1484  {
1485  st->st_mtime = 0;
1486  st->st_atime = 0;
1487 
1488  /* pathname does not exist */
1489  if (errno == ENOENT)
1490  {
1491  if (C_Confirmcreate)
1492  {
1493  struct Buffer *tmp = mutt_buffer_pool_get();
1494  mutt_buffer_printf(tmp, _("Create %s?"), s);
1495  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1496  if (ans == MUTT_NO)
1497  ret = 1;
1498  else if (ans == MUTT_ABORT)
1499  ret = -1;
1501  }
1502 
1503  /* user confirmed with MUTT_YES or set C_Confirmcreate */
1504  if (ret == 0)
1505  {
1506  /* create dir recursively */
1507  char *tmp_path = mutt_path_dirname(s);
1508  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1509  {
1510  /* report failure & abort */
1511  mutt_perror(s);
1512  FREE(&tmp_path);
1513  return 1;
1514  }
1515  FREE(&tmp_path);
1516  }
1517  }
1518  else
1519  {
1520  mutt_perror(s);
1521  return 1;
1522  }
1523  }
1524 
1526  return ret;
1527 }
1528 
1535 void mutt_sleep(short s)
1536 {
1537  if (C_SleepTime > s)
1538  sleep(C_SleepTime);
1539  else if (s)
1540  sleep(s);
1541 }
1542 
1549 const char *mutt_make_version(void)
1550 {
1551  static char vstring[256];
1552  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1553  return vstring;
1554 }
1555 
1563 void mutt_encode_path(struct Buffer *buf, const char *src)
1564 {
1565  char *p = mutt_str_strdup(src);
1566  int rc = mutt_ch_convert_string(&p, C_Charset, "us-ascii", 0);
1567  size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1568 
1569  /* convert the path to POSIX "Portable Filename Character Set" */
1570  for (size_t i = 0; i < len; i++)
1571  {
1572  if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1573  {
1574  buf->data[i] = '_';
1575  }
1576  }
1577  FREE(&p);
1578 }
1579 
1588 int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1589 {
1590  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1591  char *xdg = xdg_env ? mutt_str_strdup(xdg_env) : mutt_str_strdup(xdg_defaults[type]);
1592  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1593  char *token = NULL;
1594  int rc = 0;
1595 
1596  while ((token = strsep(&xdg, ":")))
1597  {
1598  if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1599  continue;
1601  if (access(mutt_b2s(buf), F_OK) == 0)
1602  {
1603  rc = 1;
1604  break;
1605  }
1606 
1607  if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1608  continue;
1610  if (access(mutt_b2s(buf), F_OK) == 0)
1611  {
1612  rc = 1;
1613  break;
1614  }
1615  }
1616 
1617  FREE(&x);
1618  return rc;
1619 }
1620 
1627 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1628 {
1629  enum MailboxType mb_type = mx_path_probe(path);
1630 
1631  if (mb_type == MUTT_IMAP)
1632  imap_get_parent_path(path, buf, buflen);
1633  else if (mb_type == MUTT_NOTMUCH)
1634  mutt_str_strfcpy(buf, C_Folder, buflen);
1635  else
1636  {
1637  mutt_str_strfcpy(buf, path, buflen);
1638  int n = mutt_str_strlen(buf);
1639  if (n == 0)
1640  return;
1641 
1642  /* remove any final trailing '/' */
1643  if (buf[n - 1] == '/')
1644  buf[n - 1] = '\0';
1645 
1646  /* Remove everything until the next slash */
1647  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1648  ;
1649 
1650  if (n > 0)
1651  buf[n] = '\0';
1652  else
1653  {
1654  buf[0] = '/';
1655  buf[1] = '\0';
1656  }
1657  }
1658 }
1659 
1682 int mutt_inbox_cmp(const char *a, const char *b)
1683 {
1684  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1685  if ((a[0] == '+') && (b[0] == '+'))
1686  {
1687  return (mutt_str_strcasecmp(a + 1, "inbox") == 0) ?
1688  -1 :
1689  (mutt_str_strcasecmp(b + 1, "inbox") == 0) ? 1 : 0;
1690  }
1691 
1692  const char *a_end = strrchr(a, '/');
1693  const char *b_end = strrchr(b, '/');
1694 
1695  /* If one path contains a '/', but not the other */
1696  if ((!a_end) ^ (!b_end))
1697  return 0;
1698 
1699  /* If neither path contains a '/' */
1700  if (!a_end)
1701  return 0;
1702 
1703  /* Compare the subpaths */
1704  size_t a_len = a_end - a;
1705  size_t b_len = b_end - b;
1706  size_t min = MIN(a_len, b_len);
1707  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1708  (b[min + 1] != '\0') && (mutt_str_strncasecmp(a, b, min) == 0);
1709 
1710  if (!same)
1711  return 0;
1712 
1713  if (mutt_str_strcasecmp(&a[min + 1], "inbox") == 0)
1714  return -1;
1715 
1716  if (mutt_str_strcasecmp(&b[min + 1], "inbox") == 0)
1717  return 1;
1718 
1719  return 0;
1720 }
1721 
1728 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1729 {
1730  if (!buf || !path)
1731  return;
1732 
1733  mutt_buffer_reset(buf);
1734 
1735  for (; *path; path++)
1736  {
1737  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1738  mutt_buffer_addch(buf, '_');
1739  else
1740  mutt_buffer_addch(buf, *path);
1741  }
1742 }
1743 
1750 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1751 {
1752  if (!buf || (buflen == 0))
1753  return;
1754 
1755  if (C_SizeShowBytes && (num < 1024))
1756  {
1757  snprintf(buf, buflen, "%d", (int) num);
1758  }
1759  else if (num == 0)
1760  {
1761  mutt_str_strfcpy(buf, C_SizeUnitsOnLeft ? "K0" : "0K", buflen);
1762  }
1763  else if (C_SizeShowFractions && (num < 10189)) /* 0.1K - 9.9K */
1764  {
1765  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "K%3.1f" : "%3.1fK",
1766  (num < 103) ? 0.1 : (num / 1024.0));
1767  }
1768  else if (!C_SizeShowMb || (num < 1023949)) /* 10K - 999K */
1769  {
1770  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1771  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1772  }
1773  else if (C_SizeShowFractions && (num < 10433332)) /* 1.0M - 9.9M */
1774  {
1775  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1776  }
1777  else /* 10M+ */
1778  {
1779  /* (10433332 + 52428) / 1048576 = 10 */
1780  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1781  }
1782 }
1783 
1791 void add_to_stailq(struct ListHead *head, const char *str)
1792 {
1793  /* don't add a NULL or empty string to the list */
1794  if (!str || (*str == '\0'))
1795  return;
1796 
1797  /* check to make sure the item is not already on this list */
1798  struct ListNode *np = NULL;
1799  STAILQ_FOREACH(np, head, entries)
1800  {
1801  if (mutt_str_strcasecmp(str, np->data) == 0)
1802  {
1803  return;
1804  }
1805  }
1807 }
1808 
1816 void remove_from_stailq(struct ListHead *head, const char *str)
1817 {
1818  if (mutt_str_strcmp("*", str) == 0)
1819  mutt_list_free(head); /* "unCMD *" means delete all current entries */
1820  else
1821  {
1822  struct ListNode *np = NULL, *tmp = NULL;
1823  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1824  {
1825  if (mutt_str_strcasecmp(str, np->data) == 0)
1826  {
1827  STAILQ_REMOVE(head, np, ListNode, entries);
1828  FREE(&np->data);
1829  FREE(&np);
1830  break;
1831  }
1832  }
1833  }
1834 }
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:219
Convenience wrapper for the gui headers.
Url is notmuch://.
Definition: url.h:45
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, unsigned long data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:862
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:81
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
int mutt_ch_convert_string(char **ps, const char *from, const char *to, int flags)
Convert a string between encodings.
Definition: charset.c:747
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:399
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
struct AddressList * mutt_alias_lookup(const char *s)
Find an Alias.
Definition: alias.c:285
int mutt_randbuf(void *buf, size_t buflen)
Fill a buffer with randomness.
Definition: muttlib.c:470
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
#define WithCrypto
Definition: lib.h:163
IMAP network mailbox.
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:62
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2524
Config/command parsing.
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
User aborted the question (with Ctrl-G)
Definition: quad.h:38
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:82
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:209
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:612
WHERE short C_SleepTime
Config: Time to pause after certain info messages.
Definition: globals.h:151
No flags set.
Definition: mutt_attach.h:55
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:734
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Url is imaps://.
Definition: url.h:39
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
String manipulation buffer.
Definition: buffer.h:33
Overwrite existing file.
Definition: mutt_attach.h:57
Url wasn&#39;t recognised.
Definition: url.h:34
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:376
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
Error occurred examining Mailbox.
Definition: mailbox.h:46
An email address.
Definition: address.h:34
WHERE char * C_ArrowString
Config: Use an custom string for arrow_cursor.
Definition: globals.h:195
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:732
WHERE char * LastFolder
Previously selected mailbox.
Definition: globals.h:55
char * mailbox
Mailbox and host address.
Definition: address.h:37
Url is imap://.
Definition: url.h:38
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:692
XDG home dir: ~/.config.
Definition: protos.h:49
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:816
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: globals.h:132
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int mutt_mb_charlen(const char *s, int *width)
Count the bytes in a (multibyte) character.
Definition: mbyte.c:55
#define IsPrint(ch)
Definition: mbyte.h:39
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: globals.h:210
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:656
Flags to control mutt_expando_format()
static const char * xdg_env_vars[]
Definition: muttlib.c:76
uint32_t mutt_rand32(void)
Create a 32-bit random number.
Definition: muttlib.c:527
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:688
Representation of a single alias to an email address.
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1438
The body of an email.
Definition: body.h:34
Convenience wrapper for the config headers.
Hundreds of global variables to back the user variables.
char * HomeDir
User&#39;s home directory.
Definition: globals.h:49
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1074
Email Address Handling.
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:144
WHERE bool C_SizeShowFractions
Config: Show size fractions with a single decimal place.
Definition: globals.h:249
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:341
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:132
#define MoreArgs(buf)
Definition: buffer.h:43
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1535
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:377
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2570
void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary filename.
Definition: muttlib.c:583
Parse and execute user-defined hooks.
Many unsorted constants and some structs.
API for mailboxes.
char * mutt_str_strlower(char *s)
Convert all characters in the string to lowercase.
Definition: string.c:512
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:307
WHERE bool C_SizeShowMb
Config: Show sizes in megabytes for sizes greater than 1 megabyte.
Definition: globals.h:250
UrlScheme
All recognised Url types.
Definition: url.h:32
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:876
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:112
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:119
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
WHERE bool C_SizeUnitsOnLeft
Config: Show the units as a prefix to the size.
Definition: globals.h:251
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:54
WHERE bool C_SizeShowBytes
Config: Show smaller sizes in bytes.
Definition: globals.h:248
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:359
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_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:843
char * subtype
content-type subtype
Definition: body.h:37
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:65
const char *() format_t(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, MuttFormatFlags flags)
Prototype for a mutt_expando_format() callback function.
Definition: format_flags.h:61
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:618
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:637
#define mutt_b2s(buf)
Definition: buffer.h:41
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:138
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:910
Prototypes for many functions.
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1791
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:565
int mutt_str_strncasecmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:682
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:137
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:546
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:121
const char * GitVer
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1336
#define PATH_MAX
Definition: mutt.h:44
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:414
XdgType
XDG variable types.
Definition: protos.h:47
struct Regex * C_GecosMask
Config: Regex for parsing GECOS field of /etc/passwd.
Definition: muttlib.c:70
Type: &#39;text/*&#39;.
Definition: mime.h:38
char * dptr
Current read/write position.
Definition: buffer.h:36
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1750
int mutt_inbox_cmp(const char *a, const char *b)
do two folders share the same path and one is an inbox
Definition: muttlib.c:1682
char * data
Pointer to data.
Definition: buffer.h:35
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:1286
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:776
API for encryption/signing of emails.
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:624
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:437
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:455
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: muttlib.c:540
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
static FILE * fp_random
Definition: muttlib.c:72
unsigned int type
content-type primary type
Definition: body.h:65
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname, enum SaveAttach *opt, char **directory)
Ask the user if overwriting is necessary.
Definition: muttlib.c:710
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1588
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:84
Type: &#39;message/*&#39;.
Definition: mime.h:35
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
match a regex against a string, with provided options
Definition: regex.c:593
#define IS_SPACE(ch)
Definition: string2.h:38
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:138
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1816
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1318
void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary file.
Definition: muttlib.c:557
char * data
String.
Definition: list.h:35
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:327
static const unsigned char base32[]
Definition: muttlib.c:74
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
Log at debug level 1.
Definition: logging.h:40
int n
Definition: acutest.h:492
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user&#39;s real name in /etc/passwd.
Definition: muttlib.c:366
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:145
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1728
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
Cached regular expression.
Definition: regex3.h:88
#define mutt_error(...)
Definition: logging.h:84
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:409
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:56
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:654
Append to existing file.
Definition: mutt_attach.h:56
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:50
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:94
#define FREE(x)
Definition: memory.h:40
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
const char filename_safe_chars[]
Definition: file.c:61
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:171
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct MuttWindow * MuttMessageWindow
Message Window.
Definition: mutt_window.c:46
struct Email * email_new(void)
Create a new Email.
Definition: email.c:68
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define TAILQ_EMPTY(head)
Definition: queue.h:714
int const char int line
Definition: acutest.h:617
Log at debug level 5.
Definition: logging.h:44
Convenience wrapper for the library headers.
A List node for strings.
Definition: list.h:33
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:41
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1393
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
XDG system dir: /etc/xdg.
Definition: protos.h:50
static const char * xdg_defaults[]
Definition: muttlib.c:81
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1627
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:258
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:641
void mutt_rand_base32(void *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: muttlib.c:513
Type: &#39;application/*&#39;.
Definition: mime.h:33
Log at debug level 3.
Definition: logging.h:42
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
Url is file://.
Definition: url.h:35
WHERE bool C_SaveAddress
Config: Use sender&#39;s full address as a default save folder.
Definition: globals.h:245
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:70
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to &#39;us-ascii&#39;.
Definition: muttlib.c:1563
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:53
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1549
void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
Turn an email address into a filename (for saving)
Definition: muttlib.c:794
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
WHERE bool C_ArrowCursor
Config: Use an arrow &#39;->&#39; instead of highlighting in the index.
Definition: globals.h:194