NeoMutt  2023-03-22-27-g3cb248
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 <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 "enter/lib.h"
53#include "ncrypt/lib.h"
54#include "parse/lib.h"
55#include "question/lib.h"
56#include "format_flags.h"
57#include "globals.h" // IWYU pragma: keep
58#include "hook.h"
59#include "mx.h"
60#include "protos.h"
61#ifdef USE_IMAP
62#include "imap/lib.h"
63#endif
64
65static const char *xdg_env_vars[] = {
66 [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
67 [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
68};
69
70static const char *xdg_defaults[] = {
71 [XDG_CONFIG_HOME] = "~/.config",
72 [XDG_CONFIG_DIRS] = "/etc/xdg",
73};
74
83void mutt_adv_mktemp(struct Buffer *buf)
84{
85 if (!(buf->data && (buf->data[0] != '\0')))
86 {
88 }
89 else
90 {
91 struct Buffer *prefix = mutt_buffer_pool_get();
92 mutt_buffer_strcpy(prefix, buf->data);
93 mutt_file_sanitize_filename(prefix->data, true);
94 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
95 mutt_buffer_printf(buf, "%s/%s", NONULL(c_tmp_dir), mutt_buffer_string(prefix));
96
97 struct stat st = { 0 };
98 if ((lstat(mutt_buffer_string(buf), &st) == -1) && (errno == ENOENT))
99 goto out;
100
101 char *suffix = strchr(prefix->data, '.');
102 if (suffix)
103 {
104 *suffix = '\0';
105 suffix++;
106 }
107 mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
108
109 out:
111 }
112}
113
122char *mutt_expand_path(char *buf, size_t buflen)
123{
124 return mutt_expand_path_regex(buf, buflen, false);
125}
126
134void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
135{
136 const char *s = NULL;
137 const char *tail = "";
138
139 bool recurse = false;
140
141 struct Buffer *p = mutt_buffer_pool_get();
142 struct Buffer *q = mutt_buffer_pool_get();
143 struct Buffer *tmp = mutt_buffer_pool_get();
144
145 do
146 {
147 recurse = false;
148 s = mutt_buffer_string(buf);
149
150 switch (*s)
151 {
152 case '~':
153 {
154 if ((s[1] == '/') || (s[1] == '\0'))
155 {
157 tail = s + 1;
158 }
159 else
160 {
161 char *t = strchr(s + 1, '/');
162 if (t)
163 *t = '\0';
164
165 struct passwd *pw = getpwnam(s + 1);
166 if (pw)
167 {
168 mutt_buffer_strcpy(p, pw->pw_dir);
169 if (t)
170 {
171 *t = '/';
172 tail = t;
173 }
174 else
175 {
176 tail = "";
177 }
178 }
179 else
180 {
181 /* user not found! */
182 if (t)
183 *t = '/';
185 tail = s;
186 }
187 }
188 break;
189 }
190
191 case '=':
192 case '+':
193 {
194 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
195 enum MailboxType mb_type = mx_path_probe(c_folder);
196
197 /* if folder = {host} or imap[s]://host/: don't append slash */
198 if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
199 (c_folder[strlen(c_folder) - 1] == '/')))
200 {
201 mutt_buffer_strcpy(p, NONULL(c_folder));
202 }
203 else if (mb_type == MUTT_NOTMUCH)
204 mutt_buffer_strcpy(p, NONULL(c_folder));
205 else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
206 mutt_buffer_strcpy(p, NONULL(c_folder));
207 else
208 mutt_buffer_printf(p, "%s/", NONULL(c_folder));
209
210 tail = s + 1;
211 break;
212 }
213
214 /* elm compatibility, @ expands alias to user name */
215
216 case '@':
217 {
218 struct AddressList *al = alias_lookup(s + 1);
219 if (al && !TAILQ_EMPTY(al))
220 {
221 struct Email *e = email_new();
222 e->env = mutt_env_new();
223 mutt_addrlist_copy(&e->env->from, al, false);
224 mutt_addrlist_copy(&e->env->to, al, false);
225
226 /* TODO: fix mutt_default_save() to use Buffer */
228 mutt_default_save(p->data, p->dsize, e);
230
231 email_free(&e);
232 /* Avoid infinite recursion if the resulting folder starts with '@' */
233 if (*p->data != '@')
234 recurse = true;
235
236 tail = "";
237 }
238 break;
239 }
240
241 case '>':
242 {
243 const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
244 mutt_buffer_strcpy(p, c_mbox);
245 tail = s + 1;
246 break;
247 }
248
249 case '<':
250 {
251 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
252 mutt_buffer_strcpy(p, c_record);
253 tail = s + 1;
254 break;
255 }
256
257 case '!':
258 {
259 if (s[1] == '!')
260 {
262 tail = s + 2;
263 }
264 else
265 {
266 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
267 mutt_buffer_strcpy(p, c_spool_file);
268 tail = s + 1;
269 }
270 break;
271 }
272
273 case '-':
274 {
276 tail = s + 1;
277 break;
278 }
279
280 case '^':
281 {
283 tail = s + 1;
284 break;
285 }
286
287 default:
288 {
290 tail = s;
291 }
292 }
293
294 if (regex && *(mutt_buffer_string(p)) && !recurse)
295 {
297 mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(q), tail);
298 }
299 else
300 {
301 mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(p), tail);
302 }
303
304 mutt_buffer_copy(buf, tmp);
305 } while (recurse);
306
310
311#ifdef USE_IMAP
312 /* Rewrite IMAP path in canonical form - aids in string comparisons of
313 * folders. May possibly fail, in which case buf should be the same. */
315 imap_expand_path(buf);
316#endif
317}
318
326{
328}
329
339char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
340{
341 struct Buffer *tmp = mutt_buffer_pool_get();
342
343 mutt_buffer_addstr(tmp, NONULL(buf));
345 mutt_str_copy(buf, mutt_buffer_string(tmp), buflen);
346
348
349 return buf;
350}
351
364char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
365{
366 regmatch_t pat_match[1];
367 size_t pwnl;
368 char *p = NULL;
369
370 if (!pw || !pw->pw_gecos)
371 return NULL;
372
373 memset(dest, 0, destlen);
374
375 const struct Regex *c_gecos_mask = cs_subset_regex(NeoMutt->sub, "gecos_mask");
376 if (mutt_regex_capture(c_gecos_mask, pw->pw_gecos, 1, pat_match))
377 {
378 mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
379 MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
380 }
381 else if ((p = strchr(pw->pw_gecos, ',')))
382 mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
383 else
384 mutt_str_copy(dest, pw->pw_gecos, destlen);
385
386 pwnl = strlen(pw->pw_name);
387
388 for (int idx = 0; dest[idx]; idx++)
389 {
390 if (dest[idx] == '&')
391 {
392 memmove(&dest[idx + pwnl], &dest[idx + 1],
393 MAX((ssize_t) (destlen - idx - pwnl - 1), 0));
394 memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
395 dest[idx] = toupper((unsigned char) dest[idx]);
396 }
397 }
398
399 return dest;
400}
401
409{
410 switch (m->type)
411 {
412 case TYPE_TEXT:
413 if (mutt_istr_equal("plain", m->subtype))
414 return false;
415 break;
416 case TYPE_APPLICATION:
418 return false;
420 return false;
421 break;
422
423 case TYPE_MULTIPART:
424 case TYPE_MESSAGE:
425 return false;
426 }
427
428 return true;
429}
430
436bool mutt_is_text_part(struct Body *b)
437{
438 int t = b->type;
439 char *s = b->subtype;
440
442 return false;
443
444 if (t == TYPE_TEXT)
445 return true;
446
447 if (t == TYPE_MESSAGE)
448 {
449 if (mutt_istr_equal("delivery-status", s))
450 return true;
451 }
452
453 if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
454 {
455 if (mutt_istr_equal("pgp-keys", s))
456 return true;
457 }
458
459 return false;
460}
461
469void mutt_pretty_mailbox(char *buf, size_t buflen)
470{
471 if (!buf)
472 return;
473
474 char *p = buf, *q = buf;
475 size_t len;
476 enum UrlScheme scheme;
477 char tmp[PATH_MAX] = { 0 };
478
479 scheme = url_check_scheme(buf);
480
481 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
482 if ((scheme == U_IMAP) || (scheme == U_IMAPS))
483 {
484 imap_pretty_mailbox(buf, buflen, c_folder);
485 return;
486 }
487
488 if (scheme == U_NOTMUCH)
489 return;
490
491 /* if buf is an url, only collapse path component */
492 if (scheme != U_UNKNOWN)
493 {
494 p = strchr(buf, ':') + 1;
495 if (mutt_strn_equal(p, "//", 2))
496 q = strchr(p + 2, '/');
497 if (!q)
498 q = strchr(p, '\0');
499 p = q;
500 }
501
502 /* cleanup path */
503 if (strstr(p, "//") || strstr(p, "/./"))
504 {
505 /* first attempt to collapse the pathname, this is more
506 * lightweight than realpath() and doesn't resolve links */
507 while (*p)
508 {
509 if ((p[0] == '/') && (p[1] == '/'))
510 {
511 *q++ = '/';
512 p += 2;
513 }
514 else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
515 {
516 *q++ = '/';
517 p += 3;
518 }
519 else
520 *q++ = *p++;
521 }
522 *q = '\0';
523 }
524 else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
525 realpath(p, tmp))
526 {
527 mutt_str_copy(p, tmp, buflen - (p - buf));
528 }
529
530 if ((len = mutt_str_startswith(buf, c_folder)) && (buf[len] == '/'))
531 {
532 *buf++ = '=';
533 memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
534 }
535 else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
536 {
537 *buf++ = '~';
538 memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
539 }
540}
541
547{
548 if (!buf || !buf->data)
549 return;
550 /* This reduces the size of the Buffer, so we can pass it through.
551 * We adjust the size just to make sure buf->data is not NULL though */
553 mutt_pretty_mailbox(buf->data, buf->dsize);
555}
556
568int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
569 enum SaveAttach *opt, char **directory)
570{
571 struct stat st = { 0 };
572
573 mutt_buffer_strcpy(fname, path);
574 if (access(mutt_buffer_string(fname), F_OK) != 0)
575 return 0;
576 if (stat(mutt_buffer_string(fname), &st) != 0)
577 return -1;
578 if (S_ISDIR(st.st_mode))
579 {
580 enum QuadOption ans = MUTT_NO;
581 if (directory)
582 {
583 switch (mutt_multi_choice
584 /* L10N: Means "The path you specified as the destination file is a directory."
585 See the msgid "Save to file: " (alias.c, recvattach.c)
586 These three letters correspond to the choices in the string. */
587 (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
588 {
589 case 3: /* all */
590 mutt_str_replace(directory, mutt_buffer_string(fname));
591 break;
592 case 1: /* yes */
593 FREE(directory);
594 break;
595 case -1: /* abort */
596 FREE(directory);
597 return -1;
598 case 2: /* no */
599 FREE(directory);
600 return 1;
601 }
602 }
603 /* L10N: Means "The path you specified as the destination file is a directory."
604 See the msgid "Save to file: " (alias.c, recvattach.c) */
605 else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
606 return (ans == MUTT_NO) ? 1 : -1;
607
608 struct Buffer *tmp = mutt_buffer_pool_get();
610 if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_COMP_FILE | MUTT_COMP_CLEAR,
611 false, NULL, NULL, NULL) != 0) ||
613 {
615 return (-1);
616 }
619 }
620
621 if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_buffer_string(fname), F_OK) == 0))
622 {
623 char buf[4096] = { 0 };
624 snprintf(buf, sizeof(buf), "%s - %s", mutt_buffer_string(fname),
625 // L10N: Options for: File %s exists, (o)verwrite, (a)ppend, or (c)ancel?
626 _("File exists, (o)verwrite, (a)ppend, or (c)ancel?"));
627 switch (mutt_multi_choice(buf, _("oac")))
628 {
629 case -1: /* abort */
630 return -1;
631 case 3: /* cancel */
632 return 1;
633
634 case 2: /* append */
635 *opt = MUTT_SAVE_APPEND;
636 break;
637 case 1: /* overwrite */
638 *opt = MUTT_SAVE_OVERWRITE;
639 break;
640 }
641 }
642 return 0;
643}
644
654void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
655{
656 if (addr && addr->mailbox)
657 {
658 mutt_str_copy(buf, addr->mailbox, buflen);
659 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
660 if (!c_save_address)
661 {
662 char *p = strpbrk(buf, "%@");
663 if (p)
664 *p = '\0';
665 }
666 mutt_str_lower(buf);
667 }
668 else
669 *buf = '\0';
670}
671
677void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
678{
679 if (a && a->mailbox)
680 {
681 mutt_buffer_strcpy(dest, a->mailbox);
682 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
683 if (!c_save_address)
684 {
685 char *p = strpbrk(dest->data, "%@");
686 if (p)
687 {
688 *p = '\0';
690 }
691 }
692 mutt_str_lower(dest->data);
693 }
694 else
695 {
696 mutt_buffer_reset(dest);
697 }
698}
699
707void mutt_safe_path(struct Buffer *dest, const struct Address *a)
708{
709 mutt_buffer_save_path(dest, a);
710 for (char *p = dest->data; *p; p++)
711 if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
712 *p = '_';
713}
714
726void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
727 format_t callback, intptr_t data, MuttFormatFlags flags)
728{
729 char prefix[128], tmp[1024];
730 char *cp = NULL, *wptr = buf;
731 char ch;
732 char if_str[128], else_str[128];
733 size_t wlen, count, len, wid;
734 FILE *fp_filter = NULL;
735 char *recycler = NULL;
736
737 char src2[1024];
738 mutt_str_copy(src2, src, mutt_str_len(src) + 1);
739 src = src2;
740
741 const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
742 const char *const c_arrow_string = cs_subset_string(NeoMutt->sub, "arrow_string");
743
744 prefix[0] = '\0';
745 buflen--; /* save room for the terminal \0 */
746 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
747 mutt_strwidth(c_arrow_string) + 1 :
748 0;
749 col += wlen;
750
751 if ((flags & MUTT_FORMAT_NOFILTER) == 0)
752 {
753 int off = -1;
754
755 /* Do not consider filters if no pipe at end */
756 int n = mutt_str_len(src);
757 if ((n > 1) && (src[n - 1] == '|'))
758 {
759 /* Scan backwards for backslashes */
760 off = n;
761 while ((off > 0) && (src[off - 2] == '\\'))
762 off--;
763 }
764
765 /* If number of backslashes is even, the pipe is real. */
766 /* n-off is the number of backslashes. */
767 if ((off > 0) && (((n - off) % 2) == 0))
768 {
769 char srccopy[1024] = { 0 };
770 int i = 0;
771
772 mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
773
774 strncpy(srccopy, src, n);
775 srccopy[n - 1] = '\0';
776
777 /* prepare Buffers */
778 struct Buffer srcbuf = mutt_buffer_make(0);
779 mutt_buffer_addstr(&srcbuf, srccopy);
780 /* note: we are resetting dptr and *reading* from the buffer, so we don't
781 * want to use mutt_buffer_reset(). */
782 mutt_buffer_seek(&srcbuf, 0);
783 struct Buffer word = mutt_buffer_make(0);
784 struct Buffer cmd = mutt_buffer_make(0);
785
786 /* Iterate expansions across successive arguments */
787 do
788 {
789 /* Extract the command name and copy to command line */
790 mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
791 if (word.data)
792 *word.data = '\0';
793 parse_extract_token(&word, &srcbuf, TOKEN_NO_FLAGS);
794 mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
795 mutt_buffer_addch(&cmd, '\'');
796 mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
797 data, flags | MUTT_FORMAT_NOFILTER);
798 for (char *p = tmp; p && (*p != '\0'); p++)
799 {
800 if (*p == '\'')
801 {
802 /* shell quoting doesn't permit escaping a single quote within
803 * single-quoted material. double-quoting instead will lead
804 * shell variable expansions, so break out of the single-quoted
805 * span, insert a double-quoted single quote, and resume. */
806 mutt_buffer_addstr(&cmd, "'\"'\"'");
807 }
808 else
809 {
810 mutt_buffer_addch(&cmd, *p);
811 }
812 }
813 mutt_buffer_addch(&cmd, '\'');
814 mutt_buffer_addch(&cmd, ' ');
815 } while (MoreArgs(&srcbuf));
816
817 mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
818
819 col -= wlen; /* reset to passed in value */
820 wptr = buf; /* reset write ptr */
821 pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
822 if (pid != -1)
823 {
824 int rc;
825
826 n = fread(buf, 1, buflen /* already decremented */, fp_filter);
827 mutt_file_fclose(&fp_filter);
828 rc = filter_wait(pid);
829 if (rc != 0)
830 mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
831 if (n > 0)
832 {
833 buf[n] = '\0';
834 while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
835 buf[--n] = '\0';
836 mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
837
838 /* If the result ends with '%', this indicates that the filter
839 * generated %-tokens that neomutt can expand. Eliminate the '%'
840 * marker and recycle the string through mutt_expando_format().
841 * To literally end with "%", use "%%". */
842 if ((n > 0) && (buf[n - 1] == '%'))
843 {
844 n--;
845 buf[n] = '\0'; /* remove '%' */
846 if ((n > 0) && (buf[n - 1] != '%'))
847 {
848 recycler = mutt_str_dup(buf);
849 if (recycler)
850 {
851 /* buflen is decremented at the start of this function
852 * to save space for the terminal nul char. We can add
853 * it back for the recursive call since the expansion of
854 * format pipes does not try to append a nul itself. */
855 mutt_expando_format(buf, buflen + 1, col, cols, recycler,
856 callback, data, flags);
857 FREE(&recycler);
858 }
859 }
860 }
861 }
862 else
863 {
864 /* read error */
865 mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
866 strerror(errno), errno);
867 *wptr = '\0';
868 }
869 }
870 else
871 {
872 /* Filter failed; erase write buffer */
873 *wptr = '\0';
874 }
875
877 mutt_buffer_dealloc(&srcbuf);
878 mutt_buffer_dealloc(&word);
879 return;
880 }
881 }
882
883 while (*src && (wlen < buflen))
884 {
885 if (*src == '%')
886 {
887 if (*++src == '%')
888 {
889 *wptr++ = '%';
890 wlen++;
891 col++;
892 src++;
893 continue;
894 }
895
896 if (*src == '?')
897 {
898 /* change original %? to new %< notation */
899 /* %?x?y&z? to %<x?y&z> where y and z are nestable */
900 char *p = (char *) src;
901 *p = '<';
902 /* skip over "x" */
903 for (; *p && (*p != '?'); p++)
904 ; // do nothing
905
906 /* nothing */
907 if (*p == '?')
908 p++;
909 /* fix up the "y&z" section */
910 for (; *p && (*p != '?'); p++)
911 {
912 /* escape '<' and '>' to work inside nested-if */
913 if ((*p == '<') || (*p == '>'))
914 {
915 memmove(p + 2, p, mutt_str_len(p) + 1);
916 *p++ = '\\';
917 *p++ = '\\';
918 }
919 }
920 if (*p == '?')
921 *p = '>';
922 }
923
924 if (*src == '<')
925 {
926 flags |= MUTT_FORMAT_OPTIONAL;
927 ch = *(++src); /* save the character to switch on */
928 src++;
929 cp = prefix;
930 count = 0;
931 while ((count < (sizeof(prefix) - 1)) && (*src != '\0') && (*src != '?'))
932 {
933 *cp++ = *src++;
934 count++;
935 }
936 *cp = '\0';
937 }
938 else
939 {
940 flags &= ~MUTT_FORMAT_OPTIONAL;
941
942 /* eat the format string */
943 cp = prefix;
944 count = 0;
945 while ((count < (sizeof(prefix) - 1)) && strchr("0123456789.-=", *src))
946 {
947 *cp++ = *src++;
948 count++;
949 }
950 *cp = '\0';
951
952 if (*src == '\0')
953 break; /* bad format */
954
955 ch = *src++; /* save the character to switch on */
956 }
957
958 if (flags & MUTT_FORMAT_OPTIONAL)
959 {
960 int lrbalance;
961
962 if (*src != '?')
963 break; /* bad format */
964 src++;
965
966 /* eat the 'if' part of the string */
967 cp = if_str;
968 count = 0;
969 lrbalance = 1;
970 while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
971 {
972 if ((src[0] == '%') && (src[1] == '>'))
973 {
974 /* This is a padding expando; copy two chars and carry on */
975 *cp++ = *src++;
976 *cp++ = *src++;
977 count += 2;
978 continue;
979 }
980
981 if (*src == '\\')
982 {
983 src++;
984 *cp++ = *src++;
985 }
986 else if ((src[0] == '%') && (src[1] == '<'))
987 {
988 lrbalance++;
989 }
990 else if (src[0] == '>')
991 {
992 lrbalance--;
993 }
994 if (lrbalance == 0)
995 break;
996 if ((lrbalance == 1) && (src[0] == '&'))
997 break;
998 *cp++ = *src++;
999 count++;
1000 }
1001 *cp = '\0';
1002
1003 /* eat the 'else' part of the string (optional) */
1004 if (*src == '&')
1005 src++; /* skip the & */
1006 cp = else_str;
1007 count = 0;
1008 while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1009 {
1010 if ((src[0] == '%') && (src[1] == '>'))
1011 {
1012 /* This is a padding expando; copy two chars and carry on */
1013 *cp++ = *src++;
1014 *cp++ = *src++;
1015 count += 2;
1016 continue;
1017 }
1018
1019 if (*src == '\\')
1020 {
1021 src++;
1022 *cp++ = *src++;
1023 }
1024 else if ((src[0] == '%') && (src[1] == '<'))
1025 {
1026 lrbalance++;
1027 }
1028 else if (src[0] == '>')
1029 {
1030 lrbalance--;
1031 }
1032 if (lrbalance == 0)
1033 break;
1034 if ((lrbalance == 1) && (src[0] == '&'))
1035 break;
1036 *cp++ = *src++;
1037 count++;
1038 }
1039 *cp = '\0';
1040
1041 if ((*src == '\0'))
1042 break; /* bad format */
1043
1044 src++; /* move past the trailing '>' (formerly '?') */
1045 }
1046
1047 /* handle generic cases first */
1048 if ((ch == '>') || (ch == '*'))
1049 {
1050 /* %>X: right justify to EOL, left takes precedence
1051 * %*X: right justify to EOL, right takes precedence */
1052 int soft = ch == '*';
1053 int pl, pw;
1054 pl = mutt_mb_charlen(src, &pw);
1055 if (pl <= 0)
1056 {
1057 pl = 1;
1058 pw = 1;
1059 }
1060
1061 /* see if there's room to add content, else ignore */
1062 if (((col < cols) && (wlen < buflen)) || soft)
1063 {
1064 int pad;
1065
1066 /* get contents after padding */
1067 mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1068 len = mutt_str_len(tmp);
1069 wid = mutt_strwidth(tmp);
1070
1071 pad = (cols - col - wid) / pw;
1072 if (pad >= 0)
1073 {
1074 /* try to consume as many columns as we can, if we don't have
1075 * memory for that, use as much memory as possible */
1076 if (wlen + (pad * pl) + len > buflen)
1077 {
1078 pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1079 }
1080 else
1081 {
1082 /* Add pre-spacing to make multi-column pad characters and
1083 * the contents after padding line up */
1084 while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1085 {
1086 *wptr++ = ' ';
1087 wlen++;
1088 col++;
1089 }
1090 }
1091 while (pad-- > 0)
1092 {
1093 memcpy(wptr, src, pl);
1094 wptr += pl;
1095 wlen += pl;
1096 col += pw;
1097 }
1098 }
1099 else if (soft)
1100 {
1101 int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
1102 mutt_strwidth(c_arrow_string) + 1 :
1103 0;
1104 int avail_cols = (cols > offset) ? (cols - offset) : 0;
1105 /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1106 *wptr = '\0';
1107 /* make sure right part is at most as wide as display */
1108 len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1109 /* truncate left so that right part fits completely in */
1110 wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1111 wptr = buf + wlen;
1112 /* Multi-column characters may be truncated in the middle.
1113 * Add spacing so the right hand side lines up. */
1114 while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1115 {
1116 *wptr++ = ' ';
1117 wlen++;
1118 col++;
1119 }
1120 }
1121 if ((len + wlen) > buflen)
1122 len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1123 memcpy(wptr, tmp, len);
1124 wptr += len;
1125 }
1126 break; /* skip rest of input */
1127 }
1128 else if (ch == '|')
1129 {
1130 /* pad to EOL */
1131 int pl, pw;
1132 pl = mutt_mb_charlen(src, &pw);
1133 if (pl <= 0)
1134 {
1135 pl = 1;
1136 pw = 1;
1137 }
1138
1139 /* see if there's room to add content, else ignore */
1140 if ((col < cols) && (wlen < buflen))
1141 {
1142 int c = (cols - col) / pw;
1143 if ((c > 0) && ((wlen + (c * pl)) > buflen))
1144 c = ((signed) (buflen - wlen)) / pl;
1145 while (c > 0)
1146 {
1147 memcpy(wptr, src, pl);
1148 wptr += pl;
1149 wlen += pl;
1150 col += pw;
1151 c--;
1152 }
1153 }
1154 break; /* skip rest of input */
1155 }
1156 else
1157 {
1158 bool to_lower = false;
1159 bool no_dots = false;
1160
1161 while ((ch == '_') || (ch == ':'))
1162 {
1163 if (ch == '_')
1164 to_lower = true;
1165 else if (ch == ':')
1166 no_dots = true;
1167
1168 ch = *src++;
1169 }
1170
1171 /* use callback function to handle this case */
1172 *tmp = '\0';
1173 src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1174 else_str, data, flags);
1175
1176 if (to_lower)
1177 mutt_str_lower(tmp);
1178 if (no_dots)
1179 {
1180 char *p = tmp;
1181 for (; *p; p++)
1182 if (*p == '.')
1183 *p = '_';
1184 }
1185
1186 len = mutt_str_len(tmp);
1187 if ((len + wlen) > buflen)
1188 len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1189
1190 memcpy(wptr, tmp, len);
1191 wptr += len;
1192 wlen += len;
1193 col += mutt_strwidth(tmp);
1194 }
1195 }
1196 else if (*src == '\\')
1197 {
1198 if (!*++src)
1199 break;
1200 switch (*src)
1201 {
1202 case 'f':
1203 *wptr = '\f';
1204 break;
1205 case 'n':
1206 *wptr = '\n';
1207 break;
1208 case 'r':
1209 *wptr = '\r';
1210 break;
1211 case 't':
1212 *wptr = '\t';
1213 break;
1214 case 'v':
1215 *wptr = '\v';
1216 break;
1217 default:
1218 *wptr = *src;
1219 break;
1220 }
1221 src++;
1222 wptr++;
1223 wlen++;
1224 col++;
1225 }
1226 else
1227 {
1228 int bytes, width;
1229 /* in case of error, simply copy byte */
1230 bytes = mutt_mb_charlen(src, &width);
1231 if (bytes < 0)
1232 {
1233 bytes = 1;
1234 width = 1;
1235 }
1236 if ((bytes > 0) && ((wlen + bytes) < buflen))
1237 {
1238 memcpy(wptr, src, bytes);
1239 wptr += bytes;
1240 src += bytes;
1241 wlen += bytes;
1242 col += width;
1243 }
1244 else
1245 {
1246 src += buflen - wlen;
1247 wlen = buflen;
1248 }
1249 }
1250 }
1251 *wptr = '\0';
1252}
1253
1264FILE *mutt_open_read(const char *path, pid_t *thepid)
1265{
1266 FILE *fp = NULL;
1267 struct stat st = { 0 };
1268
1269 size_t len = mutt_str_len(path);
1270 if (len == 0)
1271 {
1272 return NULL;
1273 }
1274
1275 if (path[len - 1] == '|')
1276 {
1277 /* read from a pipe */
1278
1279 char *p = mutt_str_dup(path);
1280
1281 p[len - 1] = 0;
1282 mutt_endwin();
1283 *thepid = filter_create(p, NULL, &fp, NULL);
1284 FREE(&p);
1285 }
1286 else
1287 {
1288 if (stat(path, &st) < 0)
1289 return NULL;
1290 if (S_ISDIR(st.st_mode))
1291 {
1292 errno = EINVAL;
1293 return NULL;
1294 }
1295 fp = fopen(path, "r");
1296 *thepid = -1;
1297 }
1298 return fp;
1299}
1300
1309int mutt_save_confirm(const char *s, struct stat *st)
1310{
1311 int rc = 0;
1312
1313 enum MailboxType type = mx_path_probe(s);
1314
1315#ifdef USE_POP
1316 if (type == MUTT_POP)
1317 {
1318 mutt_error(_("Can't save message to POP mailbox"));
1319 return 1;
1320 }
1321#endif
1322
1323 if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1324 {
1325 const bool c_confirm_append = cs_subset_bool(NeoMutt->sub, "confirm_append");
1326 if (c_confirm_append)
1327 {
1328 struct Buffer *tmp = mutt_buffer_pool_get();
1329 mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1331 if (ans == MUTT_NO)
1332 rc = 1;
1333 else if (ans == MUTT_ABORT)
1334 rc = -1;
1336 }
1337 }
1338
1339#ifdef USE_NNTP
1340 if (type == MUTT_NNTP)
1341 {
1342 mutt_error(_("Can't save message to news server"));
1343 return 0;
1344 }
1345#endif
1346
1347 if (stat(s, st) != -1)
1348 {
1349 if (type == MUTT_MAILBOX_ERROR)
1350 {
1351 mutt_error(_("%s is not a mailbox"), s);
1352 return 1;
1353 }
1354 }
1355 else if (type != MUTT_IMAP)
1356 {
1357 st->st_mtime = 0;
1358 st->st_atime = 0;
1359
1360 /* pathname does not exist */
1361 if (errno == ENOENT)
1362 {
1363 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1364 if (c_confirm_create)
1365 {
1366 struct Buffer *tmp = mutt_buffer_pool_get();
1367 mutt_buffer_printf(tmp, _("Create %s?"), s);
1369 if (ans == MUTT_NO)
1370 rc = 1;
1371 else if (ans == MUTT_ABORT)
1372 rc = -1;
1374 }
1375
1376 /* user confirmed with MUTT_YES or set `$confirm_create` */
1377 if (rc == 0)
1378 {
1379 /* create dir recursively */
1380 char *tmp_path = mutt_path_dirname(s);
1381 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1382 {
1383 /* report failure & abort */
1384 mutt_perror(s);
1385 FREE(&tmp_path);
1386 return 1;
1387 }
1388 FREE(&tmp_path);
1389 }
1390 }
1391 else
1392 {
1393 mutt_perror(s);
1394 return 1;
1395 }
1396 }
1397
1399 return rc;
1400}
1401
1408void mutt_sleep(short s)
1409{
1410 const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
1411 if (c_sleep_time > s)
1412 sleep(c_sleep_time);
1413 else if (s)
1414 sleep(s);
1415}
1416
1423const char *mutt_make_version(void)
1424{
1425 static char vstring[256];
1426 snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1427 return vstring;
1428}
1429
1437void mutt_encode_path(struct Buffer *buf, const char *src)
1438{
1439 char *p = mutt_str_dup(src);
1440 const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
1441 int rc = mutt_ch_convert_string(&p, c_charset, "us-ascii", MUTT_ICONV_NO_FLAGS);
1442 size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1443
1444 /* convert the path to POSIX "Portable Filename Character Set" */
1445 for (size_t i = 0; i < len; i++)
1446 {
1447 if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1448 {
1449 buf->data[i] = '_';
1450 }
1451 }
1452 FREE(&p);
1453}
1454
1463int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1464{
1465 const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1466 char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(xdg_defaults[type]);
1467 char *x = xdg; /* mutt_str_sep() changes xdg, so free x instead later */
1468 char *token = NULL;
1469 int rc = 0;
1470
1471 while ((token = mutt_str_sep(&xdg, ":")))
1472 {
1473 if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1474 continue;
1476 if (access(mutt_buffer_string(buf), F_OK) == 0)
1477 {
1478 rc = 1;
1479 break;
1480 }
1481
1482 if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1483 continue;
1485 if (access(mutt_buffer_string(buf), F_OK) == 0)
1486 {
1487 rc = 1;
1488 break;
1489 }
1490 }
1491
1492 FREE(&x);
1493 return rc;
1494}
1495
1502void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1503{
1504 enum MailboxType mb_type = mx_path_probe(path);
1505
1506 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1507 if (mb_type == MUTT_IMAP)
1508 imap_get_parent_path(path, buf, buflen);
1509 else if (mb_type == MUTT_NOTMUCH)
1510 mutt_str_copy(buf, c_folder, buflen);
1511 else
1512 {
1513 mutt_str_copy(buf, path, buflen);
1514 int n = mutt_str_len(buf);
1515 if (n == 0)
1516 return;
1517
1518 /* remove any final trailing '/' */
1519 if (buf[n - 1] == '/')
1520 buf[n - 1] = '\0';
1521
1522 /* Remove everything until the next slash */
1523 for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1524 ; // do nothing
1525
1526 if (n > 0)
1527 {
1528 buf[n] = '\0';
1529 }
1530 else
1531 {
1532 buf[0] = '/';
1533 buf[1] = '\0';
1534 }
1535 }
1536}
1537
1560int mutt_inbox_cmp(const char *a, const char *b)
1561{
1562 /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1563 if ((a[0] == '+') && (b[0] == '+'))
1564 {
1565 return mutt_istr_equal(a + 1, "inbox") ? -1 :
1566 mutt_istr_equal(b + 1, "inbox") ? 1 :
1567 0;
1568 }
1569
1570 const char *a_end = strrchr(a, '/');
1571 const char *b_end = strrchr(b, '/');
1572
1573 /* If one path contains a '/', but not the other */
1574 if ((!a_end) ^ (!b_end))
1575 return 0;
1576
1577 /* If neither path contains a '/' */
1578 if (!a_end)
1579 return 0;
1580
1581 /* Compare the subpaths */
1582 size_t a_len = a_end - a;
1583 size_t b_len = b_end - b;
1584 size_t min = MIN(a_len, b_len);
1585 int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1586 (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1587
1588 if (!same)
1589 return 0;
1590
1591 if (mutt_istr_equal(&a[min + 1], "inbox"))
1592 return -1;
1593
1594 if (mutt_istr_equal(&b[min + 1], "inbox"))
1595 return 1;
1596
1597 return 0;
1598}
1599
1606void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1607{
1608 if (!buf || !path)
1609 return;
1610
1611 mutt_buffer_reset(buf);
1612
1613 for (; *path; path++)
1614 {
1615 if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1616 mutt_buffer_addch(buf, '_');
1617 else
1618 mutt_buffer_addch(buf, *path);
1619 }
1620}
1621
1628void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1629{
1630 if (!buf || (buflen == 0))
1631 return;
1632
1633 const bool c_size_show_bytes = cs_subset_bool(NeoMutt->sub, "size_show_bytes");
1634 const bool c_size_show_fractions = cs_subset_bool(NeoMutt->sub, "size_show_fractions");
1635 const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
1636 const bool c_size_units_on_left = cs_subset_bool(NeoMutt->sub, "size_units_on_left");
1637
1638 if (c_size_show_bytes && (num < 1024))
1639 {
1640 snprintf(buf, buflen, "%d", (int) num);
1641 }
1642 else if (num == 0)
1643 {
1644 mutt_str_copy(buf, c_size_units_on_left ? "K0" : "0K", buflen);
1645 }
1646 else if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
1647 {
1648 snprintf(buf, buflen, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
1649 (num < 103) ? 0.1 : (num / 1024.0));
1650 }
1651 else if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
1652 {
1653 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1654 snprintf(buf, buflen, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1655 }
1656 else if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
1657 {
1658 snprintf(buf, buflen, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1659 }
1660 else /* 10M+ */
1661 {
1662 /* (10433332 + 52428) / 1048576 = 10 */
1663 snprintf(buf, buflen, c_size_units_on_left ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1664 }
1665}
1666
1674void add_to_stailq(struct ListHead *head, const char *str)
1675{
1676 /* don't add a NULL or empty string to the list */
1677 if (!str || (*str == '\0'))
1678 return;
1679
1680 /* check to make sure the item is not already on this list */
1681 struct ListNode *np = NULL;
1682 STAILQ_FOREACH(np, head, entries)
1683 {
1684 if (mutt_istr_equal(str, np->data))
1685 {
1686 return;
1687 }
1688 }
1690}
1691
1699void remove_from_stailq(struct ListHead *head, const char *str)
1700{
1701 if (mutt_str_equal("*", str))
1702 {
1703 mutt_list_free(head); /* "unCMD *" means delete all current entries */
1704 }
1705 else
1706 {
1707 struct ListNode *np = NULL, *tmp = NULL;
1708 STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1709 {
1710 if (mutt_istr_equal(str, np->data))
1711 {
1712 STAILQ_REMOVE(head, np, ListNode, entries);
1713 FREE(&np->data);
1714 FREE(&np);
1715 break;
1716 }
1717 }
1718 }
1719}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:745
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:313
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:365
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:347
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:521
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:500
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:427
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
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: globals.c:38
Convenience wrapper for the core headers.
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:541
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:859
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:353
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:908
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 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:30
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:33
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:684
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:952
const char filename_safe_chars[]
Definition: file.c:58
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:665
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
char * LastFolder
Previously selected mailbox.
Definition: globals.c:44
char * CurrentFolder
Currently selected mailbox.
Definition: globals.c:43
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:726
#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:2425
Convenience wrapper for the gui headers.
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:195
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:735
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:581
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2465
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:751
#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:618
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 occurrence 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:58
#define PATH_MAX
Definition: mutt.h:41
#define MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:65
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:339
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1502
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1699
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:546
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:325
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:364
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1674
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:134
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:408
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:122
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:1560
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1423
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1408
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:654
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:677
static const char * xdg_env_vars[]
Definition: muttlib.c:65
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1463
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1628
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:436
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:469
static const char * xdg_defaults[]
Definition: muttlib.c:70
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:83
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:707
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:568
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1606
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1437
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1309
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1264
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:183
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1334
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.
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
#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
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: tmp.h:38
#define mutt_buffer_mktemp(buf)
Definition: tmp.h:37
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:223
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