NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
muttlib.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <ctype.h>
33#include <errno.h>
34#include <limits.h>
35#include <pwd.h>
36#include <stdbool.h>
37#include <stdint.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sys/stat.h>
42#include <unistd.h>
43#include "mutt/lib.h"
44#include "address/lib.h"
45#include "config/lib.h"
46#include "email/lib.h"
47#include "core/lib.h"
48#include "alias/lib.h"
49#include "gui/lib.h"
50#include "mutt.h"
51#include "muttlib.h"
52#include "browser/lib.h"
53#include "editor/lib.h"
54#include "history/lib.h"
55#include "ncrypt/lib.h"
56#include "parse/lib.h"
57#include "question/lib.h"
58#include "format_flags.h"
59#include "globals.h" // IWYU pragma: keep
60#include "hook.h"
61#include "mx.h"
62#include "protos.h"
63#ifdef USE_IMAP
64#include "imap/lib.h"
65#endif
66
68static const char *XdgEnvVars[] = {
69 [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
70 [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
71};
72
74static const char *XdgDefaults[] = {
75 [XDG_CONFIG_HOME] = "~/.config",
76 [XDG_CONFIG_DIRS] = "/etc/xdg",
77};
78
87void mutt_adv_mktemp(struct Buffer *buf)
88{
89 if (!(buf->data && (buf->data[0] != '\0')))
90 {
91 buf_mktemp(buf);
92 }
93 else
94 {
95 struct Buffer *prefix = buf_pool_get();
96 buf_strcpy(prefix, buf->data);
97 mutt_file_sanitize_filename(prefix->data, true);
98 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
99 buf_printf(buf, "%s/%s", NONULL(c_tmp_dir), buf_string(prefix));
100
101 struct stat st = { 0 };
102 if ((lstat(buf_string(buf), &st) == -1) && (errno == ENOENT))
103 goto out;
104
105 char *suffix = strchr(prefix->data, '.');
106 if (suffix)
107 {
108 *suffix = '\0';
109 suffix++;
110 }
111 buf_mktemp_pfx_sfx(buf, prefix->data, suffix);
112
113 out:
114 buf_pool_release(&prefix);
115 }
116}
117
126char *mutt_expand_path(char *buf, size_t buflen)
127{
128 return mutt_expand_path_regex(buf, buflen, false);
129}
130
138void buf_expand_path_regex(struct Buffer *buf, bool regex)
139{
140 const char *s = NULL;
141 const char *tail = "";
142
143 bool recurse = false;
144
145 struct Buffer *p = buf_pool_get();
146 struct Buffer *q = buf_pool_get();
147 struct Buffer *tmp = buf_pool_get();
148
149 do
150 {
151 recurse = false;
152 s = buf_string(buf);
153
154 switch (*s)
155 {
156 case '~':
157 {
158 if ((s[1] == '/') || (s[1] == '\0'))
159 {
161 tail = s + 1;
162 }
163 else
164 {
165 char *t = strchr(s + 1, '/');
166 if (t)
167 *t = '\0';
168
169 struct passwd *pw = getpwnam(s + 1);
170 if (pw)
171 {
172 buf_strcpy(p, pw->pw_dir);
173 if (t)
174 {
175 *t = '/';
176 tail = t;
177 }
178 else
179 {
180 tail = "";
181 }
182 }
183 else
184 {
185 /* user not found! */
186 if (t)
187 *t = '/';
188 buf_reset(p);
189 tail = s;
190 }
191 }
192 break;
193 }
194
195 case '=':
196 case '+':
197 {
198 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
199 enum MailboxType mb_type = mx_path_probe(c_folder);
200
201 /* if folder = {host} or imap[s]://host/: don't append slash */
202 if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
203 (c_folder[strlen(c_folder) - 1] == '/')))
204 {
205 buf_strcpy(p, NONULL(c_folder));
206 }
207 else if (mb_type == MUTT_NOTMUCH)
208 {
209 buf_strcpy(p, NONULL(c_folder));
210 }
211 else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
212 {
213 buf_strcpy(p, NONULL(c_folder));
214 }
215 else
216 {
217 buf_printf(p, "%s/", NONULL(c_folder));
218 }
219
220 tail = s + 1;
221 break;
222 }
223
224 /* elm compatibility, @ expands alias to user name */
225
226 case '@':
227 {
228 struct AddressList *al = alias_lookup(s + 1);
229 if (al && !TAILQ_EMPTY(al))
230 {
231 struct Email *e = email_new();
232 e->env = mutt_env_new();
233 mutt_addrlist_copy(&e->env->from, al, false);
234 mutt_addrlist_copy(&e->env->to, al, false);
235
236 /* TODO: fix mutt_default_save() to use Buffer */
238 mutt_default_save(p->data, p->dsize, e);
239 buf_fix_dptr(p);
240
241 email_free(&e);
242 /* Avoid infinite recursion if the resulting folder starts with '@' */
243 if (*p->data != '@')
244 recurse = true;
245
246 tail = "";
247 }
248 break;
249 }
250
251 case '>':
252 {
253 const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
254 buf_strcpy(p, c_mbox);
255 tail = s + 1;
256 break;
257 }
258
259 case '<':
260 {
261 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
262 buf_strcpy(p, c_record);
263 tail = s + 1;
264 break;
265 }
266
267 case '!':
268 {
269 if (s[1] == '!')
270 {
272 tail = s + 2;
273 }
274 else
275 {
276 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
277 buf_strcpy(p, c_spool_file);
278 tail = s + 1;
279 }
280 break;
281 }
282
283 case '-':
284 {
286 tail = s + 1;
287 break;
288 }
289
290 case '^':
291 {
293 tail = s + 1;
294 break;
295 }
296
297 default:
298 {
299 buf_reset(p);
300 tail = s;
301 }
302 }
303
304 if (regex && *(buf_string(p)) && !recurse)
305 {
307 buf_printf(tmp, "%s%s", buf_string(q), tail);
308 }
309 else
310 {
311 buf_printf(tmp, "%s%s", buf_string(p), tail);
312 }
313
314 buf_copy(buf, tmp);
315 } while (recurse);
316
319 buf_pool_release(&tmp);
320
321#ifdef USE_IMAP
322 /* Rewrite IMAP path in canonical form - aids in string comparisons of
323 * folders. May possibly fail, in which case buf should be the same. */
324 if (imap_path_probe(buf_string(buf), NULL) == MUTT_IMAP)
325 imap_expand_path(buf);
326#endif
327}
328
335void buf_expand_path(struct Buffer *buf)
336{
337 buf_expand_path_regex(buf, false);
338}
339
349char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
350{
351 struct Buffer *tmp = buf_pool_get();
352
353 buf_addstr(tmp, NONULL(buf));
354 buf_expand_path_regex(tmp, regex);
355 mutt_str_copy(buf, buf_string(tmp), buflen);
356
357 buf_pool_release(&tmp);
358
359 return buf;
360}
361
374char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
375{
376 regmatch_t pat_match[1];
377 size_t pwnl;
378 char *p = NULL;
379
380 if (!pw || !pw->pw_gecos)
381 return NULL;
382
383 memset(dest, 0, destlen);
384
385 const struct Regex *c_gecos_mask = cs_subset_regex(NeoMutt->sub, "gecos_mask");
386 if (mutt_regex_capture(c_gecos_mask, pw->pw_gecos, 1, pat_match))
387 {
388 mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
389 MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
390 }
391 else if ((p = strchr(pw->pw_gecos, ',')))
392 {
393 mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
394 }
395 else
396 {
397 mutt_str_copy(dest, pw->pw_gecos, destlen);
398 }
399
400 pwnl = strlen(pw->pw_name);
401
402 for (int idx = 0; dest[idx]; idx++)
403 {
404 if (dest[idx] == '&')
405 {
406 memmove(&dest[idx + pwnl], &dest[idx + 1],
407 MAX((ssize_t) (destlen - idx - pwnl - 1), 0));
408 memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
409 dest[idx] = toupper((unsigned char) dest[idx]);
410 }
411 }
412
413 return dest;
414}
415
423{
424 switch (m->type)
425 {
426 case TYPE_TEXT:
427 if (mutt_istr_equal("plain", m->subtype))
428 return false;
429 break;
430 case TYPE_APPLICATION:
432 return false;
434 return false;
435 break;
436
437 case TYPE_MULTIPART:
438 case TYPE_MESSAGE:
439 return false;
440 }
441
442 return true;
443}
444
450bool mutt_is_text_part(struct Body *b)
451{
452 int t = b->type;
453 char *s = b->subtype;
454
456 return false;
457
458 if (t == TYPE_TEXT)
459 return true;
460
461 if (t == TYPE_MESSAGE)
462 {
463 if (mutt_istr_equal("delivery-status", s))
464 return true;
465 }
466
467 if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
468 {
469 if (mutt_istr_equal("pgp-keys", s))
470 return true;
471 }
472
473 return false;
474}
475
483void mutt_pretty_mailbox(char *buf, size_t buflen)
484{
485 if (!buf)
486 return;
487
488 char *p = buf, *q = buf;
489 size_t len;
490 enum UrlScheme scheme;
491 char tmp[PATH_MAX] = { 0 };
492
493 scheme = url_check_scheme(buf);
494
495 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
496 if ((scheme == U_IMAP) || (scheme == U_IMAPS))
497 {
498 imap_pretty_mailbox(buf, buflen, c_folder);
499 return;
500 }
501
502 if (scheme == U_NOTMUCH)
503 return;
504
505 /* if buf is an url, only collapse path component */
506 if (scheme != U_UNKNOWN)
507 {
508 p = strchr(buf, ':') + 1;
509 if (mutt_strn_equal(p, "//", 2))
510 q = strchr(p + 2, '/');
511 if (!q)
512 q = strchr(p, '\0');
513 p = q;
514 }
515
516 /* cleanup path */
517 if (strstr(p, "//") || strstr(p, "/./"))
518 {
519 /* first attempt to collapse the pathname, this is more
520 * lightweight than realpath() and doesn't resolve links */
521 while (*p)
522 {
523 if ((p[0] == '/') && (p[1] == '/'))
524 {
525 *q++ = '/';
526 p += 2;
527 }
528 else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
529 {
530 *q++ = '/';
531 p += 3;
532 }
533 else
534 {
535 *q++ = *p++;
536 }
537 }
538 *q = '\0';
539 }
540 else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
541 realpath(p, tmp))
542 {
543 mutt_str_copy(p, tmp, buflen - (p - buf));
544 }
545
546 if ((len = mutt_str_startswith(buf, c_folder)) && (buf[len] == '/'))
547 {
548 *buf++ = '=';
549 memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
550 }
551 else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
552 {
553 *buf++ = '~';
554 memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
555 }
556}
557
562void buf_pretty_mailbox(struct Buffer *buf)
563{
564 if (!buf || !buf->data)
565 return;
566 /* This reduces the size of the Buffer, so we can pass it through.
567 * We adjust the size just to make sure buf->data is not NULL though */
568 buf_alloc(buf, PATH_MAX);
569 mutt_pretty_mailbox(buf->data, buf->dsize);
570 buf_fix_dptr(buf);
571}
572
584int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
585 enum SaveAttach *opt, char **directory)
586{
587 struct stat st = { 0 };
588
589 buf_strcpy(fname, path);
590 if (access(buf_string(fname), F_OK) != 0)
591 return 0;
592 if (stat(buf_string(fname), &st) != 0)
593 return -1;
594 if (S_ISDIR(st.st_mode))
595 {
596 enum QuadOption ans = MUTT_NO;
597 if (directory)
598 {
599 switch (mw_multi_choice
600 /* L10N: Means "The path you specified as the destination file is a directory."
601 See the msgid "Save to file: " (alias.c, recvattach.c)
602 These three letters correspond to the choices in the string. */
603 (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
604 {
605 case 3: /* all */
606 mutt_str_replace(directory, buf_string(fname));
607 break;
608 case 1: /* yes */
609 FREE(directory);
610 break;
611 case -1: /* abort */
612 FREE(directory);
613 return -1;
614 case 2: /* no */
615 FREE(directory);
616 return 1;
617 }
618 }
619 /* L10N: Means "The path you specified as the destination file is a directory."
620 See the msgid "Save to file: " (alias.c, recvattach.c) */
621 else if ((ans = query_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
622 return (ans == MUTT_NO) ? 1 : -1;
623
624 struct Buffer *tmp = buf_pool_get();
625 buf_strcpy(tmp, mutt_path_basename(NONULL(attname)));
626 struct FileCompletionData cdata = { false, NULL, NULL, NULL };
627 if ((mw_get_field(_("File under directory: "), tmp, MUTT_COMP_CLEAR,
628 HC_FILE, &CompleteMailboxOps, &cdata) != 0) ||
629 buf_is_empty(tmp))
630 {
631 buf_pool_release(&tmp);
632 return (-1);
633 }
634 buf_concat_path(fname, path, buf_string(tmp));
635 buf_pool_release(&tmp);
636 }
637
638 if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(buf_string(fname), F_OK) == 0))
639 {
640 char buf[4096] = { 0 };
641 snprintf(buf, sizeof(buf), "%s - %s", buf_string(fname),
642 // L10N: Options for: File %s exists, (o)verwrite, (a)ppend, or (c)ancel?
643 _("File exists, (o)verwrite, (a)ppend, or (c)ancel?"));
644 switch (mw_multi_choice(buf, _("oac")))
645 {
646 case -1: /* abort */
647 return -1;
648 case 3: /* cancel */
649 return 1;
650
651 case 2: /* append */
652 *opt = MUTT_SAVE_APPEND;
653 break;
654 case 1: /* overwrite */
655 *opt = MUTT_SAVE_OVERWRITE;
656 break;
657 }
658 }
659 return 0;
660}
661
671void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
672{
673 if (addr && addr->mailbox)
674 {
675 mutt_str_copy(buf, buf_string(addr->mailbox), buflen);
676 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
677 if (!c_save_address)
678 {
679 char *p = strpbrk(buf, "%@");
680 if (p)
681 *p = '\0';
682 }
683 mutt_str_lower(buf);
684 }
685 else
686 {
687 *buf = '\0';
688 }
689}
690
696void buf_save_path(struct Buffer *dest, const struct Address *a)
697{
698 if (a && a->mailbox)
699 {
700 buf_copy(dest, a->mailbox);
701 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
702 if (!c_save_address)
703 {
704 char *p = strpbrk(dest->data, "%@");
705 if (p)
706 {
707 *p = '\0';
708 buf_fix_dptr(dest);
709 }
710 }
711 mutt_str_lower(dest->data);
712 }
713 else
714 {
715 buf_reset(dest);
716 }
717}
718
726void mutt_safe_path(struct Buffer *dest, const struct Address *a)
727{
728 buf_save_path(dest, a);
729 for (char *p = dest->data; *p; p++)
730 if ((*p == '/') || isspace(*p) || !IsPrint((unsigned char) *p))
731 *p = '_';
732}
733
745void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
746 format_t callback, intptr_t data, MuttFormatFlags flags)
747{
748 char prefix[128], tmp[1024];
749 char *cp = NULL, *wptr = buf;
750 char ch;
751 char if_str[128], else_str[128];
752 size_t wlen, count, len, wid;
753 FILE *fp_filter = NULL;
754 char *recycler = NULL;
755
756 char src2[1024];
757 mutt_str_copy(src2, src, mutt_str_len(src) + 1);
758 src = src2;
759
760 const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
761 const char *const c_arrow_string = cs_subset_string(NeoMutt->sub, "arrow_string");
762 const int arrow_width = mutt_strwidth(c_arrow_string);
763
764 prefix[0] = '\0';
765 buflen--; /* save room for the terminal \0 */
766 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ? arrow_width + 1 : 0;
767 col += wlen;
768
769 if ((flags & MUTT_FORMAT_NOFILTER) == 0)
770 {
771 int off = -1;
772
773 /* Do not consider filters if no pipe at end */
774 int n = mutt_str_len(src);
775 if ((n > 1) && (src[n - 1] == '|'))
776 {
777 /* Scan backwards for backslashes */
778 off = n;
779 while ((off > 0) && (src[off - 2] == '\\'))
780 off--;
781 }
782
783 /* If number of backslashes is even, the pipe is real. */
784 /* n-off is the number of backslashes. */
785 if ((off > 0) && (((n - off) % 2) == 0))
786 {
787 char srccopy[1024] = { 0 };
788 int i = 0;
789
790 mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
791
792 strncpy(srccopy, src, n);
793 srccopy[n - 1] = '\0';
794
795 /* prepare Buffers */
796 struct Buffer srcbuf = buf_make(0);
797 buf_addstr(&srcbuf, srccopy);
798 /* note: we are resetting dptr and *reading* from the buffer, so we don't
799 * want to use buf_reset(). */
800 buf_seek(&srcbuf, 0);
801 struct Buffer word = buf_make(0);
802 struct Buffer cmd = buf_make(0);
803
804 /* Iterate expansions across successive arguments */
805 do
806 {
807 /* Extract the command name and copy to command line */
808 mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
809 if (word.data)
810 *word.data = '\0';
811 parse_extract_token(&word, &srcbuf, TOKEN_NO_FLAGS);
812 mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
813 buf_addch(&cmd, '\'');
814 mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
815 data, flags | MUTT_FORMAT_NOFILTER);
816 for (char *p = tmp; p && (*p != '\0'); p++)
817 {
818 if (*p == '\'')
819 {
820 /* shell quoting doesn't permit escaping a single quote within
821 * single-quoted material. double-quoting instead will lead
822 * shell variable expansions, so break out of the single-quoted
823 * span, insert a double-quoted single quote, and resume. */
824 buf_addstr(&cmd, "'\"'\"'");
825 }
826 else
827 {
828 buf_addch(&cmd, *p);
829 }
830 }
831 buf_addch(&cmd, '\'');
832 buf_addch(&cmd, ' ');
833 } while (MoreArgs(&srcbuf));
834
835 mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
836
837 col -= wlen; /* reset to passed in value */
838 wptr = buf; /* reset write ptr */
839 pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL, EnvList);
840 if (pid != -1)
841 {
842 int rc;
843
844 n = fread(buf, 1, buflen /* already decremented */, fp_filter);
845 mutt_file_fclose(&fp_filter);
846 rc = filter_wait(pid);
847 if (rc != 0)
848 mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
849 if (n > 0)
850 {
851 buf[n] = '\0';
852 while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
853 buf[--n] = '\0';
854 mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
855
856 /* If the result ends with '%', this indicates that the filter
857 * generated %-tokens that neomutt can expand. Eliminate the '%'
858 * marker and recycle the string through mutt_expando_format().
859 * To literally end with "%", use "%%". */
860 if ((n > 0) && (buf[n - 1] == '%'))
861 {
862 n--;
863 buf[n] = '\0'; /* remove '%' */
864 if ((n > 0) && (buf[n - 1] != '%'))
865 {
866 recycler = mutt_str_dup(buf);
867 if (recycler)
868 {
869 /* buflen is decremented at the start of this function
870 * to save space for the terminal nul char. We can add
871 * it back for the recursive call since the expansion of
872 * format pipes does not try to append a nul itself. */
873 mutt_expando_format(buf, buflen + 1, col, cols, recycler,
874 callback, data, flags);
875 FREE(&recycler);
876 }
877 }
878 }
879 }
880 else
881 {
882 /* read error */
883 mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
884 strerror(errno), errno);
885 *wptr = '\0';
886 }
887 }
888 else
889 {
890 /* Filter failed; erase write buffer */
891 *wptr = '\0';
892 }
893
894 buf_dealloc(&cmd);
895 buf_dealloc(&srcbuf);
896 buf_dealloc(&word);
897 return;
898 }
899 }
900
901 while (*src && (wlen < buflen))
902 {
903 if (*src == '%')
904 {
905 if (*++src == '%')
906 {
907 *wptr++ = '%';
908 wlen++;
909 col++;
910 src++;
911 continue;
912 }
913
914 if (*src == '?')
915 {
916 /* change original %? to new %< notation */
917 /* %?x?y&z? to %<x?y&z> where y and z are nestable */
918 char *p = (char *) src;
919 *p = '<';
920 /* skip over "x" */
921 for (; *p && (*p != '?'); p++)
922 ; // do nothing
923
924 /* nothing */
925 if (*p == '?')
926 p++;
927 /* fix up the "y&z" section */
928 for (; *p && (*p != '?'); p++)
929 {
930 /* escape '<' and '>' to work inside nested-if */
931 if ((*p == '<') || (*p == '>'))
932 {
933 memmove(p + 2, p, mutt_str_len(p) + 1);
934 *p++ = '\\';
935 *p++ = '\\';
936 }
937 }
938 if (*p == '?')
939 *p = '>';
940 }
941
942 if (*src == '<')
943 {
944 flags |= MUTT_FORMAT_OPTIONAL;
945 ch = *(++src); /* save the character to switch on */
946 src++;
947 cp = prefix;
948 count = 0;
949 while ((count < (sizeof(prefix) - 1)) && (*src != '\0') && (*src != '?'))
950 {
951 *cp++ = *src++;
952 count++;
953 }
954 *cp = '\0';
955 }
956 else
957 {
958 flags &= ~MUTT_FORMAT_OPTIONAL;
959
960 /* eat the format string */
961 cp = prefix;
962 count = 0;
963 while ((count < (sizeof(prefix) - 1)) && strchr("0123456789.-=", *src))
964 {
965 *cp++ = *src++;
966 count++;
967 }
968 *cp = '\0';
969
970 if (*src == '\0')
971 break; /* bad format */
972
973 ch = *src++; /* save the character to switch on */
974 }
975
976 if (flags & MUTT_FORMAT_OPTIONAL)
977 {
978 int lrbalance;
979
980 if (*src != '?')
981 break; /* bad format */
982 src++;
983
984 /* eat the 'if' part of the string */
985 cp = if_str;
986 count = 0;
987 lrbalance = 1;
988 while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
989 {
990 if ((src[0] == '%') && (src[1] == '>'))
991 {
992 /* This is a padding expando; copy two chars and carry on */
993 *cp++ = *src++;
994 *cp++ = *src++;
995 count += 2;
996 continue;
997 }
998
999 if (*src == '\\')
1000 {
1001 src++;
1002 *cp++ = *src++;
1003 }
1004 else if ((src[0] == '%') && (src[1] == '<'))
1005 {
1006 lrbalance++;
1007 }
1008 else if (src[0] == '>')
1009 {
1010 lrbalance--;
1011 }
1012 if (lrbalance == 0)
1013 break;
1014 if ((lrbalance == 1) && (src[0] == '&'))
1015 break;
1016 *cp++ = *src++;
1017 count++;
1018 }
1019 *cp = '\0';
1020
1021 /* eat the 'else' part of the string (optional) */
1022 if (*src == '&')
1023 src++; /* skip the & */
1024 cp = else_str;
1025 count = 0;
1026 while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1027 {
1028 if ((src[0] == '%') && (src[1] == '>'))
1029 {
1030 /* This is a padding expando; copy two chars and carry on */
1031 *cp++ = *src++;
1032 *cp++ = *src++;
1033 count += 2;
1034 continue;
1035 }
1036
1037 if (*src == '\\')
1038 {
1039 src++;
1040 *cp++ = *src++;
1041 }
1042 else if ((src[0] == '%') && (src[1] == '<'))
1043 {
1044 lrbalance++;
1045 }
1046 else if (src[0] == '>')
1047 {
1048 lrbalance--;
1049 }
1050 if (lrbalance == 0)
1051 break;
1052 if ((lrbalance == 1) && (src[0] == '&'))
1053 break;
1054 *cp++ = *src++;
1055 count++;
1056 }
1057 *cp = '\0';
1058
1059 if ((*src == '\0'))
1060 break; /* bad format */
1061
1062 src++; /* move past the trailing '>' (formerly '?') */
1063 }
1064
1065 /* handle generic cases first */
1066 if ((ch == '>') || (ch == '*'))
1067 {
1068 /* %>X: right justify to EOL, left takes precedence
1069 * %*X: right justify to EOL, right takes precedence */
1070 int soft = ch == '*';
1071 int pl, pw;
1072 pl = mutt_mb_charlen(src, &pw);
1073 if (pl <= 0)
1074 {
1075 pl = 1;
1076 pw = 1;
1077 }
1078
1079 /* see if there's room to add content, else ignore */
1080 if (((col < cols) && (wlen < buflen)) || soft)
1081 {
1082 int pad;
1083
1084 /* get contents after padding */
1085 mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1086 len = mutt_str_len(tmp);
1087 wid = mutt_strwidth(tmp);
1088
1089 pad = (cols - col - wid) / pw;
1090 if (pad >= 0)
1091 {
1092 /* try to consume as many columns as we can, if we don't have
1093 * memory for that, use as much memory as possible */
1094 if (wlen + (pad * pl) + len > buflen)
1095 {
1096 pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1097 }
1098 else
1099 {
1100 /* Add pre-spacing to make multi-column pad characters and
1101 * the contents after padding line up */
1102 while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1103 {
1104 *wptr++ = ' ';
1105 wlen++;
1106 col++;
1107 }
1108 }
1109 while (pad-- > 0)
1110 {
1111 memcpy(wptr, src, pl);
1112 wptr += pl;
1113 wlen += pl;
1114 col += pw;
1115 }
1116 }
1117 else if (soft)
1118 {
1119 int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
1120 arrow_width + 1 :
1121 0;
1122 int avail_cols = (cols > offset) ? (cols - offset) : 0;
1123 /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1124 *wptr = '\0';
1125 /* make sure right part is at most as wide as display */
1126 len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1127 /* truncate left so that right part fits completely in */
1128 wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1129 wptr = buf + wlen;
1130 /* Multi-column characters may be truncated in the middle.
1131 * Add spacing so the right hand side lines up. */
1132 while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1133 {
1134 *wptr++ = ' ';
1135 wlen++;
1136 col++;
1137 }
1138 }
1139 if ((len + wlen) > buflen)
1140 len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1141 memcpy(wptr, tmp, len);
1142 wptr += len;
1143 }
1144 break; /* skip rest of input */
1145 }
1146 else if (ch == '|')
1147 {
1148 /* pad to EOL */
1149 int pl, pw;
1150 pl = mutt_mb_charlen(src, &pw);
1151 if (pl <= 0)
1152 {
1153 pl = 1;
1154 pw = 1;
1155 }
1156
1157 /* see if there's room to add content, else ignore */
1158 if ((col < cols) && (wlen < buflen))
1159 {
1160 int c = (cols - col) / pw;
1161 if ((c > 0) && ((wlen + (c * pl)) > buflen))
1162 c = ((signed) (buflen - wlen)) / pl;
1163 while (c > 0)
1164 {
1165 memcpy(wptr, src, pl);
1166 wptr += pl;
1167 wlen += pl;
1168 col += pw;
1169 c--;
1170 }
1171 }
1172 break; /* skip rest of input */
1173 }
1174 else
1175 {
1176 bool to_lower = false;
1177 bool no_dots = false;
1178
1179 while ((ch == '_') || (ch == ':'))
1180 {
1181 if (ch == '_')
1182 to_lower = true;
1183 else if (ch == ':')
1184 no_dots = true;
1185
1186 ch = *src++;
1187 }
1188
1189 /* use callback function to handle this case */
1190 *tmp = '\0';
1191 src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1192 else_str, data, flags);
1193
1194 if (to_lower)
1195 mutt_str_lower(tmp);
1196 if (no_dots)
1197 {
1198 char *p = tmp;
1199 for (; *p; p++)
1200 if (*p == '.')
1201 *p = '_';
1202 }
1203
1204 len = mutt_str_len(tmp);
1205 if ((len + wlen) > buflen)
1206 len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1207
1208 memcpy(wptr, tmp, len);
1209 wptr += len;
1210 wlen += len;
1211 col += mutt_strwidth(tmp);
1212 }
1213 }
1214 else if (*src == '\\')
1215 {
1216 if (!*++src)
1217 break;
1218 switch (*src)
1219 {
1220 case 'f':
1221 *wptr = '\f';
1222 break;
1223 case 'n':
1224 *wptr = '\n';
1225 break;
1226 case 'r':
1227 *wptr = '\r';
1228 break;
1229 case 't':
1230 *wptr = '\t';
1231 break;
1232 case 'v':
1233 *wptr = '\v';
1234 break;
1235 default:
1236 *wptr = *src;
1237 break;
1238 }
1239 src++;
1240 wptr++;
1241 wlen++;
1242 col++;
1243 }
1244 else
1245 {
1246 int bytes, width;
1247 /* in case of error, simply copy byte */
1248 bytes = mutt_mb_charlen(src, &width);
1249 if (bytes < 0)
1250 {
1251 bytes = 1;
1252 width = 1;
1253 }
1254 if ((bytes > 0) && ((wlen + bytes) < buflen))
1255 {
1256 memcpy(wptr, src, bytes);
1257 wptr += bytes;
1258 src += bytes;
1259 wlen += bytes;
1260 col += width;
1261 }
1262 else
1263 {
1264 src += buflen - wlen;
1265 wlen = buflen;
1266 }
1267 }
1268 }
1269 *wptr = '\0';
1270}
1271
1282FILE *mutt_open_read(const char *path, pid_t *thepid)
1283{
1284 FILE *fp = NULL;
1285 struct stat st = { 0 };
1286
1287 size_t len = mutt_str_len(path);
1288 if (len == 0)
1289 {
1290 return NULL;
1291 }
1292
1293 if (path[len - 1] == '|')
1294 {
1295 /* read from a pipe */
1296
1297 char *p = mutt_str_dup(path);
1298
1299 p[len - 1] = 0;
1300 mutt_endwin();
1301 *thepid = filter_create(p, NULL, &fp, NULL, EnvList);
1302 FREE(&p);
1303 }
1304 else
1305 {
1306 if (stat(path, &st) < 0)
1307 return NULL;
1308 if (S_ISDIR(st.st_mode))
1309 {
1310 errno = EINVAL;
1311 return NULL;
1312 }
1313 fp = fopen(path, "r");
1314 *thepid = -1;
1315 }
1316 return fp;
1317}
1318
1327int mutt_save_confirm(const char *s, struct stat *st)
1328{
1329 int rc = 0;
1330
1331 enum MailboxType type = mx_path_probe(s);
1332
1333#ifdef USE_POP
1334 if (type == MUTT_POP)
1335 {
1336 mutt_error(_("Can't save message to POP mailbox"));
1337 return 1;
1338 }
1339#endif
1340
1341 if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1342 {
1343 const bool c_confirm_append = cs_subset_bool(NeoMutt->sub, "confirm_append");
1344 if (c_confirm_append)
1345 {
1346 struct Buffer *tmp = buf_pool_get();
1347 buf_printf(tmp, _("Append messages to %s?"), s);
1349 NeoMutt->sub, "confirm_append");
1350 if (ans == MUTT_NO)
1351 rc = 1;
1352 else if (ans == MUTT_ABORT)
1353 rc = -1;
1354 buf_pool_release(&tmp);
1355 }
1356 }
1357
1358#ifdef USE_NNTP
1359 if (type == MUTT_NNTP)
1360 {
1361 mutt_error(_("Can't save message to news server"));
1362 return 0;
1363 }
1364#endif
1365
1366 if (stat(s, st) != -1)
1367 {
1368 if (type == MUTT_MAILBOX_ERROR)
1369 {
1370 mutt_error(_("%s is not a mailbox"), s);
1371 return 1;
1372 }
1373 }
1374 else if (type != MUTT_IMAP)
1375 {
1376 st->st_mtime = 0;
1377 st->st_atime = 0;
1378
1379 /* pathname does not exist */
1380 if (errno == ENOENT)
1381 {
1382 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1383 if (c_confirm_create)
1384 {
1385 struct Buffer *tmp = buf_pool_get();
1386 buf_printf(tmp, _("Create %s?"), s);
1388 NeoMutt->sub, "confirm_create");
1389 if (ans == MUTT_NO)
1390 rc = 1;
1391 else if (ans == MUTT_ABORT)
1392 rc = -1;
1393 buf_pool_release(&tmp);
1394 }
1395
1396 /* user confirmed with MUTT_YES or set `$confirm_create` */
1397 if (rc == 0)
1398 {
1399 /* create dir recursively */
1400 char *tmp_path = mutt_path_dirname(s);
1401 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1402 {
1403 /* report failure & abort */
1404 mutt_perror("%s", s);
1405 FREE(&tmp_path);
1406 return 1;
1407 }
1408 FREE(&tmp_path);
1409 }
1410 }
1411 else
1412 {
1413 mutt_perror("%s", s);
1414 return 1;
1415 }
1416 }
1417
1418 msgwin_clear_text(NULL);
1419 return rc;
1420}
1421
1428void mutt_sleep(short s)
1429{
1430 const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
1431 if (c_sleep_time > s)
1432 sleep(c_sleep_time);
1433 else if (s)
1434 sleep(s);
1435}
1436
1443const char *mutt_make_version(void)
1444{
1445 static char vstring[256];
1446 snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1447 return vstring;
1448}
1449
1457void mutt_encode_path(struct Buffer *buf, const char *src)
1458{
1459 char *p = mutt_str_dup(src);
1460 int rc = mutt_ch_convert_string(&p, cc_charset(), "us-ascii", MUTT_ICONV_NO_FLAGS);
1461 size_t len = buf_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1462
1463 /* convert the path to POSIX "Portable Filename Character Set" */
1464 for (size_t i = 0; i < len; i++)
1465 {
1466 if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1467 {
1468 buf->data[i] = '_';
1469 }
1470 }
1471 FREE(&p);
1472}
1473
1482int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1483{
1484 const char *xdg_env = mutt_str_getenv(XdgEnvVars[type]);
1485 char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(XdgDefaults[type]);
1486 char *x = xdg; /* mutt_str_sep() changes xdg, so free x instead later */
1487 char *token = NULL;
1488 int rc = 0;
1489
1490 while ((token = mutt_str_sep(&xdg, ":")))
1491 {
1492 if (buf_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1493 continue;
1494 buf_expand_path(buf);
1495 if (access(buf_string(buf), F_OK) == 0)
1496 {
1497 rc = 1;
1498 break;
1499 }
1500
1501 if (buf_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1502 continue;
1503 buf_expand_path(buf);
1504 if (access(buf_string(buf), F_OK) == 0)
1505 {
1506 rc = 1;
1507 break;
1508 }
1509 }
1510
1511 FREE(&x);
1512 return rc;
1513}
1514
1521void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1522{
1523 enum MailboxType mb_type = mx_path_probe(path);
1524
1525 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1526 if (mb_type == MUTT_IMAP)
1527 {
1528 imap_get_parent_path(path, buf, buflen);
1529 }
1530 else if (mb_type == MUTT_NOTMUCH)
1531 {
1532 mutt_str_copy(buf, c_folder, buflen);
1533 }
1534 else
1535 {
1536 mutt_str_copy(buf, path, buflen);
1537 int n = mutt_str_len(buf);
1538 if (n == 0)
1539 return;
1540
1541 /* remove any final trailing '/' */
1542 if (buf[n - 1] == '/')
1543 buf[n - 1] = '\0';
1544
1545 /* Remove everything until the next slash */
1546 for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1547 ; // do nothing
1548
1549 if (n > 0)
1550 {
1551 buf[n] = '\0';
1552 }
1553 else
1554 {
1555 buf[0] = '/';
1556 buf[1] = '\0';
1557 }
1558 }
1559}
1560
1583int mutt_inbox_cmp(const char *a, const char *b)
1584{
1585 /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1586 if ((a[0] == '+') && (b[0] == '+'))
1587 {
1588 return mutt_istr_equal(a + 1, "inbox") ? -1 :
1589 mutt_istr_equal(b + 1, "inbox") ? 1 :
1590 0;
1591 }
1592
1593 const char *a_end = strrchr(a, '/');
1594 const char *b_end = strrchr(b, '/');
1595
1596 /* If one path contains a '/', but not the other */
1597 if ((!a_end) ^ (!b_end))
1598 return 0;
1599
1600 /* If neither path contains a '/' */
1601 if (!a_end)
1602 return 0;
1603
1604 /* Compare the subpaths */
1605 size_t a_len = a_end - a;
1606 size_t b_len = b_end - b;
1607 size_t min = MIN(a_len, b_len);
1608 int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1609 (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1610
1611 if (!same)
1612 return 0;
1613
1614 if (mutt_istr_equal(&a[min + 1], "inbox"))
1615 return -1;
1616
1617 if (mutt_istr_equal(&b[min + 1], "inbox"))
1618 return 1;
1619
1620 return 0;
1621}
1622
1629void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1630{
1631 if (!buf || !path)
1632 return;
1633
1634 buf_reset(buf);
1635
1636 for (; *path; path++)
1637 {
1638 if ((slash && (*path == '/')) || !strchr(FilenameSafeChars, *path))
1639 buf_addch(buf, '_');
1640 else
1641 buf_addch(buf, *path);
1642 }
1643}
1644
1651void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1652{
1653 if (!buf || (buflen == 0))
1654 return;
1655
1656 const bool c_size_show_bytes = cs_subset_bool(NeoMutt->sub, "size_show_bytes");
1657 const bool c_size_show_fractions = cs_subset_bool(NeoMutt->sub, "size_show_fractions");
1658 const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
1659 const bool c_size_units_on_left = cs_subset_bool(NeoMutt->sub, "size_units_on_left");
1660
1661 if (c_size_show_bytes && (num < 1024))
1662 {
1663 snprintf(buf, buflen, "%d", (int) num);
1664 }
1665 else if (num == 0)
1666 {
1667 mutt_str_copy(buf, c_size_units_on_left ? "K0" : "0K", buflen);
1668 }
1669 else if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
1670 {
1671 snprintf(buf, buflen, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
1672 (num < 103) ? 0.1 : (num / 1024.0));
1673 }
1674 else if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
1675 {
1676 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1677 snprintf(buf, buflen, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1678 }
1679 else if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
1680 {
1681 snprintf(buf, buflen, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1682 }
1683 else /* 10M+ */
1684 {
1685 /* (10433332 + 52428) / 1048576 = 10 */
1686 snprintf(buf, buflen, c_size_units_on_left ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1687 }
1688}
1689
1697void add_to_stailq(struct ListHead *head, const char *str)
1698{
1699 /* don't add a NULL or empty string to the list */
1700 if (!str || (*str == '\0'))
1701 return;
1702
1703 /* check to make sure the item is not already on this list */
1704 struct ListNode *np = NULL;
1705 STAILQ_FOREACH(np, head, entries)
1706 {
1707 if (mutt_istr_equal(str, np->data))
1708 {
1709 return;
1710 }
1711 }
1713}
1714
1722void remove_from_stailq(struct ListHead *head, const char *str)
1723{
1724 if (mutt_str_equal("*", str))
1725 {
1726 mutt_list_free(head); /* "unCMD *" means delete all current entries */
1727 }
1728 else
1729 {
1730 struct ListNode *np = NULL, *tmp = NULL;
1731 STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1732 {
1733 if (mutt_istr_equal(str, np->data))
1734 {
1735 STAILQ_REMOVE(head, np, ListNode, entries);
1736 FREE(&np->data);
1737 FREE(&np);
1738 break;
1739 }
1740 }
1741 }
1742}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:762
Email Address Handling.
Email Aliases.
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:280
const struct CompleteOps CompleteMailboxOps
Auto-Completion of Files / Mailboxes.
Definition: complete.c:161
Select a Mailbox from a list.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
void buf_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:593
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:389
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:572
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:484
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: globals.c:39
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:115
Convenience wrapper for the core headers.
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:537
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:598
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:582
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:154
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:642
Enter a string.
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.
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:43
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:48
#define MoreArgs(buf)
Definition: extract.h:31
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:45
const char FilenameSafeChars[]
Set of characters <=0x7F that are safe to use in filenames.
Definition: file.c:61
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:709
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:977
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:667
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:218
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition: filter.c:207
Flags to control mutt_expando_format()
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
#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
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
char * LastFolder
Previously selected mailbox.
Definition: globals.c:45
char * CurrentFolder
Currently selected mailbox.
Definition: globals.c:44
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:86
Global variables.
const char * GitVer
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:745
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:276
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:61
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2329
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:1583
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
Read/write command history from/to a file.
@ HC_FILE
Files.
Definition: lib.h:52
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:736
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:160
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:578
int imap_expand_path(struct Buffer *path)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2369
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
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:40
#define FREE(x)
Definition: memory.h:45
#define MIN(a, b)
Definition: memory.h:32
#define MAX(a, b)
Definition: memory.h:31
@ 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(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:515
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:826
#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
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition: path.c:314
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:381
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:619
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:385
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
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:497
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:918
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
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:525
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:184
Many unsorted constants and some structs.
#define PATH_MAX
Definition: mutt.h:41
#define MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:56
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:57
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:60
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:58
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:349
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1521
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1722
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:374
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1697
void buf_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:138
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:422
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:126
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1443
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1428
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:671
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:562
void buf_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:696
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1482
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1651
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:450
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:483
static const char * XdgEnvVars[]
Accepted XDG environment variables.
Definition: muttlib.c:68
void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1629
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:87
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:335
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:726
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:584
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1457
static const char * XdgDefaults[]
XDG default locations.
Definition: muttlib.c:74
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1327
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1282
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:182
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1343
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
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Prototypes for many functions.
XdgType
XDG variable types.
Definition: protos.h:42
@ XDG_CONFIG_HOME
XDG home dir: ~/.config.
Definition: protos.h:43
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition: protos.h:44
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 query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:346
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:331
#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
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
struct Buffer * 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
Input for the file completion function.
Definition: curs_lib.h:49
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Cached regular expression.
Definition: regex3.h:89
#define buf_mktemp(buf)
Definition: tmp.h:33
#define buf_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: tmp.h:34
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:225
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