NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
hdrline.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <locale.h>
34#include <stdbool.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40#include "mutt/lib.h"
41#include "address/lib.h"
42#include "config/lib.h"
43#include "email/lib.h"
44#include "core/lib.h"
45#include "alias/lib.h"
46#include "gui/lib.h"
47#include "hdrline.h"
48#include "attach/lib.h"
49#include "color/lib.h"
50#include "ncrypt/lib.h"
51#include "format_flags.h"
52#include "hook.h"
53#include "maillist.h"
54#include "mutt_thread.h"
55#include "muttlib.h"
56#include "mx.h"
57#include "sort.h"
58#include "subjectrx.h"
59#ifdef USE_NOTMUCH
60#include "notmuch/lib.h"
61#endif
62
67{
68 struct Mailbox *mailbox;
70 struct Email *email;
71 const char *pager_progress;
72};
73
78{
90};
91
96{
102};
103
110{
117};
118
129static size_t add_index_color(char *buf, size_t buflen, MuttFormatFlags flags, enum ColorId color)
130{
131 /* only add color markers if we are operating on main index entries. */
132 if (!(flags & MUTT_FORMAT_INDEX))
133 return 0;
134
135 /* this item is going to be passed to an external filter */
136 if (flags & MUTT_FORMAT_NOFILTER)
137 return 0;
138
139 if (color == MT_COLOR_INDEX)
140 { /* buf might be uninitialized other cases */
141 const size_t len = mutt_str_len(buf);
142 buf += len;
143 buflen -= len;
144 }
145
146 if (buflen <= 2)
147 return 0;
148
149 buf[0] = MUTT_SPECIAL_INDEX;
150 buf[1] = color;
151 buf[2] = '\0';
152
153 return 2;
154}
155
166static const char *get_nth_wchar(const struct MbTable *table, int index)
167{
168 if (!table || !table->chars || (index < 0) || (index >= table->len))
169 return " ";
170
171 if (table->chars[index][0] == '\r')
172 return "";
173
174 return table->chars[index];
175}
176
185static const char *make_from_prefix(enum FieldType disp)
186{
187 /* need 2 bytes at the end, one for the space, another for NUL */
188 static char padded[8];
189 static const char *long_prefixes[DISP_MAX] = {
190 [DISP_TO] = "To ", [DISP_CC] = "Cc ", [DISP_BCC] = "Bcc ",
191 [DISP_FROM] = "", [DISP_PLAIN] = "",
192 };
193
194 const struct MbTable *c_from_chars = cs_subset_mbtable(NeoMutt->sub, "from_chars");
195
196 if (!c_from_chars || !c_from_chars->chars || (c_from_chars->len == 0))
197 return long_prefixes[disp];
198
199 const char *pchar = get_nth_wchar(c_from_chars, disp);
200 if (mutt_str_len(pchar) == 0)
201 return "";
202
203 snprintf(padded, sizeof(padded), "%s ", pchar);
204 return padded;
205}
206
221static void make_from(struct Envelope *env, char *buf, size_t buflen,
222 bool do_lists, MuttFormatFlags flags)
223{
224 if (!env || !buf)
225 return;
226
227 bool me;
228 enum FieldType disp;
229 struct AddressList *name = NULL;
230
232
233 if (do_lists || me)
234 {
235 if (check_for_mailing_list(&env->to, make_from_prefix(DISP_TO), buf, buflen))
236 return;
237 if (check_for_mailing_list(&env->cc, make_from_prefix(DISP_CC), buf, buflen))
238 return;
239 }
240
241 if (me && !TAILQ_EMPTY(&env->to))
242 {
243 disp = (flags & MUTT_FORMAT_PLAIN) ? DISP_PLAIN : DISP_TO;
244 name = &env->to;
245 }
246 else if (me && !TAILQ_EMPTY(&env->cc))
247 {
248 disp = DISP_CC;
249 name = &env->cc;
250 }
251 else if (me && !TAILQ_EMPTY(&env->bcc))
252 {
253 disp = DISP_BCC;
254 name = &env->bcc;
255 }
256 else if (!TAILQ_EMPTY(&env->from))
257 {
258 disp = DISP_FROM;
259 name = &env->from;
260 }
261 else
262 {
263 *buf = '\0';
264 return;
265 }
266
267 snprintf(buf, buflen, "%s%s", make_from_prefix(disp), mutt_get_name(TAILQ_FIRST(name)));
268}
269
277static void make_from_addr(struct Envelope *env, char *buf, size_t buflen, bool do_lists)
278{
279 if (!env || !buf)
280 return;
281
282 bool me = mutt_addr_is_user(TAILQ_FIRST(&env->from));
283
284 if (do_lists || me)
285 {
286 if (check_for_mailing_list_addr(&env->to, buf, buflen))
287 return;
288 if (check_for_mailing_list_addr(&env->cc, buf, buflen))
289 return;
290 }
291
292 if (me && !TAILQ_EMPTY(&env->to))
293 snprintf(buf, buflen, "%s", TAILQ_FIRST(&env->to)->mailbox);
294 else if (me && !TAILQ_EMPTY(&env->cc))
295 snprintf(buf, buflen, "%s", TAILQ_FIRST(&env->cc)->mailbox);
296 else if (!TAILQ_EMPTY(&env->from))
297 mutt_str_copy(buf, TAILQ_FIRST(&env->from)->mailbox, buflen);
298 else
299 *buf = '\0';
300}
301
307static bool user_in_addr(struct AddressList *al)
308{
309 struct Address *a = NULL;
310 TAILQ_FOREACH(a, al, entries)
311 if (mutt_addr_is_user(a))
312 return true;
313 return false;
314}
315
327static int user_is_recipient(struct Email *e)
328{
329 if (!e || !e->env)
330 return 0;
331
332 struct Envelope *env = e->env;
333
334 if (!e->recip_valid)
335 {
336 e->recip_valid = true;
337
339 e->recipient = 4;
340 else if (user_in_addr(&env->to))
341 {
342 if (TAILQ_NEXT(TAILQ_FIRST(&env->to), entries) || !TAILQ_EMPTY(&env->cc))
343 e->recipient = 2; /* non-unique recipient */
344 else
345 e->recipient = 1; /* unique recipient */
346 }
347 else if (user_in_addr(&env->cc))
348 e->recipient = 3;
349 else if (check_for_mailing_list(&env->to, NULL, NULL, 0))
350 e->recipient = 5;
351 else if (check_for_mailing_list(&env->cc, NULL, NULL, 0))
352 e->recipient = 5;
353 else if (user_in_addr(&env->reply_to))
354 e->recipient = 6;
355 else
356 e->recipient = 0;
357 }
358
359 return e->recipient;
360}
361
367static bool thread_is_new(struct Email *e)
368{
369 return e->collapsed && (e->num_hidden > 1) && (mutt_thread_contains_unread(e) == 1);
370}
371
377static bool thread_is_old(struct Email *e)
378{
379 return e->collapsed && (e->num_hidden > 1) && (mutt_thread_contains_unread(e) == 2);
380}
381
439static const char *index_format_str(char *buf, size_t buflen, size_t col, int cols,
440 char op, const char *src, const char *prec,
441 const char *if_str, const char *else_str,
442 intptr_t data, MuttFormatFlags flags)
443{
444 struct HdrFormatInfo *hfi = (struct HdrFormatInfo *) data;
445 char fmt[128], tmp[1024];
446 char *p = NULL, *tags = NULL;
447 bool optional = (flags & MUTT_FORMAT_OPTIONAL);
448 const bool threads = mutt_using_threads();
449 int is_index = (flags & MUTT_FORMAT_INDEX);
450 size_t colorlen;
451
452 struct Email *e = hfi->email;
453 size_t msg_in_pager = hfi->msg_in_pager;
454 struct Mailbox *m = hfi->mailbox;
455
456 if (!e || !e->env)
457 return src;
458
459 const struct Address *reply_to = TAILQ_FIRST(&e->env->reply_to);
460 const struct Address *from = TAILQ_FIRST(&e->env->from);
461 const struct Address *to = TAILQ_FIRST(&e->env->to);
462 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
463
464 const struct MbTable *c_crypt_chars = cs_subset_mbtable(NeoMutt->sub, "crypt_chars");
465 const struct MbTable *c_flag_chars = cs_subset_mbtable(NeoMutt->sub, "flag_chars");
466 const struct MbTable *c_to_chars = cs_subset_mbtable(NeoMutt->sub, "to_chars");
467 const char *const c_date_format = cs_subset_string(NeoMutt->sub, "date_format");
468
469 buf[0] = '\0';
470 switch (op)
471 {
472 case 'A':
473 case 'I':
474 if (op == 'A')
475 {
476 if (reply_to && reply_to->mailbox)
477 {
478 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
479 mutt_format_s(buf + colorlen, buflen - colorlen, prec,
480 mutt_addr_for_display(reply_to));
481 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
482 break;
483 }
484 }
485 else
486 {
487 if (mutt_mb_get_initials(mutt_get_name(from), tmp, sizeof(tmp)))
488 {
489 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
490 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
491 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
492 break;
493 }
494 }
495 /* fallthrough */
496
497 case 'a':
498 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
499 if (from && from->mailbox)
500 {
501 mutt_format_s(buf + colorlen, buflen - colorlen, prec, mutt_addr_for_display(from));
502 }
503 else
504 mutt_format_s(buf + colorlen, buflen - colorlen, prec, "");
505 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
506 break;
507
508 case 'B':
509 case 'K':
510 if (first_mailing_list(buf, buflen, &e->env->to) ||
511 first_mailing_list(buf, buflen, &e->env->cc))
512 {
513 mutt_str_copy(tmp, buf, sizeof(tmp));
514 mutt_format_s(buf, buflen, prec, tmp);
515 }
516 else if (optional)
517 {
518 optional = false;
519 }
520 break;
521
522 case 'b':
523 if (m)
524 {
525 p = strrchr(mailbox_path(m), '/');
526#ifdef USE_NOTMUCH
527 if (m->type == MUTT_NOTMUCH)
528 {
529 char *rel_path = nm_email_get_folder_rel_db(m, e);
530 if (rel_path)
531 p = rel_path;
532 }
533#endif
534
535 if (p)
536 mutt_str_copy(buf, p + 1, buflen);
537 else
538 mutt_str_copy(buf, mailbox_path(m), buflen);
539 }
540 else
541 mutt_str_copy(buf, "(null)", buflen);
542 mutt_str_copy(tmp, buf, sizeof(tmp));
543 mutt_format_s(buf, buflen, prec, tmp);
544 break;
545
546 case 'c':
547 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SIZE);
548 if (src[0] == 'r')
549 {
550 mutt_str_pretty_size(tmp, sizeof(tmp), email_size(e));
551 src++;
552 }
553 else
554 {
555 mutt_str_pretty_size(tmp, sizeof(tmp), e->body->length);
556 }
557 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
558 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
559 break;
560
561 case 'C':
562 colorlen = add_index_color(fmt, sizeof(fmt), flags, MT_COLOR_INDEX_NUMBER);
563 snprintf(fmt + colorlen, sizeof(fmt) - colorlen, "%%%sd", prec);
564 add_index_color(fmt + colorlen, sizeof(fmt) - colorlen, flags, MT_COLOR_INDEX);
565 snprintf(buf, buflen, fmt, e->msgno + 1);
566 break;
567
568 case 'd':
569 case 'D':
570 case '{':
571 case '[':
572 case '(':
573 case '<':
574 /* preprocess $date_format to handle %Z */
575 {
576 const char *cp = NULL;
577 time_t now;
578 int j = 0;
579
580 if (optional && ((op == '[') || (op == '(')))
581 {
582 now = mutt_date_now();
583 struct tm tm = mutt_date_localtime(now);
584 now -= (op == '(') ? e->received : e->date_sent;
585
586 char *is = (char *) prec;
587 bool invert = false;
588 if (*is == '>')
589 {
590 invert = true;
591 is++;
592 }
593
594 while (*is && (*is != '?'))
595 {
596 int t = strtol(is, &is, 10);
597 /* semi-broken (assuming 30 days in all months) */
598 switch (*(is++))
599 {
600 case 'y':
601 if (t > 1)
602 {
603 t--;
604 t *= (60 * 60 * 24 * 365);
605 }
606 t += ((tm.tm_mon * 60 * 60 * 24 * 30) + (tm.tm_mday * 60 * 60 * 24) +
607 (tm.tm_hour * 60 * 60) + (tm.tm_min * 60) + tm.tm_sec);
608 break;
609
610 case 'm':
611 if (t > 1)
612 {
613 t--;
614 t *= (60 * 60 * 24 * 30);
615 }
616 t += ((tm.tm_mday * 60 * 60 * 24) + (tm.tm_hour * 60 * 60) +
617 (tm.tm_min * 60) + tm.tm_sec);
618 break;
619
620 case 'w':
621 if (t > 1)
622 {
623 t--;
624 t *= (60 * 60 * 24 * 7);
625 }
626 t += ((tm.tm_wday * 60 * 60 * 24) + (tm.tm_hour * 60 * 60) +
627 (tm.tm_min * 60) + tm.tm_sec);
628 break;
629
630 case 'd':
631 if (t > 1)
632 {
633 t--;
634 t *= (60 * 60 * 24);
635 }
636 t += ((tm.tm_hour * 60 * 60) + (tm.tm_min * 60) + tm.tm_sec);
637 break;
638
639 case 'H':
640 if (t > 1)
641 {
642 t--;
643 t *= (60 * 60);
644 }
645 t += ((tm.tm_min * 60) + tm.tm_sec);
646 break;
647
648 case 'M':
649 if (t > 1)
650 {
651 t--;
652 t *= (60);
653 }
654 t += (tm.tm_sec);
655 break;
656
657 default:
658 break;
659 }
660 j += t;
661 }
662
663 if (j < 0)
664 j *= -1;
665
666 if (((now > j) || (now < (-1 * j))) ^ invert)
667 optional = false;
668 break;
669 }
670
671 p = buf;
672
673 cp = ((op == 'd') || (op == 'D')) ? (NONULL(c_date_format)) : src;
674 bool do_locales;
675 if (*cp == '!')
676 {
677 do_locales = false;
678 cp++;
679 }
680 else
681 do_locales = true;
682
683 size_t len = buflen - 1;
684 while ((len > 0) &&
685 ((((op == 'd') || (op == 'D')) && *cp) ||
686 ((op == '{') && (*cp != '}')) || ((op == '[') && (*cp != ']')) ||
687 ((op == '(') && (*cp != ')')) || ((op == '<') && (*cp != '>'))))
688 {
689 if (*cp == '%')
690 {
691 cp++;
692 if (((*cp == 'Z') || (*cp == 'z')) && ((op == 'd') || (op == '{')))
693 {
694 if (len >= 5)
695 {
696 sprintf(p, "%c%02u%02u", e->zoccident ? '-' : '+', e->zhours, e->zminutes);
697 p += 5;
698 len -= 5;
699 }
700 else
701 break; /* not enough space left */
702 }
703 else
704 {
705 if (len >= 2)
706 {
707 *p++ = '%';
708 *p++ = *cp;
709 len -= 2;
710 }
711 else
712 break; /* not enough space */
713 }
714 cp++;
715 }
716 else
717 {
718 *p++ = *cp++;
719 len--;
720 }
721 }
722 *p = '\0';
723
724 struct tm tm;
725 if ((op == '[') || (op == 'D'))
727 else if (op == '(')
729 else if (op == '<')
730 {
732 }
733 else
734 {
735 /* restore sender's time zone */
736 now = e->date_sent;
737 if (e->zoccident)
738 now -= (e->zhours * 3600 + e->zminutes * 60);
739 else
740 now += (e->zhours * 3600 + e->zminutes * 60);
741 tm = mutt_date_gmtime(now);
742 }
743
744 if (!do_locales)
745 setlocale(LC_TIME, "C");
746 strftime(tmp, sizeof(tmp), buf, &tm);
747 if (!do_locales)
748 setlocale(LC_TIME, "");
749
750 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_DATE);
751 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
752 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
753
754 if ((len > 0) && (op != 'd') && (op != 'D')) /* Skip ending op */
755 src = cp + 1;
756 break;
757 }
758
759 case 'e':
760 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
761 snprintf(buf, buflen, fmt, mutt_messages_in_thread(m, e, MIT_POSITION));
762 break;
763
764 case 'E':
765 if (!optional)
766 {
767 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
768 snprintf(buf, buflen, fmt, mutt_messages_in_thread(m, e, MIT_NUM_MESSAGES));
769 }
770 else if (mutt_messages_in_thread(m, e, MIT_NUM_MESSAGES) <= 1)
771 optional = false;
772 break;
773
774 case 'f':
775 {
776 struct Buffer *tmpbuf = mutt_buffer_pool_get();
777 mutt_addrlist_write(&e->env->from, tmpbuf, true);
778 mutt_str_copy(tmp, mutt_buffer_string(tmpbuf), sizeof(tmp));
780 mutt_format_s(buf, buflen, prec, tmp);
781 break;
782 }
783
784 case 'F':
785 if (!optional)
786 {
787 const bool is_plain = (src[0] == 'p');
788 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
789 make_from(e->env, tmp, sizeof(tmp), false,
791 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
792 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
793
794 if (is_plain)
795 src++;
796 }
797 else if (mutt_addr_is_user(from))
798 {
799 optional = false;
800 }
801 break;
802
803 case 'g':
805 if (!optional)
806 {
807 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAGS);
808 mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(tags));
809 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
810 }
811 else if (!tags)
812 optional = false;
813 FREE(&tags);
814 break;
815
816 case 'G':
817 {
818 char format[3] = { 0 };
819 char *tag = NULL;
820
821 if (!optional)
822 {
823 format[0] = op;
824 format[1] = *src;
825 format[2] = '\0';
826
827 tag = mutt_hash_find(TagFormats, format);
828 if (tag)
829 {
830 tags = driver_tags_get_transformed_for(&e->tags, tag);
831 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAG);
832 mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(tags));
833 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
834 FREE(&tags);
835 }
836 src++;
837 }
838 else
839 {
840 format[0] = op;
841 format[1] = *prec;
842 format[2] = '\0';
843
844 tag = mutt_hash_find(TagFormats, format);
845 if (tag)
846 {
847 tags = driver_tags_get_transformed_for(&e->tags, tag);
848 if (!tags)
849 optional = false;
850 FREE(&tags);
851 }
852 }
853 break;
854 }
855
856 case 'H':
857 /* (Hormel) spam score */
858 if (optional)
859 optional = !mutt_buffer_is_empty(&e->env->spam);
860
861 mutt_format_s(buf, buflen, prec, mutt_buffer_string(&e->env->spam));
862 break;
863
864 case 'i':
865 mutt_format_s(buf, buflen, prec, e->env->message_id ? e->env->message_id : "<no.id>");
866 break;
867
868 case 'J':
869 {
870 bool have_tags = true;
872 if (tags)
873 {
874 if (flags & MUTT_FORMAT_TREE)
875 {
876 char *parent_tags = NULL;
877 if (e->thread->prev && e->thread->prev->message)
878 {
879 parent_tags = driver_tags_get_transformed(&e->thread->prev->message->tags);
880 }
881 if (!parent_tags && e->thread->parent && e->thread->parent->message)
882 {
883 parent_tags = driver_tags_get_transformed(
884 &e->thread->parent->message->tags);
885 }
886 if (parent_tags && mutt_istr_equal(tags, parent_tags))
887 have_tags = false;
888 FREE(&parent_tags);
889 }
890 }
891 else
892 have_tags = false;
893
894 if (optional)
895 optional = have_tags;
896
897 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAGS);
898 if (have_tags)
899 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tags);
900 else
901 mutt_format_s(buf + colorlen, buflen - colorlen, prec, "");
902 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
903 FREE(&tags);
904 break;
905 }
906
907 case 'l':
908 if (!optional)
909 {
910 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
911 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SIZE);
912 snprintf(buf + colorlen, buflen - colorlen, fmt, (int) e->lines);
913 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
914 }
915 else if (e->lines <= 0)
916 optional = false;
917 break;
918
919 case 'L':
920 if (!optional)
921 {
922 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
923 make_from(e->env, tmp, sizeof(tmp), true, flags);
924 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
925 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
926 }
927 else if (!check_for_mailing_list(&e->env->to, NULL, NULL, 0) &&
928 !check_for_mailing_list(&e->env->cc, NULL, NULL, 0))
929 {
930 optional = false;
931 }
932 break;
933
934 case 'm':
935 if (m)
936 {
937 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
938 snprintf(buf, buflen, fmt, m->msg_count);
939 }
940 else
941 mutt_str_copy(buf, "(null)", buflen);
942 break;
943
944 case 'n':
945 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
946 mutt_format_s(buf + colorlen, buflen - colorlen, prec, mutt_get_name(from));
947 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
948 break;
949
950 case 'M':
951 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
952 if (!optional)
953 {
954 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_COLLAPSED);
955 if (threads && is_index && e->collapsed && (e->num_hidden > 1))
956 {
957 snprintf(buf + colorlen, buflen - colorlen, fmt, e->num_hidden);
958 add_index_color(buf, buflen - colorlen, flags, MT_COLOR_INDEX);
959 }
960 else if (is_index && threads)
961 {
962 mutt_format_s(buf + colorlen, buflen - colorlen, prec, " ");
963 add_index_color(buf, buflen - colorlen, flags, MT_COLOR_INDEX);
964 }
965 else
966 *buf = '\0';
967 }
968 else
969 {
970 if (!(threads && is_index && e->collapsed && (e->num_hidden > 1)))
971 optional = false;
972 }
973 break;
974
975 case 'N':
976 if (!optional)
977 {
978 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
979 snprintf(buf, buflen, fmt, e->score);
980 }
981 else
982 {
983 if (e->score == 0)
984 optional = false;
985 }
986 break;
987
988 case 'O':
989 if (!optional)
990 {
991 make_from_addr(e->env, tmp, sizeof(tmp), true);
992 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
993 if (!c_save_address && (p = strpbrk(tmp, "%@")))
994 *p = '\0';
995 mutt_format_s(buf, buflen, prec, tmp);
996 }
997 else if (!check_for_mailing_list_addr(&e->env->to, NULL, 0) &&
998 !check_for_mailing_list_addr(&e->env->cc, NULL, 0))
999 {
1000 optional = false;
1001 }
1002 break;
1003
1004 case 'P':
1005 mutt_str_copy(buf, hfi->pager_progress, buflen);
1006 break;
1007
1008#ifdef USE_NNTP
1009 case 'q':
1010 mutt_format_s(buf, buflen, prec, e->env->newsgroups ? e->env->newsgroups : "");
1011 break;
1012#endif
1013
1014 case 'r':
1015 {
1016 struct Buffer *tmpbuf = mutt_buffer_pool_get();
1017 mutt_addrlist_write(&e->env->to, tmpbuf, true);
1018 mutt_str_copy(tmp, mutt_buffer_string(tmpbuf), sizeof(tmp));
1019 mutt_buffer_pool_release(&tmpbuf);
1020 if (optional && (tmp[0] == '\0'))
1021 optional = false;
1022 mutt_format_s(buf, buflen, prec, tmp);
1023 break;
1024 }
1025
1026 case 'R':
1027 {
1028 struct Buffer *tmpbuf = mutt_buffer_pool_get();
1029 mutt_addrlist_write(&e->env->cc, tmpbuf, true);
1030 mutt_str_copy(tmp, mutt_buffer_string(tmpbuf), sizeof(tmp));
1031 mutt_buffer_pool_release(&tmpbuf);
1032 if (optional && (tmp[0] == '\0'))
1033 optional = false;
1034 mutt_format_s(buf, buflen, prec, tmp);
1035 break;
1036 }
1037
1038 case 's':
1039 {
1041 char *subj = NULL;
1042 if (e->env->disp_subj)
1043 subj = e->env->disp_subj;
1044 else
1045 subj = e->env->subject;
1046 if (flags & MUTT_FORMAT_TREE && !e->collapsed)
1047 {
1048 if (flags & MUTT_FORMAT_FORCESUBJ)
1049 {
1050 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SUBJECT);
1051 mutt_format_s(buf + colorlen, buflen - colorlen, "", NONULL(subj));
1052 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1053 snprintf(tmp, sizeof(tmp), "%s%s", e->tree, buf);
1054 mutt_format_s_tree(buf, buflen, prec, tmp);
1055 }
1056 else
1057 mutt_format_s_tree(buf, buflen, prec, e->tree);
1058 }
1059 else
1060 {
1061 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SUBJECT);
1062 mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(subj));
1063 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1064 }
1065 break;
1066 }
1067
1068 case 'S':
1069 {
1070 const char *wch = NULL;
1071 if (e->deleted)
1072 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1073 else if (e->attach_del)
1074 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1075 else if (e->tagged)
1076 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1077 else if (e->flagged)
1078 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1079 else if (e->replied)
1080 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1081 else if (e->read && (msg_in_pager != e->msgno))
1082 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_SEMPTY);
1083 else if (e->old)
1084 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1085 else
1086 wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1087
1088 snprintf(tmp, sizeof(tmp), "%s", wch);
1089 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1090 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1091 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1092 break;
1093 }
1094
1095 case 't':
1096 tmp[0] = '\0';
1097 if (!check_for_mailing_list(&e->env->to, "To ", tmp, sizeof(tmp)) &&
1098 !check_for_mailing_list(&e->env->cc, "Cc ", tmp, sizeof(tmp)))
1099 {
1100 if (to)
1101 snprintf(tmp, sizeof(tmp), "To %s", mutt_get_name(to));
1102 else if (cc)
1103 snprintf(tmp, sizeof(tmp), "Cc %s", mutt_get_name(cc));
1104 }
1105 mutt_format_s(buf, buflen, prec, tmp);
1106 break;
1107
1108 case 'T':
1109 {
1110 int i;
1111 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1112 snprintf(buf, buflen, fmt,
1113 (c_to_chars && ((i = user_is_recipient(e))) < c_to_chars->len) ?
1114 c_to_chars->chars[i] :
1115 " ");
1116 break;
1117 }
1118
1119 case 'u':
1120 if (from && from->mailbox)
1121 {
1122 mutt_str_copy(tmp, mutt_addr_for_display(from), sizeof(tmp));
1123 p = strpbrk(tmp, "%@");
1124 if (p)
1125 *p = '\0';
1126 }
1127 else
1128 tmp[0] = '\0';
1129 mutt_format_s(buf, buflen, prec, tmp);
1130 break;
1131
1132 case 'v':
1133 if (mutt_addr_is_user(from))
1134 {
1135 if (to)
1136 mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(to));
1137 else if (cc)
1138 mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(cc));
1139 else
1140 *tmp = '\0';
1141 }
1142 else
1143 mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(from));
1144 p = strpbrk(tmp, " %@");
1145 if (p)
1146 *p = '\0';
1147 mutt_format_s(buf, buflen, prec, tmp);
1148 break;
1149
1150 case 'W':
1151 if (!optional)
1152 {
1153 mutt_format_s(buf, buflen, prec, e->env->organization ? e->env->organization : "");
1154 }
1155 else if (!e->env->organization)
1156 optional = false;
1157 break;
1158
1159#ifdef USE_NNTP
1160 case 'x':
1161 if (!optional)
1162 {
1163 mutt_format_s(buf, buflen, prec, e->env->x_comment_to ? e->env->x_comment_to : "");
1164 }
1165 else if (!e->env->x_comment_to)
1166 optional = false;
1167 break;
1168#endif
1169
1170 case 'X':
1171 {
1172 struct Message *msg = mx_msg_open(m, e->msgno);
1173 if (msg)
1174 {
1175 int count = mutt_count_body_parts(m, e, msg->fp);
1176 mx_msg_close(m, &msg);
1177
1178 /* The recursion allows messages without depth to return 0. */
1179 if (optional)
1180 optional = (count != 0);
1181
1182 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
1183 snprintf(buf, buflen, fmt, count);
1184 }
1185 break;
1186 }
1187
1188 case 'y':
1189 if (optional)
1190 optional = (e->env->x_label != NULL);
1191
1192 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_LABEL);
1193 mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(e->env->x_label));
1194 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1195 break;
1196
1197 case 'Y':
1198 {
1199 bool label = true;
1200 if (e->env->x_label)
1201 {
1202 struct Email *e_tmp = NULL;
1203 if (flags & MUTT_FORMAT_TREE && (e->thread->prev && e->thread->prev->message &&
1204 e->thread->prev->message->env->x_label))
1205 {
1206 e_tmp = e->thread->prev->message;
1207 }
1208 else if (flags & MUTT_FORMAT_TREE &&
1209 (e->thread->parent && e->thread->parent->message &&
1211 {
1212 e_tmp = e->thread->parent->message;
1213 }
1214 if (e_tmp && mutt_istr_equal(e->env->x_label, e_tmp->env->x_label))
1215 label = false;
1216 }
1217 else
1218 label = false;
1219
1220 if (optional)
1221 optional = label;
1222
1223 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_LABEL);
1224 if (label)
1225 mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(e->env->x_label));
1226 else
1227 mutt_format_s(buf + colorlen, buflen - colorlen, prec, "");
1228 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1229 break;
1230 }
1231
1232 case 'z':
1233 if (src[0] == 's') /* status: deleted/new/old/replied */
1234 {
1235 const char *ch = NULL;
1236 if (e->deleted)
1237 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1238 else if (e->attach_del)
1239 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1240 else if (threads && thread_is_new(e))
1241 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW_THREAD);
1242 else if (threads && thread_is_old(e))
1243 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD_THREAD);
1244 else if (e->read && (msg_in_pager != e->msgno))
1245 {
1246 if (e->replied)
1247 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1248 else
1249 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_ZEMPTY);
1250 }
1251 else
1252 {
1253 if (e->old)
1254 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1255 else
1256 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1257 }
1258
1259 snprintf(tmp, sizeof(tmp), "%s", ch);
1260 src++;
1261 }
1262 else if (src[0] == 'c') /* crypto */
1263 {
1264 const char *ch = "";
1265 if ((WithCrypto != 0) && (e->security & SEC_GOODSIGN))
1266 ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_GOOD_SIGN);
1267 else if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1268 ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_ENCRYPTED);
1269 else if ((WithCrypto != 0) && (e->security & SEC_SIGN))
1270 ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_SIGNED);
1271 else if (((WithCrypto & APPLICATION_PGP) != 0) && ((e->security & PGP_KEY) == PGP_KEY))
1272 {
1273 ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_CONTAINS_KEY);
1274 }
1275 else
1276 ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_NO_CRYPTO);
1277
1278 snprintf(tmp, sizeof(tmp), "%s", ch);
1279 src++;
1280 }
1281 else if (src[0] == 't') /* tagged, flagged, recipient */
1282 {
1283 const char *ch = "";
1284 if (e->tagged)
1285 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1286 else if (e->flagged)
1287 ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1288 else
1289 ch = get_nth_wchar(c_to_chars, user_is_recipient(e));
1290
1291 snprintf(tmp, sizeof(tmp), "%s", ch);
1292 src++;
1293 }
1294 else /* fallthrough */
1295 break;
1296
1297 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1298 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1299 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1300 break;
1301
1302 case 'Z':
1303 {
1304 /* New/Old for threads; replied; New/Old for messages */
1305 const char *first = NULL;
1306 if (threads && thread_is_new(e))
1307 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW_THREAD);
1308 else if (threads && thread_is_old(e))
1309 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD_THREAD);
1310 else if (e->read && (msg_in_pager != e->msgno))
1311 {
1312 if (e->replied)
1313 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1314 else
1315 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_ZEMPTY);
1316 }
1317 else
1318 {
1319 if (e->old)
1320 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1321 else
1322 first = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1323 }
1324
1325 /* Marked for deletion; deleted attachments; crypto */
1326 const char *second = "";
1327 if (e->deleted)
1328 second = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1329 else if (e->attach_del)
1330 second = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1331 else if ((WithCrypto != 0) && (e->security & SEC_GOODSIGN))
1332 second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_GOOD_SIGN);
1333 else if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1334 second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_ENCRYPTED);
1335 else if ((WithCrypto != 0) && (e->security & SEC_SIGN))
1336 second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_SIGNED);
1337 else if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & PGP_KEY))
1338 second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_CONTAINS_KEY);
1339 else
1340 second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_NO_CRYPTO);
1341
1342 /* Tagged, flagged and recipient flag */
1343 const char *third = "";
1344 if (e->tagged)
1345 third = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1346 else if (e->flagged)
1347 third = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1348 else
1349 third = get_nth_wchar(c_to_chars, user_is_recipient(e));
1350
1351 snprintf(tmp, sizeof(tmp), "%s%s%s", first, second, third);
1352 }
1353
1354 colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1355 mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1356 add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1357 break;
1358
1359 case '@':
1360 {
1361 if (!m)
1362 break;
1363
1364 const char *end = src;
1365 static unsigned char recurse = 0;
1366
1367 while ((*end != '\0') && (*end != '@'))
1368 end++;
1369 if ((*end == '@') && (recurse < 20))
1370 {
1371 recurse++;
1372 mutt_strn_copy(tmp, src, end - src, sizeof(tmp));
1373 mutt_expando_format(tmp, sizeof(tmp), col, cols,
1374 NONULL(mutt_idxfmt_hook(tmp, m, e)),
1375 index_format_str, data, flags);
1376 mutt_format_s_x(buf, buflen, prec, tmp, true);
1377 recurse--;
1378
1379 src = end + 1;
1380 break;
1381 }
1382 }
1383 /* fallthrough */
1384
1385 default:
1386 snprintf(buf, buflen, "%%%s%c", prec, op);
1387 break;
1388 }
1389
1390 if (optional)
1391 {
1392 mutt_expando_format(buf, buflen, col, cols, if_str, index_format_str, data, flags);
1393 }
1394 else if (flags & MUTT_FORMAT_OPTIONAL)
1395 {
1396 mutt_expando_format(buf, buflen, col, cols, else_str, index_format_str, data, flags);
1397 }
1398
1399 /* We return the format string, unchanged */
1400 return src;
1401}
1402
1417void mutt_make_string(char *buf, size_t buflen, int cols, const char *s,
1418 struct Mailbox *m, int inpgr, struct Email *e,
1419 MuttFormatFlags flags, const char *progress)
1420{
1421 struct HdrFormatInfo hfi = { 0 };
1422
1423 hfi.email = e;
1424 hfi.mailbox = m;
1425 hfi.msg_in_pager = inpgr;
1426 hfi.pager_progress = progress;
1427
1428 mutt_expando_format(buf, buflen, 0, cols, s, index_format_str, (intptr_t) &hfi, flags);
1429}
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition: address.c:1184
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:986
Email Address Handling.
Email Aliases.
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:574
GUI display the mailboxes in a side panel.
int mutt_count_body_parts(const struct Mailbox *m, struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition: attachments.c:250
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
Color and attribute parsing.
ColorId
List of all colored objects.
Definition: color.h:38
@ MT_COLOR_INDEX_AUTHOR
Index: author field.
Definition: color.h:82
@ MT_COLOR_INDEX_SIZE
Index: size field.
Definition: color.h:88
@ MT_COLOR_INDEX_TAGS
Index: tags field (g, J)
Definition: color.h:91
@ MT_COLOR_INDEX_SUBJECT
Index: subject field.
Definition: color.h:89
@ MT_COLOR_INDEX_DATE
Index: date field.
Definition: color.h:84
@ MT_COLOR_INDEX_TAG
Index: tag field (G)
Definition: color.h:90
@ MT_COLOR_INDEX_LABEL
Index: label field.
Definition: color.h:86
@ MT_COLOR_INDEX
Index: default colour.
Definition: color.h:81
@ MT_COLOR_INDEX_NUMBER
Index: index number.
Definition: color.h:87
@ MT_COLOR_INDEX_FLAGS
Index: flags field.
Definition: color.h:85
@ MT_COLOR_INDEX_COLLAPSED
Index: number of messages in collapsed thread.
Definition: color.h:83
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
struct MbTable * cs_subset_mbtable(const struct ConfigSubset *sub, const char *name)
Get a Multibyte table config item by name.
Definition: helpers.c:145
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.
Convenience wrapper for the core headers.
void mutt_format_s_x(char *buf, size_t buflen, const char *prec, const char *s, bool arboreal)
Format a string like snprintf()
Definition: curs_lib.c:755
void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string with tree characters.
Definition: curs_lib.c:804
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:792
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:653
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition: date.c:674
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
size_t email_size(const struct Email *e)
Compute the size of an email.
Definition: email.c:125
Structs that make up an email.
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_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define MUTT_FORMAT_INDEX
This is a main index entry.
Definition: format_flags.h:36
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
#define MUTT_FORMAT_TREE
Draw the thread tree.
Definition: format_flags.h:32
#define MUTT_FORMAT_PLAIN
Do not prepend DISP_TO, DISP_CC ...
Definition: format_flags.h:38
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:778
static const char * index_format_str(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)
Format a string for the index list - Implements format_t -.
Definition: hdrline.c:439
Convenience wrapper for the gui headers.
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:362
static const char * make_from_prefix(enum FieldType disp)
Create a prefix for an author field.
Definition: hdrline.c:185
CryptChars
Index into the $crypt_chars variable ($crypt_chars)
Definition: hdrline.c:96
@ FLAG_CHAR_CRYPT_CONTAINS_KEY
Character denoting a message contains a PGP key.
Definition: hdrline.c:100
@ FLAG_CHAR_CRYPT_SIGNED
Character denoting a message is signed.
Definition: hdrline.c:99
@ FLAG_CHAR_CRYPT_NO_CRYPTO
Character denoting a message has no cryptography information.
Definition: hdrline.c:101
@ FLAG_CHAR_CRYPT_GOOD_SIGN
Character denoting a message signed with a verified key.
Definition: hdrline.c:97
@ FLAG_CHAR_CRYPT_ENCRYPTED
Character denoting a message is PGP-encrypted.
Definition: hdrline.c:98
static void make_from_addr(struct Envelope *env, char *buf, size_t buflen, bool do_lists)
Create a 'from' address for a reply email.
Definition: hdrline.c:277
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1417
FieldType
Header types.
Definition: hdrline.c:110
@ DISP_PLAIN
Empty string.
Definition: hdrline.c:115
@ DISP_TO
To: string.
Definition: hdrline.c:111
@ DISP_CC
Cc: string.
Definition: hdrline.c:112
@ DISP_BCC
Bcc: string.
Definition: hdrline.c:113
@ DISP_MAX
Definition: hdrline.c:116
@ DISP_FROM
From: string.
Definition: hdrline.c:114
static bool thread_is_old(struct Email *e)
Does the email thread contain any unread emails?
Definition: hdrline.c:377
static int user_is_recipient(struct Email *e)
Is the user a recipient of the message.
Definition: hdrline.c:327
static size_t add_index_color(char *buf, size_t buflen, MuttFormatFlags flags, enum ColorId color)
Insert a color marker into a string.
Definition: hdrline.c:129
static void make_from(struct Envelope *env, char *buf, size_t buflen, bool do_lists, MuttFormatFlags flags)
Generate a From: field (with optional prefix)
Definition: hdrline.c:221
static const char * get_nth_wchar(const struct MbTable *table, int index)
Extract one char from a multi-byte table.
Definition: hdrline.c:166
static bool thread_is_new(struct Email *e)
Does the email thread contain any new emails?
Definition: hdrline.c:367
static bool user_in_addr(struct AddressList *al)
Do any of the addresses refer to the user?
Definition: hdrline.c:307
FlagChars
Index into the $flag_chars variable ($flag_chars)
Definition: hdrline.c:78
@ FLAG_CHAR_OLD
Character denoting an email that has been read.
Definition: hdrline.c:84
@ FLAG_CHAR_REPLIED
Character denoting an email that has been replied to.
Definition: hdrline.c:83
@ FLAG_CHAR_OLD_THREAD
Character denoting a thread of emails that has been read.
Definition: hdrline.c:86
@ FLAG_CHAR_ZEMPTY
Character denoting a read email, $index_format Z expando.
Definition: hdrline.c:89
@ FLAG_CHAR_TAGGED
Character denoting a tagged email.
Definition: hdrline.c:79
@ FLAG_CHAR_NEW
Character denoting an unread email.
Definition: hdrline.c:85
@ FLAG_CHAR_DELETED
Character denoting a deleted email.
Definition: hdrline.c:81
@ FLAG_CHAR_NEW_THREAD
Character denoting a thread containing at least one new email.
Definition: hdrline.c:87
@ FLAG_CHAR_DELETED_ATTACH
Character denoting a deleted attachment.
Definition: hdrline.c:82
@ FLAG_CHAR_SEMPTY
Character denoting a read email, $index_format S expando.
Definition: hdrline.c:88
@ FLAG_CHAR_IMPORTANT
Character denoting a important (flagged) email.
Definition: hdrline.c:80
String processing routines to generate the mail index.
const char * mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
Get index-format-hook format string.
Definition: hook.c:952
Parse and execute user-defined hooks.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
bool check_for_mailing_list(struct AddressList *al, const char *pfx, char *buf, int buflen)
Search list of addresses for a mailing list.
Definition: maillist.c:78
bool check_for_mailing_list_addr(struct AddressList *al, char *buf, int buflen)
Check an address list for a mailing list.
Definition: maillist.c:102
bool first_mailing_list(char *buf, size_t buflen, struct AddressList *al)
Get the first mailing list in the list of addresses.
Definition: maillist.c:124
Handle mailing lists.
bool mutt_mb_get_initials(const char *name, char *buf, size_t buflen)
Turn a name into initials.
Definition: mbyte.c:82
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
char * mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
Copy a sub-string into a buffer.
Definition: string.c:431
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
int mutt_messages_in_thread(struct Mailbox *m, struct Email *e, enum MessageInThread mit)
Count the messages in a thread.
Definition: mutt_thread.c:1608
Create/manipulate threading in emails.
#define mutt_using_threads()
Definition: mutt_thread.h:100
@ MIT_NUM_MESSAGES
How many messages are in the thread.
Definition: mutt_thread.h:75
@ MIT_POSITION
Our position in the thread.
Definition: mutt_thread.h:76
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:95
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition: mutt_thread.h:59
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1674
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1192
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1146
API for mailboxes.
API for encryption/signing of emails.
#define SEC_GOODSIGN
Email has a valid signature.
Definition: lib.h:80
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define PGP_KEY
Definition: lib.h:99
#define WithCrypto
Definition: lib.h:116
#define SEC_SIGN
Email is signed.
Definition: lib.h:79
Notmuch virtual mailbox type.
char * nm_email_get_folder_rel_db(struct Mailbox *m, struct Email *e)
Get the folder for a Email from the same level as the notmuch database.
Definition: notmuch.c:1471
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
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_NEXT(elm, field)
Definition: queue.h:832
#define TAILQ_EMPTY(head)
Definition: queue.h:721
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition: sort.c:136
Assorted sorting methods.
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
char * mailbox
Mailbox and host address.
Definition: address.h:38
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
String manipulation buffer.
Definition: buffer.h:34
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
unsigned int zminutes
Minutes away from UTC.
Definition: email.h:55
bool recip_valid
Is_recipient is valid.
Definition: email.h:103
struct Envelope * env
Envelope information.
Definition: email.h:66
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:119
int lines
How many lines in the body of this message?
Definition: email.h:60
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
struct Body * body
List of MIME parts.
Definition: email.h:67
char * tree
Character string to print thread tree.
Definition: email.h:124
bool old
Email is seen, but unread.
Definition: email.h:47
size_t num_hidden
Number of hidden messages in this view (only valid when collapsed is set)
Definition: email.h:122
bool zoccident
True, if west of UTC, False if east.
Definition: email.h:56
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
bool flagged
Marked important?
Definition: email.h:45
unsigned int zhours
Hours away from UTC.
Definition: email.h:54
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
bool replied
Email has been replied to.
Definition: email.h:49
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
int score
Message score.
Definition: email.h:112
int msgno
Number displayed to the user.
Definition: email.h:110
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
short recipient
User_is_recipient()'s return value, cached.
Definition: email.h:115
bool tagged
Email is tagged.
Definition: email.h:106
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
struct MuttThread * thread
Thread of Emails.
Definition: email.h:118
The header of an Email.
Definition: envelope.h:57
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
struct AddressList reply_to
Email's 'reply-to'.
Definition: envelope.h:64
char * message_id
Message ID.
Definition: envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition: envelope.h:82
char * newsgroups
List of newsgroups.
Definition: envelope.h:79
struct AddressList cc
Email's 'Cc' list.
Definition: envelope.h:61
struct Buffer spam
Spam header.
Definition: envelope.h:84
char * subject
Email's subject.
Definition: envelope.h:70
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:62
char * organization
Organisation header.
Definition: envelope.h:77
char * x_label
X-Label.
Definition: envelope.h:76
char * disp_subj
Display subject (modified copy of subject)
Definition: envelope.h:72
struct AddressList from
Email's 'From' list.
Definition: envelope.h:59
Data passed to index_format_str()
Definition: hdrline.c:67
struct Email * email
Current Email.
Definition: hdrline.c:70
int msg_in_pager
Index of Email displayed in the Pager.
Definition: hdrline.c:69
struct Mailbox * mailbox
Current Mailbox.
Definition: hdrline.c:68
const char * pager_progress
String representing Pager position through Email.
Definition: hdrline.c:71
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
Multibyte character table.
Definition: mbtable.h:34
int len
Number of characters.
Definition: mbtable.h:36
char ** chars
The array of multibyte character strings.
Definition: mbtable.h:37
A local copy of an email.
Definition: mxapi.h:43
FILE * fp
pointer to the message data
Definition: mxapi.h:44
struct Message::@0 flags
Flags for the Message.
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
bool subjrx_apply_mods(struct Envelope *env)
Apply regex modifications to the subject.
Definition: subjectrx.c:127
Subject Regex handling.
char * driver_tags_get_transformed(struct TagList *list)
Get transformed tags.
Definition: tags.c:133
struct HashTable * TagFormats
Hash Table of tag-formats (tag -> format string)
Definition: tags.c:39
char * driver_tags_get_transformed_for(struct TagList *head, const char *name)
Get transformed tag for a tag name from a header.
Definition: tags.c:172