NeoMutt  2022-04-29-249-gaae397
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
66static const char *xdg_env_vars[] = {
67 [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
68 [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
69};
70
71static const char *xdg_defaults[] = {
72 [XDG_CONFIG_HOME] = "~/.config",
73 [XDG_CONFIG_DIRS] = "/etc/xdg",
74};
75
84void mutt_adv_mktemp(struct Buffer *buf)
85{
86 if (!(buf->data && (buf->data[0] != '\0')))
87 {
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:
112 }
113}
114
123char *mutt_expand_path(char *buf, size_t buflen)
124{
125 return mutt_expand_path_regex(buf, buflen, false);
126}
127
135void 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. */
312 imap_expand_path(buf);
313#endif
314}
315
323{
325}
326
336char *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));
342 mutt_str_copy(buf, mutt_buffer_string(tmp), buflen);
343
345
346 return buf;
347}
348
361char *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
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
433bool 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
467void 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
495void 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
522void 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] = { 0 };
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
621int 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 }
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
707void 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
730void 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';
743 }
744 }
745 mutt_str_lower(dest->data);
746 }
747 else
748 mutt_buffer_reset(dest);
749}
750
758void 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
777void 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] = { 0 };
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';
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
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
1311FILE *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
1356int 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
1455void 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
1470const 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
1484void 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
1510int 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
1549void 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
1605int 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
1651void 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
1673void 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
1719void 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 }
1735}
1736
1744void 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:67
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:275
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:260
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:309
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:189
void mutt_buffer_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:483
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:462
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
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:389
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
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
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_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
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:858
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:354
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:907
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
#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
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:737
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.
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: 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:249
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
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:376
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:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:384
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
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:496
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:927
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
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:524
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
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
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:336
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
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 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
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1470
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
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
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
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
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1311
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: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 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