NeoMutt  2020-09-25
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 "alias/lib.h"
50 #include "gui/lib.h"
51 #include "mutt.h"
52 #include "muttlib.h"
53 #include "ncrypt/lib.h"
54 #include "format_flags.h"
55 #include "hook.h"
56 #include "init.h"
57 #include "mutt_globals.h"
58 #include "mx.h"
59 #include "protos.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 const char *xdg_env_vars[] = {
73  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
74  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
75 };
76 
77 static const char *xdg_defaults[] = {
78  [XDG_CONFIG_HOME] = "~/.config",
79  [XDG_CONFIG_DIRS] = "/etc/xdg",
80 };
81 
90 void mutt_adv_mktemp(struct Buffer *buf)
91 {
92  if (!(buf->data && (buf->data[0] != '\0')))
93  {
94  mutt_buffer_mktemp(buf);
95  }
96  else
97  {
98  struct Buffer *prefix = mutt_buffer_pool_get();
99  mutt_buffer_strcpy(prefix, buf->data);
100  mutt_file_sanitize_filename(prefix->data, true);
101  mutt_buffer_printf(buf, "%s/%s", NONULL(C_Tmpdir), mutt_b2s(prefix));
102 
103  struct stat sb;
104  if ((lstat(mutt_b2s(buf), &sb) == -1) && (errno == ENOENT))
105  goto out;
106 
107  char *suffix = strchr(prefix->data, '.');
108  if (suffix)
109  {
110  *suffix = '\0';
111  suffix++;
112  }
113  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
114 
115  out:
116  mutt_buffer_pool_release(&prefix);
117  }
118 }
119 
128 char *mutt_expand_path(char *buf, size_t buflen)
129 {
130  return mutt_expand_path_regex(buf, buflen, false);
131 }
132 
140 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
141 {
142  const char *s = NULL;
143  const char *tail = "";
144 
145  bool recurse = false;
146 
147  struct Buffer *p = mutt_buffer_pool_get();
148  struct Buffer *q = mutt_buffer_pool_get();
149  struct Buffer *tmp = mutt_buffer_pool_get();
150 
151  do
152  {
153  recurse = false;
154  s = mutt_b2s(buf);
155 
156  switch (*s)
157  {
158  case '~':
159  {
160  if ((s[1] == '/') || (s[1] == '\0'))
161  {
163  tail = s + 1;
164  }
165  else
166  {
167  char *t = strchr(s + 1, '/');
168  if (t)
169  *t = '\0';
170 
171  struct passwd *pw = getpwnam(s + 1);
172  if (pw)
173  {
174  mutt_buffer_strcpy(p, pw->pw_dir);
175  if (t)
176  {
177  *t = '/';
178  tail = t;
179  }
180  else
181  tail = "";
182  }
183  else
184  {
185  /* user not found! */
186  if (t)
187  *t = '/';
189  tail = s;
190  }
191  }
192  break;
193  }
194 
195  case '=':
196  case '+':
197  {
198  enum MailboxType mb_type = mx_path_probe(C_Folder);
199 
200  /* if folder = {host} or imap[s]://host/: don't append slash */
201  if ((mb_type == MUTT_IMAP) && ((C_Folder[strlen(C_Folder) - 1] == '}') ||
202  (C_Folder[strlen(C_Folder) - 1] == '/')))
203  {
205  }
206  else if (mb_type == MUTT_NOTMUCH)
208  else if (C_Folder && (C_Folder[strlen(C_Folder) - 1] == '/'))
210  else
211  mutt_buffer_printf(p, "%s/", NONULL(C_Folder));
212 
213  tail = s + 1;
214  break;
215  }
216 
217  /* elm compatibility, @ expands alias to user name */
218 
219  case '@':
220  {
221  struct AddressList *al = alias_lookup(s + 1);
222  if (al && !TAILQ_EMPTY(al))
223  {
224  struct Email *e = email_new();
225  e->env = mutt_env_new();
226  mutt_addrlist_copy(&e->env->from, al, false);
227  mutt_addrlist_copy(&e->env->to, al, false);
228 
229  /* TODO: fix mutt_default_save() to use Buffer */
231  mutt_default_save(p->data, p->dsize, e);
233 
234  email_free(&e);
235  /* Avoid infinite recursion if the resulting folder starts with '@' */
236  if (*p->data != '@')
237  recurse = true;
238 
239  tail = "";
240  }
241  break;
242  }
243 
244  case '>':
245  {
247  tail = s + 1;
248  break;
249  }
250 
251  case '<':
252  {
254  tail = s + 1;
255  break;
256  }
257 
258  case '!':
259  {
260  if (s[1] == '!')
261  {
263  tail = s + 2;
264  }
265  else
266  {
268  tail = s + 1;
269  }
270  break;
271  }
272 
273  case '-':
274  {
276  tail = s + 1;
277  break;
278  }
279 
280  case '^':
281  {
283  tail = s + 1;
284  break;
285  }
286 
287  default:
288  {
290  tail = s;
291  }
292  }
293 
294  if (regex && *(mutt_b2s(p)) && !recurse)
295  {
297  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(q), tail);
298  }
299  else
300  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(p), tail);
301 
302  mutt_buffer_copy(buf, tmp);
303  } while (recurse);
304 
308 
309 #ifdef USE_IMAP
310  /* Rewrite IMAP path in canonical form - aids in string comparisons of
311  * folders. May possibly fail, in which case buf should be the same. */
312  if (imap_path_probe(mutt_b2s(buf), NULL) == MUTT_IMAP)
313  imap_expand_path(buf);
314 #endif
315 }
316 
324 {
325  mutt_buffer_expand_path_regex(buf, false);
326 }
327 
337 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
338 {
339  struct Buffer *tmp = mutt_buffer_pool_get();
340 
341  mutt_buffer_addstr(tmp, NONULL(buf));
342  mutt_buffer_expand_path_regex(tmp, regex);
343  mutt_str_copy(buf, mutt_b2s(tmp), buflen);
344 
346 
347  return buf;
348 }
349 
362 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
363 {
364  regmatch_t pat_match[1];
365  size_t pwnl;
366  char *p = NULL;
367 
368  if (!pw || !pw->pw_gecos)
369  return NULL;
370 
371  memset(dest, 0, destlen);
372 
373  if (mutt_regex_capture(C_GecosMask, pw->pw_gecos, 1, pat_match))
374  {
375  mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
376  MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
377  }
378  else if ((p = strchr(pw->pw_gecos, ',')))
379  mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
380  else
381  mutt_str_copy(dest, pw->pw_gecos, destlen);
382 
383  pwnl = strlen(pw->pw_name);
384 
385  for (int idx = 0; dest[idx]; idx++)
386  {
387  if (dest[idx] == '&')
388  {
389  memmove(&dest[idx + pwnl], &dest[idx + 1],
390  MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
391  memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
392  dest[idx] = toupper((unsigned char) dest[idx]);
393  }
394  }
395 
396  return dest;
397 }
398 
405 bool mutt_needs_mailcap(struct Body *m)
406 {
407  switch (m->type)
408  {
409  case TYPE_TEXT:
410  if (mutt_istr_equal("plain", m->subtype))
411  return false;
412  break;
413  case TYPE_APPLICATION:
415  return false;
417  return false;
418  break;
419 
420  case TYPE_MULTIPART:
421  case TYPE_MESSAGE:
422  return false;
423  }
424 
425  return true;
426 }
427 
433 bool mutt_is_text_part(struct Body *b)
434 {
435  int t = b->type;
436  char *s = b->subtype;
437 
439  return false;
440 
441  if (t == TYPE_TEXT)
442  return true;
443 
444  if (t == TYPE_MESSAGE)
445  {
446  if (mutt_istr_equal("delivery-status", s))
447  return true;
448  }
449 
450  if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
451  {
452  if (mutt_istr_equal("pgp-keys", s))
453  return true;
454  }
455 
456  return false;
457 }
458 
467 void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix,
468  const char *suffix, const char *src, int line)
469 {
470  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
471  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
472  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
473 
474  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, mutt_b2s(buf));
475  if (unlink(mutt_b2s(buf)) && (errno != ENOENT))
476  {
477  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
478  line, mutt_b2s(buf), strerror(errno), errno);
479  }
480 }
481 
493 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
494  const char *suffix, const char *src, int line)
495 {
496  size_t n =
497  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
498  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
499  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
500  if (n >= buflen)
501  {
503  "%s:%d: ERROR: insufficient buffer space to hold temporary "
504  "filename! buflen=%zu but need %zu\n",
505  src, line, buflen, n);
506  }
507  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
508  if ((unlink(buf) != 0) && (errno != ENOENT))
509  {
510  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
511  line, buf, strerror(errno), errno);
512  }
513 }
514 
522 void mutt_pretty_mailbox(char *buf, size_t buflen)
523 {
524  if (!buf)
525  return;
526 
527  char *p = buf, *q = buf;
528  size_t len;
529  enum UrlScheme scheme;
530  char tmp[PATH_MAX];
531 
532  scheme = url_check_scheme(buf);
533 
534  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
535  {
536  imap_pretty_mailbox(buf, buflen, C_Folder);
537  return;
538  }
539 
540  if (scheme == U_NOTMUCH)
541  return;
542 
543  /* if buf is an url, only collapse path component */
544  if (scheme != U_UNKNOWN)
545  {
546  p = strchr(buf, ':') + 1;
547  if (mutt_strn_equal(p, "//", 2))
548  q = strchr(p + 2, '/');
549  if (!q)
550  q = strchr(p, '\0');
551  p = q;
552  }
553 
554  /* cleanup path */
555  if (strstr(p, "//") || strstr(p, "/./"))
556  {
557  /* first attempt to collapse the pathname, this is more
558  * lightweight than realpath() and doesn't resolve links */
559  while (*p)
560  {
561  if ((p[0] == '/') && (p[1] == '/'))
562  {
563  *q++ = '/';
564  p += 2;
565  }
566  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
567  {
568  *q++ = '/';
569  p += 3;
570  }
571  else
572  *q++ = *p++;
573  }
574  *q = '\0';
575  }
576  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
577  realpath(p, tmp))
578  {
579  mutt_str_copy(p, tmp, buflen - (p - buf));
580  }
581 
582  if ((len = mutt_str_startswith(buf, C_Folder)) && (buf[len] == '/'))
583  {
584  *buf++ = '=';
585  memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
586  }
587  else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
588  {
589  *buf++ = '~';
590  memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
591  }
592 }
593 
599 {
600  if (!buf || !buf->data)
601  return;
602  /* This reduces the size of the Buffer, so we can pass it through.
603  * We adjust the size just to make sure buf->data is not NULL though */
605  mutt_pretty_mailbox(buf->data, buf->dsize);
607 }
608 
620 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
621  enum SaveAttach *opt, char **directory)
622 {
623  struct stat st;
624 
625  mutt_buffer_strcpy(fname, path);
626  if (access(mutt_b2s(fname), F_OK) != 0)
627  return 0;
628  if (stat(mutt_b2s(fname), &st) != 0)
629  return -1;
630  if (S_ISDIR(st.st_mode))
631  {
632  enum QuadOption ans = MUTT_NO;
633  if (directory)
634  {
635  switch (mutt_multi_choice
636  /* L10N: Means "The path you specified as the destination file is a directory."
637  See the msgid "Save to file: " (alias.c, recvattach.c)
638  These three letters correspond to the choices in the string. */
639  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
640  {
641  case 3: /* all */
642  mutt_str_replace(directory, mutt_b2s(fname));
643  break;
644  case 1: /* yes */
645  FREE(directory);
646  break;
647  case -1: /* abort */
648  FREE(directory);
649  return -1;
650  case 2: /* no */
651  FREE(directory);
652  return 1;
653  }
654  }
655  /* L10N: Means "The path you specified as the destination file is a directory."
656  See the msgid "Save to file: " (alias.c, recvattach.c) */
657  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
658  return (ans == MUTT_NO) ? 1 : -1;
659 
660  struct Buffer *tmp = mutt_buffer_pool_get();
662  if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_FILE | MUTT_CLEAR) != 0) ||
664  {
666  return (-1);
667  }
668  mutt_buffer_concat_path(fname, path, mutt_b2s(tmp));
670  }
671 
672  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_b2s(fname), F_OK) == 0))
673  {
674  switch (
675  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
676  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
677  _("oac")))
678  {
679  case -1: /* abort */
680  return -1;
681  case 3: /* cancel */
682  return 1;
683 
684  case 2: /* append */
685  *opt = MUTT_SAVE_APPEND;
686  break;
687  case 1: /* overwrite */
688  *opt = MUTT_SAVE_OVERWRITE;
689  break;
690  }
691  }
692  return 0;
693 }
694 
704 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
705 {
706  if (addr && addr->mailbox)
707  {
708  mutt_str_copy(buf, addr->mailbox, buflen);
709  if (!C_SaveAddress)
710  {
711  char *p = strpbrk(buf, "%@");
712  if (p)
713  *p = '\0';
714  }
715  mutt_str_lower(buf);
716  }
717  else
718  *buf = '\0';
719 }
720 
726 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
727 {
728  if (a && a->mailbox)
729  {
730  mutt_buffer_strcpy(dest, a->mailbox);
731  if (!C_SaveAddress)
732  {
733  char *p = strpbrk(dest->data, "%@");
734  if (p)
735  {
736  *p = '\0';
737  mutt_buffer_fix_dptr(dest);
738  }
739  }
740  mutt_str_lower(dest->data);
741  }
742  else
743  mutt_buffer_reset(dest);
744 }
745 
753 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
754 {
755  mutt_buffer_save_path(dest, a);
756  for (char *p = dest->data; *p; p++)
757  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
758  *p = '_';
759 }
760 
772 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
773  format_t callback, intptr_t data, MuttFormatFlags flags)
774 {
775  char prefix[128], tmp[1024];
776  char *cp = NULL, *wptr = buf;
777  char ch;
778  char if_str[128], else_str[128];
779  size_t wlen, count, len, wid;
780  FILE *fp_filter = NULL;
781  char *recycler = NULL;
782 
783  char src2[256];
784  mutt_str_copy(src2, src, mutt_str_len(src) + 1);
785  src = src2;
786 
787  prefix[0] = '\0';
788  buflen--; /* save room for the terminal \0 */
789  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
791  0;
792  col += wlen;
793 
794  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
795  {
796  int off = -1;
797 
798  /* Do not consider filters if no pipe at end */
799  int n = mutt_str_len(src);
800  if ((n > 1) && (src[n - 1] == '|'))
801  {
802  /* Scan backwards for backslashes */
803  off = n;
804  while ((off > 0) && (src[off - 2] == '\\'))
805  off--;
806  }
807 
808  /* If number of backslashes is even, the pipe is real. */
809  /* n-off is the number of backslashes. */
810  if ((off > 0) && (((n - off) % 2) == 0))
811  {
812  char srccopy[1024];
813  int i = 0;
814 
815  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
816 
817  strncpy(srccopy, src, n);
818  srccopy[n - 1] = '\0';
819 
820  /* prepare Buffers */
821  struct Buffer srcbuf = mutt_buffer_make(0);
822  mutt_buffer_addstr(&srcbuf, srccopy);
823  /* note: we are resetting dptr and *reading* from the buffer, so we don't
824  * want to use mutt_buffer_reset(). */
825  srcbuf.dptr = srcbuf.data;
826  struct Buffer word = mutt_buffer_make(0);
827  struct Buffer cmd = mutt_buffer_make(0);
828 
829  /* Iterate expansions across successive arguments */
830  do
831  {
832  /* Extract the command name and copy to command line */
833  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
834  if (word.data)
835  *word.data = '\0';
836  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
837  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
838  mutt_buffer_addch(&cmd, '\'');
839  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
840  data, flags | MUTT_FORMAT_NOFILTER);
841  for (char *p = tmp; p && (*p != '\0'); p++)
842  {
843  if (*p == '\'')
844  {
845  /* shell quoting doesn't permit escaping a single quote within
846  * single-quoted material. double-quoting instead will lead
847  * shell variable expansions, so break out of the single-quoted
848  * span, insert a double-quoted single quote, and resume. */
849  mutt_buffer_addstr(&cmd, "'\"'\"'");
850  }
851  else
852  mutt_buffer_addch(&cmd, *p);
853  }
854  mutt_buffer_addch(&cmd, '\'');
855  mutt_buffer_addch(&cmd, ' ');
856  } while (MoreArgs(&srcbuf));
857 
858  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
859 
860  col -= wlen; /* reset to passed in value */
861  wptr = buf; /* reset write ptr */
862  pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
863  if (pid != -1)
864  {
865  int rc;
866 
867  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
868  mutt_file_fclose(&fp_filter);
869  rc = filter_wait(pid);
870  if (rc != 0)
871  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
872  if (n > 0)
873  {
874  buf[n] = '\0';
875  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
876  buf[--n] = '\0';
877  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
878 
879  /* If the result ends with '%', this indicates that the filter
880  * generated %-tokens that neomutt can expand. Eliminate the '%'
881  * marker and recycle the string through mutt_expando_format().
882  * To literally end with "%", use "%%". */
883  if ((n > 0) && (buf[n - 1] == '%'))
884  {
885  n--;
886  buf[n] = '\0'; /* remove '%' */
887  if ((n > 0) && (buf[n - 1] != '%'))
888  {
889  recycler = mutt_str_dup(buf);
890  if (recycler)
891  {
892  /* buflen is decremented at the start of this function
893  * to save space for the terminal nul char. We can add
894  * it back for the recursive call since the expansion of
895  * format pipes does not try to append a nul itself. */
896  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
897  callback, data, flags);
898  FREE(&recycler);
899  }
900  }
901  }
902  }
903  else
904  {
905  /* read error */
906  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
907  strerror(errno), errno);
908  *wptr = '\0';
909  }
910  }
911  else
912  {
913  /* Filter failed; erase write buffer */
914  *wptr = '\0';
915  }
916 
917  mutt_buffer_dealloc(&cmd);
918  mutt_buffer_dealloc(&srcbuf);
919  mutt_buffer_dealloc(&word);
920  return;
921  }
922  }
923 
924  while (*src && (wlen < buflen))
925  {
926  if (*src == '%')
927  {
928  if (*++src == '%')
929  {
930  *wptr++ = '%';
931  wlen++;
932  col++;
933  src++;
934  continue;
935  }
936 
937  if (*src == '?')
938  {
939  /* change original %? to new %< notation */
940  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
941  char *p = (char *) src;
942  *p = '<';
943  /* skip over "x" */
944  for (; *p && (*p != '?'); p++)
945  ; // do nothing
946 
947  /* nothing */
948  if (*p == '?')
949  p++;
950  /* fix up the "y&z" section */
951  for (; *p && (*p != '?'); p++)
952  {
953  /* escape '<' and '>' to work inside nested-if */
954  if ((*p == '<') || (*p == '>'))
955  {
956  memmove(p + 2, p, mutt_str_len(p) + 1);
957  *p++ = '\\';
958  *p++ = '\\';
959  }
960  }
961  if (*p == '?')
962  *p = '>';
963  }
964 
965  if (*src == '<')
966  {
967  flags |= MUTT_FORMAT_OPTIONAL;
968  ch = *(++src); /* save the character to switch on */
969  src++;
970  cp = prefix;
971  count = 0;
972  while ((count < sizeof(prefix)) && (*src != '?'))
973  {
974  *cp++ = *src++;
975  count++;
976  }
977  *cp = '\0';
978  }
979  else
980  {
981  flags &= ~MUTT_FORMAT_OPTIONAL;
982 
983  /* eat the format string */
984  cp = prefix;
985  count = 0;
986  while ((count < sizeof(prefix)) && (isdigit((unsigned char) *src) || (*src == '.') ||
987  (*src == '-') || (*src == '=')))
988  {
989  *cp++ = *src++;
990  count++;
991  }
992  *cp = '\0';
993 
994  if (*src == '\0')
995  break; /* bad format */
996 
997  ch = *src++; /* save the character to switch on */
998  }
999 
1000  if (flags & MUTT_FORMAT_OPTIONAL)
1001  {
1002  int lrbalance;
1003 
1004  if (*src != '?')
1005  break; /* bad format */
1006  src++;
1007 
1008  /* eat the 'if' part of the string */
1009  cp = if_str;
1010  count = 0;
1011  lrbalance = 1;
1012  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1013  {
1014  if ((src[0] == '%') && (src[1] == '>'))
1015  {
1016  /* This is a padding expando; copy two chars and carry on */
1017  *cp++ = *src++;
1018  *cp++ = *src++;
1019  count += 2;
1020  continue;
1021  }
1022 
1023  if (*src == '\\')
1024  {
1025  src++;
1026  *cp++ = *src++;
1027  }
1028  else if ((src[0] == '%') && (src[1] == '<'))
1029  {
1030  lrbalance++;
1031  }
1032  else if (src[0] == '>')
1033  {
1034  lrbalance--;
1035  }
1036  if (lrbalance == 0)
1037  break;
1038  if ((lrbalance == 1) && (src[0] == '&'))
1039  break;
1040  *cp++ = *src++;
1041  count++;
1042  }
1043  *cp = '\0';
1044 
1045  /* eat the 'else' part of the string (optional) */
1046  if (*src == '&')
1047  src++; /* skip the & */
1048  cp = else_str;
1049  count = 0;
1050  while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1051  {
1052  if ((src[0] == '%') && (src[1] == '>'))
1053  {
1054  /* This is a padding expando; copy two chars and carry on */
1055  *cp++ = *src++;
1056  *cp++ = *src++;
1057  count += 2;
1058  continue;
1059  }
1060 
1061  if (*src == '\\')
1062  {
1063  src++;
1064  *cp++ = *src++;
1065  }
1066  else if ((src[0] == '%') && (src[1] == '<'))
1067  {
1068  lrbalance++;
1069  }
1070  else if (src[0] == '>')
1071  {
1072  lrbalance--;
1073  }
1074  if (lrbalance == 0)
1075  break;
1076  if ((lrbalance == 1) && (src[0] == '&'))
1077  break;
1078  *cp++ = *src++;
1079  count++;
1080  }
1081  *cp = '\0';
1082 
1083  if ((*src == '\0'))
1084  break; /* bad format */
1085 
1086  src++; /* move past the trailing '>' (formerly '?') */
1087  }
1088 
1089  /* handle generic cases first */
1090  if ((ch == '>') || (ch == '*'))
1091  {
1092  /* %>X: right justify to EOL, left takes precedence
1093  * %*X: right justify to EOL, right takes precedence */
1094  int soft = ch == '*';
1095  int pl, pw;
1096  pl = mutt_mb_charlen(src, &pw);
1097  if (pl <= 0)
1098  {
1099  pl = 1;
1100  pw = 1;
1101  }
1102 
1103  /* see if there's room to add content, else ignore */
1104  if (((col < cols) && (wlen < buflen)) || soft)
1105  {
1106  int pad;
1107 
1108  /* get contents after padding */
1109  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1110  len = mutt_str_len(tmp);
1111  wid = mutt_strwidth(tmp);
1112 
1113  pad = (cols - col - wid) / pw;
1114  if (pad >= 0)
1115  {
1116  /* try to consume as many columns as we can, if we don't have
1117  * memory for that, use as much memory as possible */
1118  if (wlen + (pad * pl) + len > buflen)
1119  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1120  else
1121  {
1122  /* Add pre-spacing to make multi-column pad characters and
1123  * the contents after padding line up */
1124  while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1125  {
1126  *wptr++ = ' ';
1127  wlen++;
1128  col++;
1129  }
1130  }
1131  while (pad-- > 0)
1132  {
1133  memcpy(wptr, src, pl);
1134  wptr += pl;
1135  wlen += pl;
1136  col += pw;
1137  }
1138  }
1139  else if (soft)
1140  {
1141  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
1143  0;
1144  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1145  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1146  *wptr = '\0';
1147  /* make sure right part is at most as wide as display */
1148  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1149  /* truncate left so that right part fits completely in */
1150  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1151  wptr = buf + wlen;
1152  /* Multi-column characters may be truncated in the middle.
1153  * Add spacing so the right hand side lines up. */
1154  while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1155  {
1156  *wptr++ = ' ';
1157  wlen++;
1158  col++;
1159  }
1160  }
1161  if ((len + wlen) > buflen)
1162  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1163  memcpy(wptr, tmp, len);
1164  wptr += len;
1165  }
1166  break; /* skip rest of input */
1167  }
1168  else if (ch == '|')
1169  {
1170  /* pad to EOL */
1171  int pl, pw;
1172  pl = mutt_mb_charlen(src, &pw);
1173  if (pl <= 0)
1174  {
1175  pl = 1;
1176  pw = 1;
1177  }
1178 
1179  /* see if there's room to add content, else ignore */
1180  if ((col < cols) && (wlen < buflen))
1181  {
1182  int c = (cols - col) / pw;
1183  if ((c > 0) && ((wlen + (c * pl)) > buflen))
1184  c = ((signed) (buflen - wlen)) / pl;
1185  while (c > 0)
1186  {
1187  memcpy(wptr, src, pl);
1188  wptr += pl;
1189  wlen += pl;
1190  col += pw;
1191  c--;
1192  }
1193  }
1194  break; /* skip rest of input */
1195  }
1196  else
1197  {
1198  bool to_lower = false;
1199  bool no_dots = false;
1200 
1201  while ((ch == '_') || (ch == ':'))
1202  {
1203  if (ch == '_')
1204  to_lower = true;
1205  else if (ch == ':')
1206  no_dots = true;
1207 
1208  ch = *src++;
1209  }
1210 
1211  /* use callback function to handle this case */
1212  *tmp = '\0';
1213  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1214  else_str, data, flags);
1215 
1216  if (to_lower)
1217  mutt_str_lower(tmp);
1218  if (no_dots)
1219  {
1220  char *p = tmp;
1221  for (; *p; p++)
1222  if (*p == '.')
1223  *p = '_';
1224  }
1225 
1226  len = mutt_str_len(tmp);
1227  if ((len + wlen) > buflen)
1228  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1229 
1230  memcpy(wptr, tmp, len);
1231  wptr += len;
1232  wlen += len;
1233  col += mutt_strwidth(tmp);
1234  }
1235  }
1236  else if (*src == '\\')
1237  {
1238  if (!*++src)
1239  break;
1240  switch (*src)
1241  {
1242  case 'f':
1243  *wptr = '\f';
1244  break;
1245  case 'n':
1246  *wptr = '\n';
1247  break;
1248  case 'r':
1249  *wptr = '\r';
1250  break;
1251  case 't':
1252  *wptr = '\t';
1253  break;
1254  case 'v':
1255  *wptr = '\v';
1256  break;
1257  default:
1258  *wptr = *src;
1259  break;
1260  }
1261  src++;
1262  wptr++;
1263  wlen++;
1264  col++;
1265  }
1266  else
1267  {
1268  int bytes, width;
1269  /* in case of error, simply copy byte */
1270  bytes = mutt_mb_charlen(src, &width);
1271  if (bytes < 0)
1272  {
1273  bytes = 1;
1274  width = 1;
1275  }
1276  if ((bytes > 0) && ((wlen + bytes) < buflen))
1277  {
1278  memcpy(wptr, src, bytes);
1279  wptr += bytes;
1280  src += bytes;
1281  wlen += bytes;
1282  col += width;
1283  }
1284  else
1285  {
1286  src += buflen - wlen;
1287  wlen = buflen;
1288  }
1289  }
1290  }
1291  *wptr = '\0';
1292 }
1293 
1304 FILE *mutt_open_read(const char *path, pid_t *thepid)
1305 {
1306  FILE *fp = NULL;
1307  struct stat s;
1308 
1309  size_t len = mutt_str_len(path);
1310  if (len == 0)
1311  {
1312  return NULL;
1313  }
1314 
1315  if (path[len - 1] == '|')
1316  {
1317  /* read from a pipe */
1318 
1319  char *p = mutt_str_dup(path);
1320 
1321  p[len - 1] = 0;
1322  mutt_endwin();
1323  *thepid = filter_create(p, NULL, &fp, NULL);
1324  FREE(&p);
1325  }
1326  else
1327  {
1328  if (stat(path, &s) < 0)
1329  return NULL;
1330  if (S_ISDIR(s.st_mode))
1331  {
1332  errno = EINVAL;
1333  return NULL;
1334  }
1335  fp = fopen(path, "r");
1336  *thepid = -1;
1337  }
1338  return fp;
1339 }
1340 
1349 int mutt_save_confirm(const char *s, struct stat *st)
1350 {
1351  int ret = 0;
1352 
1353  enum MailboxType type = mx_path_probe(s);
1354 
1355 #ifdef USE_POP
1356  if (type == MUTT_POP)
1357  {
1358  mutt_error(_("Can't save message to POP mailbox"));
1359  return 1;
1360  }
1361 #endif
1362 
1363  if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1364  {
1365  if (C_Confirmappend)
1366  {
1367  struct Buffer *tmp = mutt_buffer_pool_get();
1368  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1369  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1370  if (ans == MUTT_NO)
1371  ret = 1;
1372  else if (ans == MUTT_ABORT)
1373  ret = -1;
1375  }
1376  }
1377 
1378 #ifdef USE_NNTP
1379  if (type == MUTT_NNTP)
1380  {
1381  mutt_error(_("Can't save message to news server"));
1382  return 0;
1383  }
1384 #endif
1385 
1386  if (stat(s, st) != -1)
1387  {
1388  if (type == MUTT_MAILBOX_ERROR)
1389  {
1390  mutt_error(_("%s is not a mailbox"), s);
1391  return 1;
1392  }
1393  }
1394  else if (type != MUTT_IMAP)
1395  {
1396  st->st_mtime = 0;
1397  st->st_atime = 0;
1398 
1399  /* pathname does not exist */
1400  if (errno == ENOENT)
1401  {
1402  if (C_Confirmcreate)
1403  {
1404  struct Buffer *tmp = mutt_buffer_pool_get();
1405  mutt_buffer_printf(tmp, _("Create %s?"), s);
1406  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1407  if (ans == MUTT_NO)
1408  ret = 1;
1409  else if (ans == MUTT_ABORT)
1410  ret = -1;
1412  }
1413 
1414  /* user confirmed with MUTT_YES or set C_Confirmcreate */
1415  if (ret == 0)
1416  {
1417  /* create dir recursively */
1418  char *tmp_path = mutt_path_dirname(s);
1419  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1420  {
1421  /* report failure & abort */
1422  mutt_perror(s);
1423  FREE(&tmp_path);
1424  return 1;
1425  }
1426  FREE(&tmp_path);
1427  }
1428  }
1429  else
1430  {
1431  mutt_perror(s);
1432  return 1;
1433  }
1434  }
1435 
1437  return ret;
1438 }
1439 
1446 void mutt_sleep(short s)
1447 {
1448  if (C_SleepTime > s)
1449  sleep(C_SleepTime);
1450  else if (s)
1451  sleep(s);
1452 }
1453 
1460 const char *mutt_make_version(void)
1461 {
1462  static char vstring[256];
1463  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1464  return vstring;
1465 }
1466 
1474 void mutt_encode_path(struct Buffer *buf, const char *src)
1475 {
1476  char *p = mutt_str_dup(src);
1477  int rc = mutt_ch_convert_string(&p, C_Charset, "us-ascii", 0);
1478  size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1479 
1480  /* convert the path to POSIX "Portable Filename Character Set" */
1481  for (size_t i = 0; i < len; i++)
1482  {
1483  if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1484  {
1485  buf->data[i] = '_';
1486  }
1487  }
1488  FREE(&p);
1489 }
1490 
1499 int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1500 {
1501  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1502  char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(xdg_defaults[type]);
1503  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1504  char *token = NULL;
1505  int rc = 0;
1506 
1507  while ((token = strsep(&xdg, ":")))
1508  {
1509  if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1510  continue;
1512  if (access(mutt_b2s(buf), F_OK) == 0)
1513  {
1514  rc = 1;
1515  break;
1516  }
1517 
1518  if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1519  continue;
1521  if (access(mutt_b2s(buf), F_OK) == 0)
1522  {
1523  rc = 1;
1524  break;
1525  }
1526  }
1527 
1528  FREE(&x);
1529  return rc;
1530 }
1531 
1538 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1539 {
1540  enum MailboxType mb_type = mx_path_probe(path);
1541 
1542  if (mb_type == MUTT_IMAP)
1543  imap_get_parent_path(path, buf, buflen);
1544  else if (mb_type == MUTT_NOTMUCH)
1545  mutt_str_copy(buf, C_Folder, buflen);
1546  else
1547  {
1548  mutt_str_copy(buf, path, buflen);
1549  int n = mutt_str_len(buf);
1550  if (n == 0)
1551  return;
1552 
1553  /* remove any final trailing '/' */
1554  if (buf[n - 1] == '/')
1555  buf[n - 1] = '\0';
1556 
1557  /* Remove everything until the next slash */
1558  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1559  ; // do nothing
1560 
1561  if (n > 0)
1562  buf[n] = '\0';
1563  else
1564  {
1565  buf[0] = '/';
1566  buf[1] = '\0';
1567  }
1568  }
1569 }
1570 
1593 int mutt_inbox_cmp(const char *a, const char *b)
1594 {
1595  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1596  if ((a[0] == '+') && (b[0] == '+'))
1597  {
1598  return mutt_istr_equal(a + 1, "inbox") ? -1 : mutt_istr_equal(b + 1, "inbox") ? 1 : 0;
1599  }
1600 
1601  const char *a_end = strrchr(a, '/');
1602  const char *b_end = strrchr(b, '/');
1603 
1604  /* If one path contains a '/', but not the other */
1605  if ((!a_end) ^ (!b_end))
1606  return 0;
1607 
1608  /* If neither path contains a '/' */
1609  if (!a_end)
1610  return 0;
1611 
1612  /* Compare the subpaths */
1613  size_t a_len = a_end - a;
1614  size_t b_len = b_end - b;
1615  size_t min = MIN(a_len, b_len);
1616  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1617  (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1618 
1619  if (!same)
1620  return 0;
1621 
1622  if (mutt_istr_equal(&a[min + 1], "inbox"))
1623  return -1;
1624 
1625  if (mutt_istr_equal(&b[min + 1], "inbox"))
1626  return 1;
1627 
1628  return 0;
1629 }
1630 
1637 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1638 {
1639  if (!buf || !path)
1640  return;
1641 
1642  mutt_buffer_reset(buf);
1643 
1644  for (; *path; path++)
1645  {
1646  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1647  mutt_buffer_addch(buf, '_');
1648  else
1649  mutt_buffer_addch(buf, *path);
1650  }
1651 }
1652 
1659 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1660 {
1661  if (!buf || (buflen == 0))
1662  return;
1663 
1664  if (C_SizeShowBytes && (num < 1024))
1665  {
1666  snprintf(buf, buflen, "%d", (int) num);
1667  }
1668  else if (num == 0)
1669  {
1670  mutt_str_copy(buf, C_SizeUnitsOnLeft ? "K0" : "0K", buflen);
1671  }
1672  else if (C_SizeShowFractions && (num < 10189)) /* 0.1K - 9.9K */
1673  {
1674  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "K%3.1f" : "%3.1fK",
1675  (num < 103) ? 0.1 : (num / 1024.0));
1676  }
1677  else if (!C_SizeShowMb || (num < 1023949)) /* 10K - 999K */
1678  {
1679  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1680  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1681  }
1682  else if (C_SizeShowFractions && (num < 10433332)) /* 1.0M - 9.9M */
1683  {
1684  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1685  }
1686  else /* 10M+ */
1687  {
1688  /* (10433332 + 52428) / 1048576 = 10 */
1689  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1690  }
1691 }
1692 
1700 void add_to_stailq(struct ListHead *head, const char *str)
1701 {
1702  /* don't add a NULL or empty string to the list */
1703  if (!str || (*str == '\0'))
1704  return;
1705 
1706  /* check to make sure the item is not already on this list */
1707  struct ListNode *np = NULL;
1708  STAILQ_FOREACH(np, head, entries)
1709  {
1710  if (mutt_istr_equal(str, np->data))
1711  {
1712  return;
1713  }
1714  }
1715  mutt_list_insert_tail(head, mutt_str_dup(str));
1716 }
1717 
1725 void remove_from_stailq(struct ListHead *head, const char *str)
1726 {
1727  if (mutt_str_equal("*", str))
1728  mutt_list_free(head); /* "unCMD *" means delete all current entries */
1729  else
1730  {
1731  struct ListNode *np = NULL, *tmp = NULL;
1732  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1733  {
1734  if (mutt_istr_equal(str, np->data))
1735  {
1736  STAILQ_REMOVE(head, np, ListNode, entries);
1737  FREE(&np->data);
1738  FREE(&np);
1739  break;
1740  }
1741  }
1742  }
1743 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
Convenience wrapper for the gui headers.
WHERE bool C_SizeUnitsOnLeft
Config: Show the units as a prefix to the size.
Definition: mutt_globals.h:162
Url is notmuch://.
Definition: url.h:45
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
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
#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
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:123
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:2385
Config/command parsing.
int mutt_ch_convert_string(char **ps, const char *from, const char *to, int flags)
Convert a string between encodings.
Definition: charset.c:754
&#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
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:78
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:522
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:737
#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
WHERE short C_SleepTime
Config: Time to pause after certain info messages.
Definition: mutt_globals.h:114
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
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
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:725
char * mailbox
Mailbox and host address.
Definition: address.h:37
Url is imap://.
Definition: url.h:38
XDG home dir: ~/.config.
Definition: protos.h:47
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:726
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
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:654
Flags to control mutt_expando_format()
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, intptr_t data, MuttFormatFlags flags)
Prototype for a mutt_expando_format() callback function.
Definition: format_flags.h:61
static const char * xdg_env_vars[]
Definition: muttlib.c:72
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:598
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1349
The body of an email.
Definition: body.h:34
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
Convenience wrapper for the config headers.
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:49
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:991
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:140
bool mutt_istrn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
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:337
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:128
#define MoreArgs(buf)
Definition: buffer.h:43
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1446
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:379
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2431
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:493
Parse and execute user-defined hooks.
Many unsorted constants and some structs.
API for mailboxes.
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:299
UrlScheme
All recognised Url types.
Definition: url.h:32
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:276
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:875
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:232
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
Email Aliases.
User aborted the question (with Ctrl-G)
Definition: quad.h:38
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
char * mutt_str_lower(char *s)
Convert all characters in the string to lowercase.
Definition: string.c:504
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:753
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:64
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:98
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:933
Prototypes for many functions.
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1700
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:552
WHERE bool C_SizeShowBytes
Config: Show smaller sizes in bytes.
Definition: mutt_globals.h:159
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:47
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:571
WHERE bool C_SaveAddress
Config: Use sender&#39;s full address as a default save folder.
Definition: mutt_globals.h:156
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:129
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1359
#define PATH_MAX
Definition: mutt.h:44
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:394
XdgType
XDG variable types.
Definition: protos.h:45
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
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1659
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:1593
char * data
Pointer to data.
Definition: buffer.h:35
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: mutt_globals.h:96
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:1309
API for encryption/signing of emails.
WHERE bool C_SizeShowFractions
Config: Show size fractions with a single decimal place.
Definition: mutt_globals.h:160
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: mutt_globals.h:142
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:610
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
WHERE bool C_SizeShowMb
Config: Show sizes in megabytes for sizes greater than 1 megabyte.
Definition: mutt_globals.h:161
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:55
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
WHERE bool C_ArrowCursor
Config: Use an arrow &#39;->&#39; instead of highlighting in the index.
Definition: mutt_globals.h:131
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:620
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1499
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:85
Type: &#39;message/*&#39;.
Definition: mime.h:35
bool mutt_strn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
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
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: mutt_globals.h:98
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1725
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1353
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:467
char * data
String.
Definition: list.h:36
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
WHERE char * C_Spoolfile
Config: Inbox.
Definition: mutt_globals.h:108
Log at debug level 1.
Definition: logging.h:40
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:362
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1637
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
Cached regular expression.
Definition: regex3.h:88
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:772
#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:405
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:56
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:54
Append to existing file.
Definition: mutt_attach.h:56
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:90
#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:183
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
Hundreds of global variables to back the user variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define TAILQ_EMPTY(head)
Definition: queue.h:714
Log at debug level 5.
Definition: logging.h:44
const char * GitVer
Convenience wrapper for the library headers.
A List node for strings.
Definition: list.h:34
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1304
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:48
static const char * xdg_defaults[]
Definition: muttlib.c:77
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: mutt_globals.h:94
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1538
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:198
Type: &#39;application/*&#39;.
Definition: mime.h:33
Log at debug level 3.
Definition: logging.h:42
WHERE char * C_ArrowString
Config: Use an custom string for arrow_cursor.
Definition: mutt_globals.h:132
Url is file://.
Definition: url.h:35
#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:1474
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:53
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1460
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
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:704
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_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: mutt_globals.h:141