NeoMutt  2022-04-29-145-g9b6a0e
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 "enter/lib.h"
54 #include "ncrypt/lib.h"
55 #include "question/lib.h"
56 #include "format_flags.h"
57 #include "hook.h"
58 #include "init.h"
59 #include "mutt_globals.h"
60 #include "mx.h"
61 #include "protos.h"
62 #ifdef USE_IMAP
63 #include "imap/lib.h"
64 #endif
65 
66 static const char *xdg_env_vars[] = {
67  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
68  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
69 };
70 
71 static const char *xdg_defaults[] = {
72  [XDG_CONFIG_HOME] = "~/.config",
73  [XDG_CONFIG_DIRS] = "/etc/xdg",
74 };
75 
84 void mutt_adv_mktemp(struct Buffer *buf)
85 {
86  if (!(buf->data && (buf->data[0] != '\0')))
87  {
88  mutt_buffer_mktemp(buf);
89  }
90  else
91  {
92  struct Buffer *prefix = mutt_buffer_pool_get();
93  mutt_buffer_strcpy(prefix, buf->data);
94  mutt_file_sanitize_filename(prefix->data, true);
95  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
96  mutt_buffer_printf(buf, "%s/%s", NONULL(c_tmpdir), mutt_buffer_string(prefix));
97 
98  struct stat st = { 0 };
99  if ((lstat(mutt_buffer_string(buf), &st) == -1) && (errno == ENOENT))
100  goto out;
101 
102  char *suffix = strchr(prefix->data, '.');
103  if (suffix)
104  {
105  *suffix = '\0';
106  suffix++;
107  }
108  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
109 
110  out:
111  mutt_buffer_pool_release(&prefix);
112  }
113 }
114 
123 char *mutt_expand_path(char *buf, size_t buflen)
124 {
125  return mutt_expand_path_regex(buf, buflen, false);
126 }
127 
135 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
136 {
137  const char *s = NULL;
138  const char *tail = "";
139 
140  bool recurse = false;
141 
142  struct Buffer *p = mutt_buffer_pool_get();
143  struct Buffer *q = mutt_buffer_pool_get();
144  struct Buffer *tmp = mutt_buffer_pool_get();
145 
146  do
147  {
148  recurse = false;
149  s = mutt_buffer_string(buf);
150 
151  switch (*s)
152  {
153  case '~':
154  {
155  if ((s[1] == '/') || (s[1] == '\0'))
156  {
158  tail = s + 1;
159  }
160  else
161  {
162  char *t = strchr(s + 1, '/');
163  if (t)
164  *t = '\0';
165 
166  struct passwd *pw = getpwnam(s + 1);
167  if (pw)
168  {
169  mutt_buffer_strcpy(p, pw->pw_dir);
170  if (t)
171  {
172  *t = '/';
173  tail = t;
174  }
175  else
176  tail = "";
177  }
178  else
179  {
180  /* user not found! */
181  if (t)
182  *t = '/';
184  tail = s;
185  }
186  }
187  break;
188  }
189 
190  case '=':
191  case '+':
192  {
193  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
194  enum MailboxType mb_type = mx_path_probe(c_folder);
195 
196  /* if folder = {host} or imap[s]://host/: don't append slash */
197  if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
198  (c_folder[strlen(c_folder) - 1] == '/')))
199  {
200  mutt_buffer_strcpy(p, NONULL(c_folder));
201  }
202  else if (mb_type == MUTT_NOTMUCH)
203  mutt_buffer_strcpy(p, NONULL(c_folder));
204  else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
205  mutt_buffer_strcpy(p, NONULL(c_folder));
206  else
207  mutt_buffer_printf(p, "%s/", NONULL(c_folder));
208 
209  tail = s + 1;
210  break;
211  }
212 
213  /* elm compatibility, @ expands alias to user name */
214 
215  case '@':
216  {
217  struct AddressList *al = alias_lookup(s + 1);
218  if (al && !TAILQ_EMPTY(al))
219  {
220  struct Email *e = email_new();
221  e->env = mutt_env_new();
222  mutt_addrlist_copy(&e->env->from, al, false);
223  mutt_addrlist_copy(&e->env->to, al, false);
224 
225  /* TODO: fix mutt_default_save() to use Buffer */
227  mutt_default_save(p->data, p->dsize, e);
229 
230  email_free(&e);
231  /* Avoid infinite recursion if the resulting folder starts with '@' */
232  if (*p->data != '@')
233  recurse = true;
234 
235  tail = "";
236  }
237  break;
238  }
239 
240  case '>':
241  {
242  const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
243  mutt_buffer_strcpy(p, c_mbox);
244  tail = s + 1;
245  break;
246  }
247 
248  case '<':
249  {
250  const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
251  mutt_buffer_strcpy(p, c_record);
252  tail = s + 1;
253  break;
254  }
255 
256  case '!':
257  {
258  if (s[1] == '!')
259  {
261  tail = s + 2;
262  }
263  else
264  {
265  const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
266  mutt_buffer_strcpy(p, c_spool_file);
267  tail = s + 1;
268  }
269  break;
270  }
271 
272  case '-':
273  {
275  tail = s + 1;
276  break;
277  }
278 
279  case '^':
280  {
282  tail = s + 1;
283  break;
284  }
285 
286  default:
287  {
289  tail = s;
290  }
291  }
292 
293  if (regex && *(mutt_buffer_string(p)) && !recurse)
294  {
296  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(q), tail);
297  }
298  else
299  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(p), tail);
300 
301  mutt_buffer_copy(buf, tmp);
302  } while (recurse);
303 
307 
308 #ifdef USE_IMAP
309  /* Rewrite IMAP path in canonical form - aids in string comparisons of
310  * folders. May possibly fail, in which case buf should be the same. */
311  if (imap_path_probe(mutt_buffer_string(buf), NULL) == MUTT_IMAP)
312  imap_expand_path(buf);
313 #endif
314 }
315 
323 {
324  mutt_buffer_expand_path_regex(buf, false);
325 }
326 
336 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
337 {
338  struct Buffer *tmp = mutt_buffer_pool_get();
339 
340  mutt_buffer_addstr(tmp, NONULL(buf));
341  mutt_buffer_expand_path_regex(tmp, regex);
342  mutt_str_copy(buf, mutt_buffer_string(tmp), buflen);
343 
345 
346  return buf;
347 }
348 
361 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
362 {
363  regmatch_t pat_match[1];
364  size_t pwnl;
365  char *p = NULL;
366 
367  if (!pw || !pw->pw_gecos)
368  return NULL;
369 
370  memset(dest, 0, destlen);
371 
372  const struct Regex *c_gecos_mask = cs_subset_regex(NeoMutt->sub, "gecos_mask");
373  if (mutt_regex_capture(c_gecos_mask, 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  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
471  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(c_tmpdir),
472  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
473  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
474 
475  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line,
476  mutt_buffer_string(buf));
477  if (unlink(mutt_buffer_string(buf)) && (errno != ENOENT))
478  {
479  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
480  line, mutt_buffer_string(buf), strerror(errno), errno);
481  }
482 }
483 
495 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
496  const char *suffix, const char *src, int line)
497 {
498  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
499  size_t n = snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(c_tmpdir),
500  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
501  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
502  if (n >= buflen)
503  {
504  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: insufficient buffer space to hold temporary 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  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
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 = { 0 };
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_COMP_FILE | MUTT_COMP_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  char buf[4096] = { 0 };
677  snprintf(buf, sizeof(buf), "%s - %s", mutt_buffer_string(fname),
678  // L10N: Options for: File %s exists, (o)verwrite, (a)ppend, or (c)ancel?
679  _("File exists, (o)verwrite, (a)ppend, or (c)ancel?"));
680  switch (mutt_multi_choice(buf, _("oac")))
681  {
682  case -1: /* abort */
683  return -1;
684  case 3: /* cancel */
685  return 1;
686 
687  case 2: /* append */
688  *opt = MUTT_SAVE_APPEND;
689  break;
690  case 1: /* overwrite */
691  *opt = MUTT_SAVE_OVERWRITE;
692  break;
693  }
694  }
695  return 0;
696 }
697 
707 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
708 {
709  if (addr && addr->mailbox)
710  {
711  mutt_str_copy(buf, addr->mailbox, buflen);
712  const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
713  if (!c_save_address)
714  {
715  char *p = strpbrk(buf, "%@");
716  if (p)
717  *p = '\0';
718  }
719  mutt_str_lower(buf);
720  }
721  else
722  *buf = '\0';
723 }
724 
730 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
731 {
732  if (a && a->mailbox)
733  {
734  mutt_buffer_strcpy(dest, a->mailbox);
735  const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
736  if (!c_save_address)
737  {
738  char *p = strpbrk(dest->data, "%@");
739  if (p)
740  {
741  *p = '\0';
742  mutt_buffer_fix_dptr(dest);
743  }
744  }
745  mutt_str_lower(dest->data);
746  }
747  else
748  mutt_buffer_reset(dest);
749 }
750 
758 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
759 {
760  mutt_buffer_save_path(dest, a);
761  for (char *p = dest->data; *p; p++)
762  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
763  *p = '_';
764 }
765 
777 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
778  format_t callback, intptr_t data, MuttFormatFlags flags)
779 {
780  char prefix[128], tmp[1024];
781  char *cp = NULL, *wptr = buf;
782  char ch;
783  char if_str[128], else_str[128];
784  size_t wlen, count, len, wid;
785  FILE *fp_filter = NULL;
786  char *recycler = NULL;
787 
788  char src2[1024];
789  mutt_str_copy(src2, src, mutt_str_len(src) + 1);
790  src = src2;
791 
792  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
793  const char *const c_arrow_string = cs_subset_string(NeoMutt->sub, "arrow_string");
794 
795  prefix[0] = '\0';
796  buflen--; /* save room for the terminal \0 */
797  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
798  mutt_strwidth(c_arrow_string) + 1 :
799  0;
800  col += wlen;
801 
802  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
803  {
804  int off = -1;
805 
806  /* Do not consider filters if no pipe at end */
807  int n = mutt_str_len(src);
808  if ((n > 1) && (src[n - 1] == '|'))
809  {
810  /* Scan backwards for backslashes */
811  off = n;
812  while ((off > 0) && (src[off - 2] == '\\'))
813  off--;
814  }
815 
816  /* If number of backslashes is even, the pipe is real. */
817  /* n-off is the number of backslashes. */
818  if ((off > 0) && (((n - off) % 2) == 0))
819  {
820  char srccopy[1024];
821  int i = 0;
822 
823  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
824 
825  strncpy(srccopy, src, n);
826  srccopy[n - 1] = '\0';
827 
828  /* prepare Buffers */
829  struct Buffer srcbuf = mutt_buffer_make(0);
830  mutt_buffer_addstr(&srcbuf, srccopy);
831  /* note: we are resetting dptr and *reading* from the buffer, so we don't
832  * want to use mutt_buffer_reset(). */
833  mutt_buffer_seek(&srcbuf, 0);
834  struct Buffer word = mutt_buffer_make(0);
835  struct Buffer cmd = mutt_buffer_make(0);
836 
837  /* Iterate expansions across successive arguments */
838  do
839  {
840  /* Extract the command name and copy to command line */
841  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
842  if (word.data)
843  *word.data = '\0';
844  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
845  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
846  mutt_buffer_addch(&cmd, '\'');
847  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
848  data, flags | MUTT_FORMAT_NOFILTER);
849  for (char *p = tmp; p && (*p != '\0'); p++)
850  {
851  if (*p == '\'')
852  {
853  /* shell quoting doesn't permit escaping a single quote within
854  * single-quoted material. double-quoting instead will lead
855  * shell variable expansions, so break out of the single-quoted
856  * span, insert a double-quoted single quote, and resume. */
857  mutt_buffer_addstr(&cmd, "'\"'\"'");
858  }
859  else
860  mutt_buffer_addch(&cmd, *p);
861  }
862  mutt_buffer_addch(&cmd, '\'');
863  mutt_buffer_addch(&cmd, ' ');
864  } while (MoreArgs(&srcbuf));
865 
866  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
867 
868  col -= wlen; /* reset to passed in value */
869  wptr = buf; /* reset write ptr */
870  pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
871  if (pid != -1)
872  {
873  int rc;
874 
875  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
876  mutt_file_fclose(&fp_filter);
877  rc = filter_wait(pid);
878  if (rc != 0)
879  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
880  if (n > 0)
881  {
882  buf[n] = '\0';
883  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
884  buf[--n] = '\0';
885  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
886 
887  /* If the result ends with '%', this indicates that the filter
888  * generated %-tokens that neomutt can expand. Eliminate the '%'
889  * marker and recycle the string through mutt_expando_format().
890  * To literally end with "%", use "%%". */
891  if ((n > 0) && (buf[n - 1] == '%'))
892  {
893  n--;
894  buf[n] = '\0'; /* remove '%' */
895  if ((n > 0) && (buf[n - 1] != '%'))
896  {
897  recycler = mutt_str_dup(buf);
898  if (recycler)
899  {
900  /* buflen is decremented at the start of this function
901  * to save space for the terminal nul char. We can add
902  * it back for the recursive call since the expansion of
903  * format pipes does not try to append a nul itself. */
904  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
905  callback, data, flags);
906  FREE(&recycler);
907  }
908  }
909  }
910  }
911  else
912  {
913  /* read error */
914  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
915  strerror(errno), errno);
916  *wptr = '\0';
917  }
918  }
919  else
920  {
921  /* Filter failed; erase write buffer */
922  *wptr = '\0';
923  }
924 
925  mutt_buffer_dealloc(&cmd);
926  mutt_buffer_dealloc(&srcbuf);
927  mutt_buffer_dealloc(&word);
928  return;
929  }
930  }
931 
932  while (*src && (wlen < buflen))
933  {
934  if (*src == '%')
935  {
936  if (*++src == '%')
937  {
938  *wptr++ = '%';
939  wlen++;
940  col++;
941  src++;
942  continue;
943  }
944 
945  if (*src == '?')
946  {
947  /* change original %? to new %< notation */
948  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
949  char *p = (char *) src;
950  *p = '<';
951  /* skip over "x" */
952  for (; *p && (*p != '?'); p++)
953  ; // do nothing
954 
955  /* nothing */
956  if (*p == '?')
957  p++;
958  /* fix up the "y&z" section */
959  for (; *p && (*p != '?'); p++)
960  {
961  /* escape '<' and '>' to work inside nested-if */
962  if ((*p == '<') || (*p == '>'))
963  {
964  memmove(p + 2, p, mutt_str_len(p) + 1);
965  *p++ = '\\';
966  *p++ = '\\';
967  }
968  }
969  if (*p == '?')
970  *p = '>';
971  }
972 
973  if (*src == '<')
974  {
975  flags |= MUTT_FORMAT_OPTIONAL;
976  ch = *(++src); /* save the character to switch on */
977  src++;
978  cp = prefix;
979  count = 0;
980  while ((count < (sizeof(prefix) - 1)) && (*src != '\0') && (*src != '?'))
981  {
982  *cp++ = *src++;
983  count++;
984  }
985  *cp = '\0';
986  }
987  else
988  {
989  flags &= ~MUTT_FORMAT_OPTIONAL;
990 
991  /* eat the format string */
992  cp = prefix;
993  count = 0;
994  while ((count < (sizeof(prefix) - 1)) && strchr("0123456789.-=", *src))
995  {
996  *cp++ = *src++;
997  count++;
998  }
999  *cp = '\0';
1000 
1001  if (*src == '\0')
1002  break; /* bad format */
1003 
1004  ch = *src++; /* save the character to switch on */
1005  }
1006 
1007  if (flags & MUTT_FORMAT_OPTIONAL)
1008  {
1009  int lrbalance;
1010 
1011  if (*src != '?')
1012  break; /* bad format */
1013  src++;
1014 
1015  /* eat the 'if' part of the string */
1016  cp = if_str;
1017  count = 0;
1018  lrbalance = 1;
1019  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1020  {
1021  if ((src[0] == '%') && (src[1] == '>'))
1022  {
1023  /* This is a padding expando; copy two chars and carry on */
1024  *cp++ = *src++;
1025  *cp++ = *src++;
1026  count += 2;
1027  continue;
1028  }
1029 
1030  if (*src == '\\')
1031  {
1032  src++;
1033  *cp++ = *src++;
1034  }
1035  else if ((src[0] == '%') && (src[1] == '<'))
1036  {
1037  lrbalance++;
1038  }
1039  else if (src[0] == '>')
1040  {
1041  lrbalance--;
1042  }
1043  if (lrbalance == 0)
1044  break;
1045  if ((lrbalance == 1) && (src[0] == '&'))
1046  break;
1047  *cp++ = *src++;
1048  count++;
1049  }
1050  *cp = '\0';
1051 
1052  /* eat the 'else' part of the string (optional) */
1053  if (*src == '&')
1054  src++; /* skip the & */
1055  cp = else_str;
1056  count = 0;
1057  while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1058  {
1059  if ((src[0] == '%') && (src[1] == '>'))
1060  {
1061  /* This is a padding expando; copy two chars and carry on */
1062  *cp++ = *src++;
1063  *cp++ = *src++;
1064  count += 2;
1065  continue;
1066  }
1067 
1068  if (*src == '\\')
1069  {
1070  src++;
1071  *cp++ = *src++;
1072  }
1073  else if ((src[0] == '%') && (src[1] == '<'))
1074  {
1075  lrbalance++;
1076  }
1077  else if (src[0] == '>')
1078  {
1079  lrbalance--;
1080  }
1081  if (lrbalance == 0)
1082  break;
1083  if ((lrbalance == 1) && (src[0] == '&'))
1084  break;
1085  *cp++ = *src++;
1086  count++;
1087  }
1088  *cp = '\0';
1089 
1090  if ((*src == '\0'))
1091  break; /* bad format */
1092 
1093  src++; /* move past the trailing '>' (formerly '?') */
1094  }
1095 
1096  /* handle generic cases first */
1097  if ((ch == '>') || (ch == '*'))
1098  {
1099  /* %>X: right justify to EOL, left takes precedence
1100  * %*X: right justify to EOL, right takes precedence */
1101  int soft = ch == '*';
1102  int pl, pw;
1103  pl = mutt_mb_charlen(src, &pw);
1104  if (pl <= 0)
1105  {
1106  pl = 1;
1107  pw = 1;
1108  }
1109 
1110  /* see if there's room to add content, else ignore */
1111  if (((col < cols) && (wlen < buflen)) || soft)
1112  {
1113  int pad;
1114 
1115  /* get contents after padding */
1116  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1117  len = mutt_str_len(tmp);
1118  wid = mutt_strwidth(tmp);
1119 
1120  pad = (cols - col - wid) / pw;
1121  if (pad >= 0)
1122  {
1123  /* try to consume as many columns as we can, if we don't have
1124  * memory for that, use as much memory as possible */
1125  if (wlen + (pad * pl) + len > buflen)
1126  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1127  else
1128  {
1129  /* Add pre-spacing to make multi-column pad characters and
1130  * the contents after padding line up */
1131  while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1132  {
1133  *wptr++ = ' ';
1134  wlen++;
1135  col++;
1136  }
1137  }
1138  while (pad-- > 0)
1139  {
1140  memcpy(wptr, src, pl);
1141  wptr += pl;
1142  wlen += pl;
1143  col += pw;
1144  }
1145  }
1146  else if (soft)
1147  {
1148  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
1149  mutt_strwidth(c_arrow_string) + 1 :
1150  0;
1151  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1152  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1153  *wptr = '\0';
1154  /* make sure right part is at most as wide as display */
1155  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1156  /* truncate left so that right part fits completely in */
1157  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1158  wptr = buf + wlen;
1159  /* Multi-column characters may be truncated in the middle.
1160  * Add spacing so the right hand side lines up. */
1161  while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1162  {
1163  *wptr++ = ' ';
1164  wlen++;
1165  col++;
1166  }
1167  }
1168  if ((len + wlen) > buflen)
1169  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1170  memcpy(wptr, tmp, len);
1171  wptr += len;
1172  }
1173  break; /* skip rest of input */
1174  }
1175  else if (ch == '|')
1176  {
1177  /* pad to EOL */
1178  int pl, pw;
1179  pl = mutt_mb_charlen(src, &pw);
1180  if (pl <= 0)
1181  {
1182  pl = 1;
1183  pw = 1;
1184  }
1185 
1186  /* see if there's room to add content, else ignore */
1187  if ((col < cols) && (wlen < buflen))
1188  {
1189  int c = (cols - col) / pw;
1190  if ((c > 0) && ((wlen + (c * pl)) > buflen))
1191  c = ((signed) (buflen - wlen)) / pl;
1192  while (c > 0)
1193  {
1194  memcpy(wptr, src, pl);
1195  wptr += pl;
1196  wlen += pl;
1197  col += pw;
1198  c--;
1199  }
1200  }
1201  break; /* skip rest of input */
1202  }
1203  else
1204  {
1205  bool to_lower = false;
1206  bool no_dots = false;
1207 
1208  while ((ch == '_') || (ch == ':'))
1209  {
1210  if (ch == '_')
1211  to_lower = true;
1212  else if (ch == ':')
1213  no_dots = true;
1214 
1215  ch = *src++;
1216  }
1217 
1218  /* use callback function to handle this case */
1219  *tmp = '\0';
1220  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1221  else_str, data, flags);
1222 
1223  if (to_lower)
1224  mutt_str_lower(tmp);
1225  if (no_dots)
1226  {
1227  char *p = tmp;
1228  for (; *p; p++)
1229  if (*p == '.')
1230  *p = '_';
1231  }
1232 
1233  len = mutt_str_len(tmp);
1234  if ((len + wlen) > buflen)
1235  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1236 
1237  memcpy(wptr, tmp, len);
1238  wptr += len;
1239  wlen += len;
1240  col += mutt_strwidth(tmp);
1241  }
1242  }
1243  else if (*src == '\\')
1244  {
1245  if (!*++src)
1246  break;
1247  switch (*src)
1248  {
1249  case 'f':
1250  *wptr = '\f';
1251  break;
1252  case 'n':
1253  *wptr = '\n';
1254  break;
1255  case 'r':
1256  *wptr = '\r';
1257  break;
1258  case 't':
1259  *wptr = '\t';
1260  break;
1261  case 'v':
1262  *wptr = '\v';
1263  break;
1264  default:
1265  *wptr = *src;
1266  break;
1267  }
1268  src++;
1269  wptr++;
1270  wlen++;
1271  col++;
1272  }
1273  else
1274  {
1275  int bytes, width;
1276  /* in case of error, simply copy byte */
1277  bytes = mutt_mb_charlen(src, &width);
1278  if (bytes < 0)
1279  {
1280  bytes = 1;
1281  width = 1;
1282  }
1283  if ((bytes > 0) && ((wlen + bytes) < buflen))
1284  {
1285  memcpy(wptr, src, bytes);
1286  wptr += bytes;
1287  src += bytes;
1288  wlen += bytes;
1289  col += width;
1290  }
1291  else
1292  {
1293  src += buflen - wlen;
1294  wlen = buflen;
1295  }
1296  }
1297  }
1298  *wptr = '\0';
1299 }
1300 
1311 FILE *mutt_open_read(const char *path, pid_t *thepid)
1312 {
1313  FILE *fp = NULL;
1314  struct stat st = { 0 };
1315 
1316  size_t len = mutt_str_len(path);
1317  if (len == 0)
1318  {
1319  return NULL;
1320  }
1321 
1322  if (path[len - 1] == '|')
1323  {
1324  /* read from a pipe */
1325 
1326  char *p = mutt_str_dup(path);
1327 
1328  p[len - 1] = 0;
1329  mutt_endwin();
1330  *thepid = filter_create(p, NULL, &fp, NULL);
1331  FREE(&p);
1332  }
1333  else
1334  {
1335  if (stat(path, &st) < 0)
1336  return NULL;
1337  if (S_ISDIR(st.st_mode))
1338  {
1339  errno = EINVAL;
1340  return NULL;
1341  }
1342  fp = fopen(path, "r");
1343  *thepid = -1;
1344  }
1345  return fp;
1346 }
1347 
1356 int mutt_save_confirm(const char *s, struct stat *st)
1357 {
1358  int rc = 0;
1359 
1360  enum MailboxType type = mx_path_probe(s);
1361 
1362 #ifdef USE_POP
1363  if (type == MUTT_POP)
1364  {
1365  mutt_error(_("Can't save message to POP mailbox"));
1366  return 1;
1367  }
1368 #endif
1369 
1370  if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1371  {
1372  const bool c_confirm_append = cs_subset_bool(NeoMutt->sub, "confirm_append");
1373  if (c_confirm_append)
1374  {
1375  struct Buffer *tmp = mutt_buffer_pool_get();
1376  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1378  if (ans == MUTT_NO)
1379  rc = 1;
1380  else if (ans == MUTT_ABORT)
1381  rc = -1;
1383  }
1384  }
1385 
1386 #ifdef USE_NNTP
1387  if (type == MUTT_NNTP)
1388  {
1389  mutt_error(_("Can't save message to news server"));
1390  return 0;
1391  }
1392 #endif
1393 
1394  if (stat(s, st) != -1)
1395  {
1396  if (type == MUTT_MAILBOX_ERROR)
1397  {
1398  mutt_error(_("%s is not a mailbox"), s);
1399  return 1;
1400  }
1401  }
1402  else if (type != MUTT_IMAP)
1403  {
1404  st->st_mtime = 0;
1405  st->st_atime = 0;
1406 
1407  /* pathname does not exist */
1408  if (errno == ENOENT)
1409  {
1410  const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1411  if (c_confirm_create)
1412  {
1413  struct Buffer *tmp = mutt_buffer_pool_get();
1414  mutt_buffer_printf(tmp, _("Create %s?"), s);
1416  if (ans == MUTT_NO)
1417  rc = 1;
1418  else if (ans == MUTT_ABORT)
1419  rc = -1;
1421  }
1422 
1423  /* user confirmed with MUTT_YES or set `$confirm_create` */
1424  if (rc == 0)
1425  {
1426  /* create dir recursively */
1427  char *tmp_path = mutt_path_dirname(s);
1428  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1429  {
1430  /* report failure & abort */
1431  mutt_perror(s);
1432  FREE(&tmp_path);
1433  return 1;
1434  }
1435  FREE(&tmp_path);
1436  }
1437  }
1438  else
1439  {
1440  mutt_perror(s);
1441  return 1;
1442  }
1443  }
1444 
1446  return rc;
1447 }
1448 
1455 void mutt_sleep(short s)
1456 {
1457  const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
1458  if (c_sleep_time > s)
1459  sleep(c_sleep_time);
1460  else if (s)
1461  sleep(s);
1462 }
1463 
1470 const char *mutt_make_version(void)
1471 {
1472  static char vstring[256];
1473  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1474  return vstring;
1475 }
1476 
1484 void mutt_encode_path(struct Buffer *buf, const char *src)
1485 {
1486  char *p = mutt_str_dup(src);
1487  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
1488  int rc = mutt_ch_convert_string(&p, c_charset, "us-ascii", MUTT_ICONV_NO_FLAGS);
1489  size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1490 
1491  /* convert the path to POSIX "Portable Filename Character Set" */
1492  for (size_t i = 0; i < len; i++)
1493  {
1494  if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1495  {
1496  buf->data[i] = '_';
1497  }
1498  }
1499  FREE(&p);
1500 }
1501 
1510 int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1511 {
1512  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1513  char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(xdg_defaults[type]);
1514  char *x = xdg; /* mutt_str_sep() changes xdg, so free x instead later */
1515  char *token = NULL;
1516  int rc = 0;
1517 
1518  while ((token = mutt_str_sep(&xdg, ":")))
1519  {
1520  if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1521  continue;
1523  if (access(mutt_buffer_string(buf), F_OK) == 0)
1524  {
1525  rc = 1;
1526  break;
1527  }
1528 
1529  if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1530  continue;
1532  if (access(mutt_buffer_string(buf), F_OK) == 0)
1533  {
1534  rc = 1;
1535  break;
1536  }
1537  }
1538 
1539  FREE(&x);
1540  return rc;
1541 }
1542 
1549 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1550 {
1551  enum MailboxType mb_type = mx_path_probe(path);
1552 
1553  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1554  if (mb_type == MUTT_IMAP)
1555  imap_get_parent_path(path, buf, buflen);
1556  else if (mb_type == MUTT_NOTMUCH)
1557  mutt_str_copy(buf, c_folder, buflen);
1558  else
1559  {
1560  mutt_str_copy(buf, path, buflen);
1561  int n = mutt_str_len(buf);
1562  if (n == 0)
1563  return;
1564 
1565  /* remove any final trailing '/' */
1566  if (buf[n - 1] == '/')
1567  buf[n - 1] = '\0';
1568 
1569  /* Remove everything until the next slash */
1570  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1571  ; // do nothing
1572 
1573  if (n > 0)
1574  buf[n] = '\0';
1575  else
1576  {
1577  buf[0] = '/';
1578  buf[1] = '\0';
1579  }
1580  }
1581 }
1582 
1605 int mutt_inbox_cmp(const char *a, const char *b)
1606 {
1607  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1608  if ((a[0] == '+') && (b[0] == '+'))
1609  {
1610  return mutt_istr_equal(a + 1, "inbox") ? -1 :
1611  mutt_istr_equal(b + 1, "inbox") ? 1 :
1612  0;
1613  }
1614 
1615  const char *a_end = strrchr(a, '/');
1616  const char *b_end = strrchr(b, '/');
1617 
1618  /* If one path contains a '/', but not the other */
1619  if ((!a_end) ^ (!b_end))
1620  return 0;
1621 
1622  /* If neither path contains a '/' */
1623  if (!a_end)
1624  return 0;
1625 
1626  /* Compare the subpaths */
1627  size_t a_len = a_end - a;
1628  size_t b_len = b_end - b;
1629  size_t min = MIN(a_len, b_len);
1630  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1631  (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1632 
1633  if (!same)
1634  return 0;
1635 
1636  if (mutt_istr_equal(&a[min + 1], "inbox"))
1637  return -1;
1638 
1639  if (mutt_istr_equal(&b[min + 1], "inbox"))
1640  return 1;
1641 
1642  return 0;
1643 }
1644 
1651 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1652 {
1653  if (!buf || !path)
1654  return;
1655 
1656  mutt_buffer_reset(buf);
1657 
1658  for (; *path; path++)
1659  {
1660  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1661  mutt_buffer_addch(buf, '_');
1662  else
1663  mutt_buffer_addch(buf, *path);
1664  }
1665 }
1666 
1673 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1674 {
1675  if (!buf || (buflen == 0))
1676  return;
1677 
1678  const bool c_size_show_bytes = cs_subset_bool(NeoMutt->sub, "size_show_bytes");
1679  const bool c_size_show_fractions = cs_subset_bool(NeoMutt->sub, "size_show_fractions");
1680  const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
1681  const bool c_size_units_on_left = cs_subset_bool(NeoMutt->sub, "size_units_on_left");
1682 
1683  if (c_size_show_bytes && (num < 1024))
1684  {
1685  snprintf(buf, buflen, "%d", (int) num);
1686  }
1687  else if (num == 0)
1688  {
1689  mutt_str_copy(buf, c_size_units_on_left ? "K0" : "0K", buflen);
1690  }
1691  else if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
1692  {
1693  snprintf(buf, buflen, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
1694  (num < 103) ? 0.1 : (num / 1024.0));
1695  }
1696  else if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
1697  {
1698  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1699  snprintf(buf, buflen, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1700  }
1701  else if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
1702  {
1703  snprintf(buf, buflen, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1704  }
1705  else /* 10M+ */
1706  {
1707  /* (10433332 + 52428) / 1048576 = 10 */
1708  snprintf(buf, buflen, c_size_units_on_left ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1709  }
1710 }
1711 
1719 void add_to_stailq(struct ListHead *head, const char *str)
1720 {
1721  /* don't add a NULL or empty string to the list */
1722  if (!str || (*str == '\0'))
1723  return;
1724 
1725  /* check to make sure the item is not already on this list */
1726  struct ListNode *np = NULL;
1727  STAILQ_FOREACH(np, head, entries)
1728  {
1729  if (mutt_istr_equal(str, np->data))
1730  {
1731  return;
1732  }
1733  }
1734  mutt_list_insert_tail(head, mutt_str_dup(str));
1735 }
1736 
1744 void remove_from_stailq(struct ListHead *head, const char *str)
1745 {
1746  if (mutt_str_equal("*", str))
1747  mutt_list_free(head); /* "unCMD *" means delete all current entries */
1748  else
1749  {
1750  struct ListNode *np = NULL, *tmp = NULL;
1751  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1752  {
1753  if (mutt_istr_equal(str, np->data))
1754  {
1755  STAILQ_REMOVE(head, np, ListNode, entries);
1756  FREE(&np->data);
1757  FREE(&np);
1758  break;
1759  }
1760  }
1761  }
1762 }
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
Email Address Handling.
Email Aliases.
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:280
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:63
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
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:250
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:292
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:238
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:223
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:179
void mutt_buffer_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:466
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:158
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:445
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
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:372
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define MoreArgs(buf)
Definition: buffer.h:40
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
Convenience wrapper for the core headers.
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:544
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:601
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:855
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:355
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:904
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
Structs that make up an email.
Enter a string.
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: window.c:180
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:43
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:666
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:930
const char filename_safe_chars[]
Definition: file.c:61
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:647
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
Flags to control mutt_expando_format()
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
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)
Definition: format_flags.h:64
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
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:777
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2400
Convenience wrapper for the gui headers.
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:196
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:731
Parse and execute user-defined hooks.
IMAP network mailbox.
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:161
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:589
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2440
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:273
Config/command parsing.
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
MailboxType
Supported mailbox formats.
Definition: mailbox.h:41
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition: mailbox.h:43
@ MUTT_POP
'POP3' Mailbox type
Definition: mailbox.h:52
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:44
int mutt_mb_charlen(const char *s, int *width)
Count the bytes in a (multibyte) character.
Definition: mbyte.c:54
#define IsPrint(ch)
Definition: mbyte.h:38
#define FREE(x)
Definition: memory.h:43
#define MIN(a, b)
Definition: memory.h:31
#define MAX(a, b)
Definition: memory.h:30
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:247
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:752
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:376
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
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:614
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
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:473
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:384
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:904
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:629
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
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:501
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurance of any of delim characters in *stringp.
Definition: string.c:183
Many unsorted constants and some structs.
#define MUTT_COMP_FILE
File completion (in browser)
Definition: mutt.h:55
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:67
#define PATH_MAX
Definition: mutt.h:40
#define MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:62
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:56
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:58
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:57
Hundreds of global variables to back the user variables.
char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:55
char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:54
const char * GitVer
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1549
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1744
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1719
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:135
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:405
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:495
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:123
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:1605
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
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1455
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:707
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:730
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:336
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1311
static const char * xdg_env_vars[]
Definition: muttlib.c:66
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1510
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1673
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1470
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:522
static const char * xdg_defaults[]
Definition: muttlib.c:71
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:84
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:361
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:758
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
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1651
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1484
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1356
Some miscellaneous functions.
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:75
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:184
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1327
API for mailboxes.
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define WithCrypto
Definition: lib.h:116
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Prototypes for many functions.
XdgType
XDG variable types.
Definition: protos.h:43
@ XDG_CONFIG_HOME
XDG home dir: ~/.config.
Definition: protos.h:44
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition: protos.h:45
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:54
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
#define TAILQ_EMPTY(head)
Definition: queue.h:721
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:129
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
An email address.
Definition: address.h:36
char * mailbox
Mailbox and host address.
Definition: address.h:38
The body of an email.
Definition: body.h:36
char * subtype
content-type subtype
Definition: body.h:60
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
The envelope/body of an email.
Definition: email.h:37
struct Envelope * env
Envelope information.
Definition: email.h:66
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
struct AddressList from
Email's 'From' list.
Definition: envelope.h:59
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Cached regular expression.
Definition: regex3.h:89
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
UrlScheme
All recognised Url types.
Definition: url.h:34
@ U_NOTMUCH
Url is notmuch://.
Definition: url.h:46
@ U_UNKNOWN
Url wasn't recognised.
Definition: url.h:35
@ U_FILE
Url is file://.
Definition: url.h:36
@ U_IMAP
Url is imap://.
Definition: url.h:39
@ U_IMAPS
Url is imaps://.
Definition: url.h:40