NeoMutt  2021-02-05-89-gabe350
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_buffer_string(prefix));
102 
103  struct stat sb;
104  if ((lstat(mutt_buffer_string(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_buffer_string(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_buffer_string(p)) && !recurse)
295  {
297  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(q), tail);
298  }
299  else
300  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(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_buffer_string(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_buffer_string(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,
475  mutt_buffer_string(buf));
476  if (unlink(mutt_buffer_string(buf)) && (errno != ENOENT))
477  {
478  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
479  line, mutt_buffer_string(buf), strerror(errno), errno);
480  }
481 }
482 
494 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
495  const char *suffix, const char *src, int line)
496 {
497  size_t n =
498  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
499  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
500  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
501  if (n >= buflen)
502  {
504  "%s:%d: ERROR: insufficient buffer space to hold temporary "
505  "filename! buflen=%zu but need %zu\n",
506  src, line, buflen, n);
507  }
508  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
509  if ((unlink(buf) != 0) && (errno != ENOENT))
510  {
511  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
512  line, buf, strerror(errno), errno);
513  }
514 }
515 
523 void mutt_pretty_mailbox(char *buf, size_t buflen)
524 {
525  if (!buf)
526  return;
527 
528  char *p = buf, *q = buf;
529  size_t len;
530  enum UrlScheme scheme;
531  char tmp[PATH_MAX];
532 
533  scheme = url_check_scheme(buf);
534 
535  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
536  {
537  imap_pretty_mailbox(buf, buflen, C_Folder);
538  return;
539  }
540 
541  if (scheme == U_NOTMUCH)
542  return;
543 
544  /* if buf is an url, only collapse path component */
545  if (scheme != U_UNKNOWN)
546  {
547  p = strchr(buf, ':') + 1;
548  if (mutt_strn_equal(p, "//", 2))
549  q = strchr(p + 2, '/');
550  if (!q)
551  q = strchr(p, '\0');
552  p = q;
553  }
554 
555  /* cleanup path */
556  if (strstr(p, "//") || strstr(p, "/./"))
557  {
558  /* first attempt to collapse the pathname, this is more
559  * lightweight than realpath() and doesn't resolve links */
560  while (*p)
561  {
562  if ((p[0] == '/') && (p[1] == '/'))
563  {
564  *q++ = '/';
565  p += 2;
566  }
567  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
568  {
569  *q++ = '/';
570  p += 3;
571  }
572  else
573  *q++ = *p++;
574  }
575  *q = '\0';
576  }
577  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
578  realpath(p, tmp))
579  {
580  mutt_str_copy(p, tmp, buflen - (p - buf));
581  }
582 
583  if ((len = mutt_str_startswith(buf, C_Folder)) && (buf[len] == '/'))
584  {
585  *buf++ = '=';
586  memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
587  }
588  else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
589  {
590  *buf++ = '~';
591  memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
592  }
593 }
594 
600 {
601  if (!buf || !buf->data)
602  return;
603  /* This reduces the size of the Buffer, so we can pass it through.
604  * We adjust the size just to make sure buf->data is not NULL though */
606  mutt_pretty_mailbox(buf->data, buf->dsize);
608 }
609 
621 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
622  enum SaveAttach *opt, char **directory)
623 {
624  struct stat st;
625 
626  mutt_buffer_strcpy(fname, path);
627  if (access(mutt_buffer_string(fname), F_OK) != 0)
628  return 0;
629  if (stat(mutt_buffer_string(fname), &st) != 0)
630  return -1;
631  if (S_ISDIR(st.st_mode))
632  {
633  enum QuadOption ans = MUTT_NO;
634  if (directory)
635  {
636  switch (mutt_multi_choice
637  /* L10N: Means "The path you specified as the destination file is a directory."
638  See the msgid "Save to file: " (alias.c, recvattach.c)
639  These three letters correspond to the choices in the string. */
640  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
641  {
642  case 3: /* all */
643  mutt_str_replace(directory, mutt_buffer_string(fname));
644  break;
645  case 1: /* yes */
646  FREE(directory);
647  break;
648  case -1: /* abort */
649  FREE(directory);
650  return -1;
651  case 2: /* no */
652  FREE(directory);
653  return 1;
654  }
655  }
656  /* L10N: Means "The path you specified as the destination file is a directory."
657  See the msgid "Save to file: " (alias.c, recvattach.c) */
658  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
659  return (ans == MUTT_NO) ? 1 : -1;
660 
661  struct Buffer *tmp = mutt_buffer_pool_get();
663  if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_FILE | MUTT_CLEAR,
664  false, NULL, NULL, NULL) != 0) ||
666  {
668  return (-1);
669  }
670  mutt_buffer_concat_path(fname, path, mutt_buffer_string(tmp));
672  }
673 
674  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_buffer_string(fname), F_OK) == 0))
675  {
676  switch (
677  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
678  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
679  _("oac")))
680  {
681  case -1: /* abort */
682  return -1;
683  case 3: /* cancel */
684  return 1;
685 
686  case 2: /* append */
687  *opt = MUTT_SAVE_APPEND;
688  break;
689  case 1: /* overwrite */
690  *opt = MUTT_SAVE_OVERWRITE;
691  break;
692  }
693  }
694  return 0;
695 }
696 
706 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
707 {
708  if (addr && addr->mailbox)
709  {
710  mutt_str_copy(buf, addr->mailbox, buflen);
711  if (!C_SaveAddress)
712  {
713  char *p = strpbrk(buf, "%@");
714  if (p)
715  *p = '\0';
716  }
717  mutt_str_lower(buf);
718  }
719  else
720  *buf = '\0';
721 }
722 
728 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
729 {
730  if (a && a->mailbox)
731  {
732  mutt_buffer_strcpy(dest, a->mailbox);
733  if (!C_SaveAddress)
734  {
735  char *p = strpbrk(dest->data, "%@");
736  if (p)
737  {
738  *p = '\0';
739  mutt_buffer_fix_dptr(dest);
740  }
741  }
742  mutt_str_lower(dest->data);
743  }
744  else
745  mutt_buffer_reset(dest);
746 }
747 
755 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
756 {
757  mutt_buffer_save_path(dest, a);
758  for (char *p = dest->data; *p; p++)
759  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
760  *p = '_';
761 }
762 
774 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
775  format_t callback, intptr_t data, MuttFormatFlags flags)
776 {
777  char prefix[128], tmp[1024];
778  char *cp = NULL, *wptr = buf;
779  char ch;
780  char if_str[128], else_str[128];
781  size_t wlen, count, len, wid;
782  FILE *fp_filter = NULL;
783  char *recycler = NULL;
784 
785  char src2[256];
786  mutt_str_copy(src2, src, mutt_str_len(src) + 1);
787  src = src2;
788 
789  prefix[0] = '\0';
790  buflen--; /* save room for the terminal \0 */
791  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
793  0;
794  col += wlen;
795 
796  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
797  {
798  int off = -1;
799 
800  /* Do not consider filters if no pipe at end */
801  int n = mutt_str_len(src);
802  if ((n > 1) && (src[n - 1] == '|'))
803  {
804  /* Scan backwards for backslashes */
805  off = n;
806  while ((off > 0) && (src[off - 2] == '\\'))
807  off--;
808  }
809 
810  /* If number of backslashes is even, the pipe is real. */
811  /* n-off is the number of backslashes. */
812  if ((off > 0) && (((n - off) % 2) == 0))
813  {
814  char srccopy[1024];
815  int i = 0;
816 
817  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
818 
819  strncpy(srccopy, src, n);
820  srccopy[n - 1] = '\0';
821 
822  /* prepare Buffers */
823  struct Buffer srcbuf = mutt_buffer_make(0);
824  mutt_buffer_addstr(&srcbuf, srccopy);
825  /* note: we are resetting dptr and *reading* from the buffer, so we don't
826  * want to use mutt_buffer_reset(). */
827  mutt_buffer_seek(&srcbuf, 0);
828  struct Buffer word = mutt_buffer_make(0);
829  struct Buffer cmd = mutt_buffer_make(0);
830 
831  /* Iterate expansions across successive arguments */
832  do
833  {
834  /* Extract the command name and copy to command line */
835  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
836  if (word.data)
837  *word.data = '\0';
838  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
839  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
840  mutt_buffer_addch(&cmd, '\'');
841  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
842  data, flags | MUTT_FORMAT_NOFILTER);
843  for (char *p = tmp; p && (*p != '\0'); p++)
844  {
845  if (*p == '\'')
846  {
847  /* shell quoting doesn't permit escaping a single quote within
848  * single-quoted material. double-quoting instead will lead
849  * shell variable expansions, so break out of the single-quoted
850  * span, insert a double-quoted single quote, and resume. */
851  mutt_buffer_addstr(&cmd, "'\"'\"'");
852  }
853  else
854  mutt_buffer_addch(&cmd, *p);
855  }
856  mutt_buffer_addch(&cmd, '\'');
857  mutt_buffer_addch(&cmd, ' ');
858  } while (MoreArgs(&srcbuf));
859 
860  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
861 
862  col -= wlen; /* reset to passed in value */
863  wptr = buf; /* reset write ptr */
864  pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
865  if (pid != -1)
866  {
867  int rc;
868 
869  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
870  mutt_file_fclose(&fp_filter);
871  rc = filter_wait(pid);
872  if (rc != 0)
873  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
874  if (n > 0)
875  {
876  buf[n] = '\0';
877  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
878  buf[--n] = '\0';
879  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
880 
881  /* If the result ends with '%', this indicates that the filter
882  * generated %-tokens that neomutt can expand. Eliminate the '%'
883  * marker and recycle the string through mutt_expando_format().
884  * To literally end with "%", use "%%". */
885  if ((n > 0) && (buf[n - 1] == '%'))
886  {
887  n--;
888  buf[n] = '\0'; /* remove '%' */
889  if ((n > 0) && (buf[n - 1] != '%'))
890  {
891  recycler = mutt_str_dup(buf);
892  if (recycler)
893  {
894  /* buflen is decremented at the start of this function
895  * to save space for the terminal nul char. We can add
896  * it back for the recursive call since the expansion of
897  * format pipes does not try to append a nul itself. */
898  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
899  callback, data, flags);
900  FREE(&recycler);
901  }
902  }
903  }
904  }
905  else
906  {
907  /* read error */
908  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
909  strerror(errno), errno);
910  *wptr = '\0';
911  }
912  }
913  else
914  {
915  /* Filter failed; erase write buffer */
916  *wptr = '\0';
917  }
918 
919  mutt_buffer_dealloc(&cmd);
920  mutt_buffer_dealloc(&srcbuf);
921  mutt_buffer_dealloc(&word);
922  return;
923  }
924  }
925 
926  while (*src && (wlen < buflen))
927  {
928  if (*src == '%')
929  {
930  if (*++src == '%')
931  {
932  *wptr++ = '%';
933  wlen++;
934  col++;
935  src++;
936  continue;
937  }
938 
939  if (*src == '?')
940  {
941  /* change original %? to new %< notation */
942  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
943  char *p = (char *) src;
944  *p = '<';
945  /* skip over "x" */
946  for (; *p && (*p != '?'); p++)
947  ; // do nothing
948 
949  /* nothing */
950  if (*p == '?')
951  p++;
952  /* fix up the "y&z" section */
953  for (; *p && (*p != '?'); p++)
954  {
955  /* escape '<' and '>' to work inside nested-if */
956  if ((*p == '<') || (*p == '>'))
957  {
958  memmove(p + 2, p, mutt_str_len(p) + 1);
959  *p++ = '\\';
960  *p++ = '\\';
961  }
962  }
963  if (*p == '?')
964  *p = '>';
965  }
966 
967  if (*src == '<')
968  {
969  flags |= MUTT_FORMAT_OPTIONAL;
970  ch = *(++src); /* save the character to switch on */
971  src++;
972  cp = prefix;
973  count = 0;
974  while ((count < sizeof(prefix)) && (*src != '?'))
975  {
976  *cp++ = *src++;
977  count++;
978  }
979  *cp = '\0';
980  }
981  else
982  {
983  flags &= ~MUTT_FORMAT_OPTIONAL;
984 
985  /* eat the format string */
986  cp = prefix;
987  count = 0;
988  while ((count < sizeof(prefix)) && (isdigit((unsigned char) *src) || (*src == '.') ||
989  (*src == '-') || (*src == '=')))
990  {
991  *cp++ = *src++;
992  count++;
993  }
994  *cp = '\0';
995 
996  if (*src == '\0')
997  break; /* bad format */
998 
999  ch = *src++; /* save the character to switch on */
1000  }
1001 
1002  if (flags & MUTT_FORMAT_OPTIONAL)
1003  {
1004  int lrbalance;
1005 
1006  if (*src != '?')
1007  break; /* bad format */
1008  src++;
1009 
1010  /* eat the 'if' part of the string */
1011  cp = if_str;
1012  count = 0;
1013  lrbalance = 1;
1014  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1015  {
1016  if ((src[0] == '%') && (src[1] == '>'))
1017  {
1018  /* This is a padding expando; copy two chars and carry on */
1019  *cp++ = *src++;
1020  *cp++ = *src++;
1021  count += 2;
1022  continue;
1023  }
1024 
1025  if (*src == '\\')
1026  {
1027  src++;
1028  *cp++ = *src++;
1029  }
1030  else if ((src[0] == '%') && (src[1] == '<'))
1031  {
1032  lrbalance++;
1033  }
1034  else if (src[0] == '>')
1035  {
1036  lrbalance--;
1037  }
1038  if (lrbalance == 0)
1039  break;
1040  if ((lrbalance == 1) && (src[0] == '&'))
1041  break;
1042  *cp++ = *src++;
1043  count++;
1044  }
1045  *cp = '\0';
1046 
1047  /* eat the 'else' part of the string (optional) */
1048  if (*src == '&')
1049  src++; /* skip the & */
1050  cp = else_str;
1051  count = 0;
1052  while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1053  {
1054  if ((src[0] == '%') && (src[1] == '>'))
1055  {
1056  /* This is a padding expando; copy two chars and carry on */
1057  *cp++ = *src++;
1058  *cp++ = *src++;
1059  count += 2;
1060  continue;
1061  }
1062 
1063  if (*src == '\\')
1064  {
1065  src++;
1066  *cp++ = *src++;
1067  }
1068  else if ((src[0] == '%') && (src[1] == '<'))
1069  {
1070  lrbalance++;
1071  }
1072  else if (src[0] == '>')
1073  {
1074  lrbalance--;
1075  }
1076  if (lrbalance == 0)
1077  break;
1078  if ((lrbalance == 1) && (src[0] == '&'))
1079  break;
1080  *cp++ = *src++;
1081  count++;
1082  }
1083  *cp = '\0';
1084 
1085  if ((*src == '\0'))
1086  break; /* bad format */
1087 
1088  src++; /* move past the trailing '>' (formerly '?') */
1089  }
1090 
1091  /* handle generic cases first */
1092  if ((ch == '>') || (ch == '*'))
1093  {
1094  /* %>X: right justify to EOL, left takes precedence
1095  * %*X: right justify to EOL, right takes precedence */
1096  int soft = ch == '*';
1097  int pl, pw;
1098  pl = mutt_mb_charlen(src, &pw);
1099  if (pl <= 0)
1100  {
1101  pl = 1;
1102  pw = 1;
1103  }
1104 
1105  /* see if there's room to add content, else ignore */
1106  if (((col < cols) && (wlen < buflen)) || soft)
1107  {
1108  int pad;
1109 
1110  /* get contents after padding */
1111  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1112  len = mutt_str_len(tmp);
1113  wid = mutt_strwidth(tmp);
1114 
1115  pad = (cols - col - wid) / pw;
1116  if (pad >= 0)
1117  {
1118  /* try to consume as many columns as we can, if we don't have
1119  * memory for that, use as much memory as possible */
1120  if (wlen + (pad * pl) + len > buflen)
1121  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1122  else
1123  {
1124  /* Add pre-spacing to make multi-column pad characters and
1125  * the contents after padding line up */
1126  while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1127  {
1128  *wptr++ = ' ';
1129  wlen++;
1130  col++;
1131  }
1132  }
1133  while (pad-- > 0)
1134  {
1135  memcpy(wptr, src, pl);
1136  wptr += pl;
1137  wlen += pl;
1138  col += pw;
1139  }
1140  }
1141  else if (soft)
1142  {
1143  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ?
1145  0;
1146  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1147  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1148  *wptr = '\0';
1149  /* make sure right part is at most as wide as display */
1150  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1151  /* truncate left so that right part fits completely in */
1152  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1153  wptr = buf + wlen;
1154  /* Multi-column characters may be truncated in the middle.
1155  * Add spacing so the right hand side lines up. */
1156  while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1157  {
1158  *wptr++ = ' ';
1159  wlen++;
1160  col++;
1161  }
1162  }
1163  if ((len + wlen) > buflen)
1164  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1165  memcpy(wptr, tmp, len);
1166  wptr += len;
1167  }
1168  break; /* skip rest of input */
1169  }
1170  else if (ch == '|')
1171  {
1172  /* pad to EOL */
1173  int pl, pw;
1174  pl = mutt_mb_charlen(src, &pw);
1175  if (pl <= 0)
1176  {
1177  pl = 1;
1178  pw = 1;
1179  }
1180 
1181  /* see if there's room to add content, else ignore */
1182  if ((col < cols) && (wlen < buflen))
1183  {
1184  int c = (cols - col) / pw;
1185  if ((c > 0) && ((wlen + (c * pl)) > buflen))
1186  c = ((signed) (buflen - wlen)) / pl;
1187  while (c > 0)
1188  {
1189  memcpy(wptr, src, pl);
1190  wptr += pl;
1191  wlen += pl;
1192  col += pw;
1193  c--;
1194  }
1195  }
1196  break; /* skip rest of input */
1197  }
1198  else
1199  {
1200  bool to_lower = false;
1201  bool no_dots = false;
1202 
1203  while ((ch == '_') || (ch == ':'))
1204  {
1205  if (ch == '_')
1206  to_lower = true;
1207  else if (ch == ':')
1208  no_dots = true;
1209 
1210  ch = *src++;
1211  }
1212 
1213  /* use callback function to handle this case */
1214  *tmp = '\0';
1215  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1216  else_str, data, flags);
1217 
1218  if (to_lower)
1219  mutt_str_lower(tmp);
1220  if (no_dots)
1221  {
1222  char *p = tmp;
1223  for (; *p; p++)
1224  if (*p == '.')
1225  *p = '_';
1226  }
1227 
1228  len = mutt_str_len(tmp);
1229  if ((len + wlen) > buflen)
1230  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1231 
1232  memcpy(wptr, tmp, len);
1233  wptr += len;
1234  wlen += len;
1235  col += mutt_strwidth(tmp);
1236  }
1237  }
1238  else if (*src == '\\')
1239  {
1240  if (!*++src)
1241  break;
1242  switch (*src)
1243  {
1244  case 'f':
1245  *wptr = '\f';
1246  break;
1247  case 'n':
1248  *wptr = '\n';
1249  break;
1250  case 'r':
1251  *wptr = '\r';
1252  break;
1253  case 't':
1254  *wptr = '\t';
1255  break;
1256  case 'v':
1257  *wptr = '\v';
1258  break;
1259  default:
1260  *wptr = *src;
1261  break;
1262  }
1263  src++;
1264  wptr++;
1265  wlen++;
1266  col++;
1267  }
1268  else
1269  {
1270  int bytes, width;
1271  /* in case of error, simply copy byte */
1272  bytes = mutt_mb_charlen(src, &width);
1273  if (bytes < 0)
1274  {
1275  bytes = 1;
1276  width = 1;
1277  }
1278  if ((bytes > 0) && ((wlen + bytes) < buflen))
1279  {
1280  memcpy(wptr, src, bytes);
1281  wptr += bytes;
1282  src += bytes;
1283  wlen += bytes;
1284  col += width;
1285  }
1286  else
1287  {
1288  src += buflen - wlen;
1289  wlen = buflen;
1290  }
1291  }
1292  }
1293  *wptr = '\0';
1294 }
1295 
1306 FILE *mutt_open_read(const char *path, pid_t *thepid)
1307 {
1308  FILE *fp = NULL;
1309  struct stat s;
1310 
1311  size_t len = mutt_str_len(path);
1312  if (len == 0)
1313  {
1314  return NULL;
1315  }
1316 
1317  if (path[len - 1] == '|')
1318  {
1319  /* read from a pipe */
1320 
1321  char *p = mutt_str_dup(path);
1322 
1323  p[len - 1] = 0;
1324  mutt_endwin();
1325  *thepid = filter_create(p, NULL, &fp, NULL);
1326  FREE(&p);
1327  }
1328  else
1329  {
1330  if (stat(path, &s) < 0)
1331  return NULL;
1332  if (S_ISDIR(s.st_mode))
1333  {
1334  errno = EINVAL;
1335  return NULL;
1336  }
1337  fp = fopen(path, "r");
1338  *thepid = -1;
1339  }
1340  return fp;
1341 }
1342 
1351 int mutt_save_confirm(const char *s, struct stat *st)
1352 {
1353  int ret = 0;
1354 
1355  enum MailboxType type = mx_path_probe(s);
1356 
1357 #ifdef USE_POP
1358  if (type == MUTT_POP)
1359  {
1360  mutt_error(_("Can't save message to POP mailbox"));
1361  return 1;
1362  }
1363 #endif
1364 
1365  if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1366  {
1367  if (C_ConfirmAppend)
1368  {
1369  struct Buffer *tmp = mutt_buffer_pool_get();
1370  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1372  if (ans == MUTT_NO)
1373  ret = 1;
1374  else if (ans == MUTT_ABORT)
1375  ret = -1;
1377  }
1378  }
1379 
1380 #ifdef USE_NNTP
1381  if (type == MUTT_NNTP)
1382  {
1383  mutt_error(_("Can't save message to news server"));
1384  return 0;
1385  }
1386 #endif
1387 
1388  if (stat(s, st) != -1)
1389  {
1390  if (type == MUTT_MAILBOX_ERROR)
1391  {
1392  mutt_error(_("%s is not a mailbox"), s);
1393  return 1;
1394  }
1395  }
1396  else if (type != MUTT_IMAP)
1397  {
1398  st->st_mtime = 0;
1399  st->st_atime = 0;
1400 
1401  /* pathname does not exist */
1402  if (errno == ENOENT)
1403  {
1404  if (C_ConfirmCreate)
1405  {
1406  struct Buffer *tmp = mutt_buffer_pool_get();
1407  mutt_buffer_printf(tmp, _("Create %s?"), s);
1409  if (ans == MUTT_NO)
1410  ret = 1;
1411  else if (ans == MUTT_ABORT)
1412  ret = -1;
1414  }
1415 
1416  /* user confirmed with MUTT_YES or set `$confirm_create` */
1417  if (ret == 0)
1418  {
1419  /* create dir recursively */
1420  char *tmp_path = mutt_path_dirname(s);
1421  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1422  {
1423  /* report failure & abort */
1424  mutt_perror(s);
1425  FREE(&tmp_path);
1426  return 1;
1427  }
1428  FREE(&tmp_path);
1429  }
1430  }
1431  else
1432  {
1433  mutt_perror(s);
1434  return 1;
1435  }
1436  }
1437 
1439  return ret;
1440 }
1441 
1448 void mutt_sleep(short s)
1449 {
1450  if (C_SleepTime > s)
1451  sleep(C_SleepTime);
1452  else if (s)
1453  sleep(s);
1454 }
1455 
1462 const char *mutt_make_version(void)
1463 {
1464  static char vstring[256];
1465  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1466  return vstring;
1467 }
1468 
1476 void mutt_encode_path(struct Buffer *buf, const char *src)
1477 {
1478  char *p = mutt_str_dup(src);
1479  int rc = mutt_ch_convert_string(&p, C_Charset, "us-ascii", MUTT_ICONV_NO_FLAGS);
1480  size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1481 
1482  /* convert the path to POSIX "Portable Filename Character Set" */
1483  for (size_t i = 0; i < len; i++)
1484  {
1485  if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1486  {
1487  buf->data[i] = '_';
1488  }
1489  }
1490  FREE(&p);
1491 }
1492 
1501 int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1502 {
1503  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1504  char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(xdg_defaults[type]);
1505  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1506  char *token = NULL;
1507  int rc = 0;
1508 
1509  while ((token = strsep(&xdg, ":")))
1510  {
1511  if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1512  continue;
1514  if (access(mutt_buffer_string(buf), F_OK) == 0)
1515  {
1516  rc = 1;
1517  break;
1518  }
1519 
1520  if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1521  continue;
1523  if (access(mutt_buffer_string(buf), F_OK) == 0)
1524  {
1525  rc = 1;
1526  break;
1527  }
1528  }
1529 
1530  FREE(&x);
1531  return rc;
1532 }
1533 
1540 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1541 {
1542  enum MailboxType mb_type = mx_path_probe(path);
1543 
1544  if (mb_type == MUTT_IMAP)
1545  imap_get_parent_path(path, buf, buflen);
1546  else if (mb_type == MUTT_NOTMUCH)
1547  mutt_str_copy(buf, C_Folder, buflen);
1548  else
1549  {
1550  mutt_str_copy(buf, path, buflen);
1551  int n = mutt_str_len(buf);
1552  if (n == 0)
1553  return;
1554 
1555  /* remove any final trailing '/' */
1556  if (buf[n - 1] == '/')
1557  buf[n - 1] = '\0';
1558 
1559  /* Remove everything until the next slash */
1560  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1561  ; // do nothing
1562 
1563  if (n > 0)
1564  buf[n] = '\0';
1565  else
1566  {
1567  buf[0] = '/';
1568  buf[1] = '\0';
1569  }
1570  }
1571 }
1572 
1595 int mutt_inbox_cmp(const char *a, const char *b)
1596 {
1597  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1598  if ((a[0] == '+') && (b[0] == '+'))
1599  {
1600  return mutt_istr_equal(a + 1, "inbox") ? -1 :
1601  mutt_istr_equal(b + 1, "inbox") ? 1 :
1602  0;
1603  }
1604 
1605  const char *a_end = strrchr(a, '/');
1606  const char *b_end = strrchr(b, '/');
1607 
1608  /* If one path contains a '/', but not the other */
1609  if ((!a_end) ^ (!b_end))
1610  return 0;
1611 
1612  /* If neither path contains a '/' */
1613  if (!a_end)
1614  return 0;
1615 
1616  /* Compare the subpaths */
1617  size_t a_len = a_end - a;
1618  size_t b_len = b_end - b;
1619  size_t min = MIN(a_len, b_len);
1620  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1621  (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1622 
1623  if (!same)
1624  return 0;
1625 
1626  if (mutt_istr_equal(&a[min + 1], "inbox"))
1627  return -1;
1628 
1629  if (mutt_istr_equal(&b[min + 1], "inbox"))
1630  return 1;
1631 
1632  return 0;
1633 }
1634 
1641 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1642 {
1643  if (!buf || !path)
1644  return;
1645 
1646  mutt_buffer_reset(buf);
1647 
1648  for (; *path; path++)
1649  {
1650  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1651  mutt_buffer_addch(buf, '_');
1652  else
1653  mutt_buffer_addch(buf, *path);
1654  }
1655 }
1656 
1663 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1664 {
1665  if (!buf || (buflen == 0))
1666  return;
1667 
1668  if (C_SizeShowBytes && (num < 1024))
1669  {
1670  snprintf(buf, buflen, "%d", (int) num);
1671  }
1672  else if (num == 0)
1673  {
1674  mutt_str_copy(buf, C_SizeUnitsOnLeft ? "K0" : "0K", buflen);
1675  }
1676  else if (C_SizeShowFractions && (num < 10189)) /* 0.1K - 9.9K */
1677  {
1678  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "K%3.1f" : "%3.1fK",
1679  (num < 103) ? 0.1 : (num / 1024.0));
1680  }
1681  else if (!C_SizeShowMb || (num < 1023949)) /* 10K - 999K */
1682  {
1683  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1684  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1685  }
1686  else if (C_SizeShowFractions && (num < 10433332)) /* 1.0M - 9.9M */
1687  {
1688  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1689  }
1690  else /* 10M+ */
1691  {
1692  /* (10433332 + 52428) / 1048576 = 10 */
1693  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1694  }
1695 }
1696 
1704 void add_to_stailq(struct ListHead *head, const char *str)
1705 {
1706  /* don't add a NULL or empty string to the list */
1707  if (!str || (*str == '\0'))
1708  return;
1709 
1710  /* check to make sure the item is not already on this list */
1711  struct ListNode *np = NULL;
1712  STAILQ_FOREACH(np, head, entries)
1713  {
1714  if (mutt_istr_equal(str, np->data))
1715  {
1716  return;
1717  }
1718  }
1719  mutt_list_insert_tail(head, mutt_str_dup(str));
1720 }
1721 
1729 void remove_from_stailq(struct ListHead *head, const char *str)
1730 {
1731  if (mutt_str_equal("*", str))
1732  mutt_list_free(head); /* "unCMD *" means delete all current entries */
1733  else
1734  {
1735  struct ListNode *np = NULL, *tmp = NULL;
1736  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1737  {
1738  if (mutt_istr_equal(str, np->data))
1739  {
1740  STAILQ_REMOVE(head, np, ListNode, entries);
1741  FREE(&np->data);
1742  FREE(&np);
1743  break;
1744  }
1745  }
1746  }
1747 }
lib.h
mutt_encode_path
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1476
mutt_endwin
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:572
mutt_open_read
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1306
QuadOption
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
mx_access
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:189
U_IMAP
@ U_IMAP
Url is imap://.
Definition: url.h:38
pad
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:198
MessageWindow
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:47
C_SizeUnitsOnLeft
WHERE bool C_SizeUnitsOnLeft
Config: Show the units as a prefix to the size.
Definition: mutt_globals.h:163
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
imap_pretty_mailbox
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:590
APPLICATION_SMIME
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:98
mutt_strn_equal
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
MoreArgs
#define MoreArgs(buf)
Definition: buffer.h:40
XDG_CONFIG_DIRS
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition: protos.h:48
ListNode
A List node for strings.
Definition: list.h:34
Buffer
String manipulation buffer.
Definition: buffer.h:33
LL_DEBUG3
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
mutt_file_sanitize_filename
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:618
mutt_buffer_seek
void mutt_buffer_seek(struct Buffer *buf, size_t offset)
set current read/write position to offset from beginning
Definition: buffer.c:466
STAILQ_REMOVE
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:399
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
mutt_buffer_pretty_mailbox
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
mutt_save_path
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:706
mutt_buffer_is_empty
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
mutt_buffer_dealloc
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
format_flags.h
url_check_scheme
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
Body
The body of an email.
Definition: body.h:34
C_SleepTime
WHERE short C_SleepTime
Config: Time to pause after certain info messages.
Definition: mutt_globals.h:114
MUTT_FORMAT_ARROWCURSOR
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
MUTT_ICONV_NO_FLAGS
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:73
mutt_window_clearline
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:232
UrlScheme
UrlScheme
All recognised Url types.
Definition: url.h:32
U_UNKNOWN
@ U_UNKNOWN
Url wasn't recognised.
Definition: url.h:34
MUTT_POP
@ MUTT_POP
'POP3' Mailbox type
Definition: mailbox.h:55
mutt_file_sanitize_regex
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:637
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
mutt_multi_choice
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:937
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:714
Regex
Cached regular expression.
Definition: regex3.h:89
MUTT_YES
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:40
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
mutt_strwidth
int mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:1363
FREE
#define FREE(x)
Definition: memory.h:40
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
mutt_buffer_mktemp_pfx_sfx
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:78
MUTT_TOKEN_NO_FLAGS
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:70
U_FILE
@ U_FILE
Url is file://.
Definition: url.h:35
Buffer::dptr
char * dptr
Current read/write position.
Definition: buffer.h:36
MUTT_MAILBOX_ERROR
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition: mailbox.h:46
Buffer::dsize
size_t dsize
Length of data.
Definition: buffer.h:37
mutt_regex_capture
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:595
LL_DEBUG5
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
mutt_ch_convert_string
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:754
MUTT_ABORT
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:38
mutt_buffer_reset
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
mutt_sleep
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1448
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
mutt_expand_path_regex
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:337
mutt_mktemp_full
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:494
mx_path_probe
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1335
email_new
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
Body::subtype
char * subtype
content-type subtype
Definition: body.h:37
mutt_list_insert_tail
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
filename_safe_chars
const char filename_safe_chars[]
Definition: file.c:61
mutt_buffer_alloc
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
SaveAttach
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:54
imap_get_parent_path
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:163
xdg_env_vars
static const char * xdg_env_vars[]
Definition: muttlib.c:72
mutt_istr_equal
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
lib.h
C_SaveAddress
WHERE bool C_SaveAddress
Config: Use sender's full address as a default save folder.
Definition: mutt_globals.h:157
mutt_buffer_expand_path_regex
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:140
MUTT_FILE
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
protos.h
mutt_extract_token
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:393
filter_create
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
mutt_str_pretty_size
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1663
mutt_buffer_addch
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
mutt_expand_path
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:128
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
mutt_path_basename
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
mutt_is_application_pgp
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:554
mutt_buffer_save_path
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:728
MUTT_NNTP
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:52
init.h
mutt_buffer_copy
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:445
lib.h
muttlib.h
C_Folder
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: mutt_globals.h:96
add_to_stailq
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1704
APPLICATION_PGP
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
mutt_save_confirm
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1351
lib.h
lib.h
LastFolder
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:55
C_SizeShowMb
WHERE bool C_SizeShowMb
Config: Show sizes in megabytes for sizes greater than 1 megabyte.
Definition: mutt_globals.h:162
MUTT_UNKNOWN
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
MAX
#define MAX(a, b)
Definition: memory.h:30
U_IMAPS
@ U_IMAPS
Url is imaps://.
Definition: url.h:39
C_ArrowCursor
WHERE bool C_ArrowCursor
Config: Use an arrow '->' instead of highlighting in the index.
Definition: mutt_globals.h:131
mutt_safe_path
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:755
MUTT_NOTMUCH
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:54
MUTT_IMAP
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:53
mutt_default_save
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:655
Envelope::to
struct AddressList to
Email's 'To' list.
Definition: envelope.h:58
TYPE_MESSAGE
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
mutt_buffer_concat_path
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
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
MUTT_SAVE_APPEND
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:57
mutt_rand64
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:129
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
MUTT_NO
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:39
C_Tmpdir
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:56
C_SizeShowFractions
WHERE bool C_SizeShowFractions
Config: Show size fractions with a single decimal place.
Definition: mutt_globals.h:161
mutt_buffer_expand_path
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
mutt_buffer_get_field
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:260
mutt_str_len
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
mutt_str_getenv
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:991
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
STAILQ_FOREACH_SAFE
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:359
C_GecosMask
struct Regex * C_GecosMask
Config: Regex for parsing GECOS field of /etc/passwd.
Definition: muttlib.c:70
mutt_env_new
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
mutt_str_lower
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:504
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
lib.h
filter_wait
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
MUTT_FORMAT_OPTIONAL
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
mutt_inbox_cmp
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:1595
mutt_adv_mktemp
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:90
WithCrypto
#define WithCrypto
Definition: lib.h:123
IsPrint
#define IsPrint(ch)
Definition: mbyte.h:39
C_Record
WHERE char * C_Record
Config: Folder to save 'sent' messages.
Definition: mutt_globals.h:98
C_SizeShowBytes
WHERE bool C_SizeShowBytes
Config: Show smaller sizes in bytes.
Definition: mutt_globals.h:160
Address::mailbox
char * mailbox
Mailbox and host address.
Definition: address.h:37
lib.h
lib.h
mutt_check_overwrite
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:621
mutt_file_mkdir
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:875
format_t
const typedef 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
mutt_set_xdg_path
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1501
Envelope::from
struct AddressList from
Email's 'From' list.
Definition: envelope.h:57
TYPE_APPLICATION
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
HomeDir
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
mutt_buffer_mktemp_full
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
mutt_expando_format
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:774
mutt_wstr_trunc
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:1313
mutt_mb_charlen
int mutt_mb_charlen(const char *s, int *width)
Count the bytes in a (multibyte) character.
Definition: mbyte.c:55
ShortHostname
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
TYPE_TEXT
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
alias_lookup
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:276
XdgType
XdgType
XDG variable types.
Definition: protos.h:45
MUTT_SAVE_NO_FLAGS
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:56
mutt_buffer_fix_dptr
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
C_SpoolFile
WHERE char * C_SpoolFile
Config: Inbox.
Definition: mutt_globals.h:108
mutt.h
remove_from_stailq
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1729
Body::type
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
C_Mbox
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: mutt_globals.h:94
imap_path_probe
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2372
mutt_gecos_name
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:362
U_NOTMUCH
@ U_NOTMUCH
Url is notmuch://.
Definition: url.h:45
ListNode::data
char * data
String.
Definition: list.h:36
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
MUTT_CLEAR
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:62
CurrentFolder
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:54
C_ArrowString
WHERE char * C_ArrowString
Config: Use an custom string for arrow_cursor.
Definition: mutt_globals.h:132
email_free
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
mutt_is_text_part
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
mutt_buffer_addstr
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
mutt_istrn_equal
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
mutt_needs_mailcap
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:405
GitVer
const char * GitVer
mx.h
XDG_CONFIG_HOME
@ XDG_CONFIG_HOME
XDG home dir: ~/.config.
Definition: protos.h:47
MUTT_SAVE_OVERWRITE
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:58
C_Charset
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53
mutt_yesorno
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:380
mutt_get_parent_path
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1540
Buffer::data
char * data
Pointer to data.
Definition: buffer.h:35
IS_SPACE
#define IS_SPACE(ch)
Definition: string2.h:38
TYPE_MULTIPART
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
Email
The envelope/body of an email.
Definition: email.h:37
lib.h
mutt_str_startswith
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
mutt_make_version
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1462
MUTT_FORMAT_NOFILTER
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
mutt_buffer_sanitize_filename
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1641
mutt_addrlist_copy
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
C_ConfirmAppend
WHERE bool C_ConfirmAppend
Config: Confirm before appending emails to a mailbox.
Definition: mutt_globals.h:141
mutt_buffer_make
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
mutt_list_free
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
hook.h
C_ConfirmCreate
WHERE bool C_ConfirmCreate
Config: Confirm before creating a new mailbox.
Definition: mutt_globals.h:142
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
mutt_is_application_smime
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:612
Address
An email address.
Definition: address.h:34
mutt_pretty_mailbox
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:523
MuttFormatFlags
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
xdg_defaults
static const char * xdg_defaults[]
Definition: muttlib.c:77
imap_expand_path
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2412
idx
size_t idx
Definition: mailbox.c:234
MIN
#define MIN(a, b)
Definition: memory.h:31
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_path_dirname
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:376
mutt_str_copy
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