NeoMutt  2022-04-29-145-g9b6a0e
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 
129 static 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 
166 static 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 
185 static 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 
221 static 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 
231  me = mutt_addr_is_user(TAILQ_FIRST(&env->from));
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 
277 static 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 
307 static 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 
327 static 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 
338  if (mutt_addr_is_user(TAILQ_FIRST(&env->from)))
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 
367 static bool thread_is_new(struct Email *e)
368 {
369  return e->collapsed && (e->num_hidden > 1) && (mutt_thread_contains_unread(e) == 1);
370 }
371 
377 static bool thread_is_old(struct Email *e)
378 {
379  return e->collapsed && (e->num_hidden > 1) && (mutt_thread_contains_unread(e) == 2);
380 }
381 
439 static 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_epoch();
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 == '(')
728  tm = mutt_date_localtime(e->received);
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  tmp[0] = '\0';
776  mutt_addrlist_write(&e->env->from, tmp, sizeof(tmp), true);
777  mutt_format_s(buf, buflen, prec, tmp);
778  break;
779 
780  case 'F':
781  if (!optional)
782  {
783  const bool is_plain = (src[0] == 'p');
784  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
785  make_from(e->env, tmp, sizeof(tmp), false,
786  (is_plain ? MUTT_FORMAT_PLAIN : MUTT_FORMAT_NO_FLAGS));
787  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
788  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
789 
790  if (is_plain)
791  src++;
792  }
793  else if (mutt_addr_is_user(from))
794  {
795  optional = false;
796  }
797  break;
798 
799  case 'g':
800  tags = driver_tags_get_transformed(&e->tags);
801  if (!optional)
802  {
803  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAGS);
804  mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(tags));
805  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
806  }
807  else if (!tags)
808  optional = false;
809  FREE(&tags);
810  break;
811 
812  case 'G':
813  {
814  char format[3];
815  char *tag = NULL;
816 
817  if (!optional)
818  {
819  format[0] = op;
820  format[1] = *src;
821  format[2] = '\0';
822 
823  tag = mutt_hash_find(TagFormats, format);
824  if (tag)
825  {
826  tags = driver_tags_get_transformed_for(&e->tags, tag);
827  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAG);
828  mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(tags));
829  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
830  FREE(&tags);
831  }
832  src++;
833  }
834  else
835  {
836  format[0] = op;
837  format[1] = *prec;
838  format[2] = '\0';
839 
840  tag = mutt_hash_find(TagFormats, format);
841  if (tag)
842  {
843  tags = driver_tags_get_transformed_for(&e->tags, tag);
844  if (!tags)
845  optional = false;
846  FREE(&tags);
847  }
848  }
849  break;
850  }
851 
852  case 'H':
853  /* (Hormel) spam score */
854  if (optional)
855  optional = !mutt_buffer_is_empty(&e->env->spam);
856 
857  mutt_format_s(buf, buflen, prec, mutt_buffer_string(&e->env->spam));
858  break;
859 
860  case 'i':
861  mutt_format_s(buf, buflen, prec, e->env->message_id ? e->env->message_id : "<no.id>");
862  break;
863 
864  case 'J':
865  {
866  bool have_tags = true;
867  tags = driver_tags_get_transformed(&e->tags);
868  if (tags)
869  {
870  if (flags & MUTT_FORMAT_TREE)
871  {
872  char *parent_tags = NULL;
873  if (e->thread->prev && e->thread->prev->message)
874  {
875  parent_tags = driver_tags_get_transformed(&e->thread->prev->message->tags);
876  }
877  if (!parent_tags && e->thread->parent && e->thread->parent->message)
878  {
879  parent_tags = driver_tags_get_transformed(
880  &e->thread->parent->message->tags);
881  }
882  if (parent_tags && mutt_istr_equal(tags, parent_tags))
883  have_tags = false;
884  FREE(&parent_tags);
885  }
886  }
887  else
888  have_tags = false;
889 
890  if (optional)
891  optional = have_tags;
892 
893  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_TAGS);
894  if (have_tags)
895  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tags);
896  else
897  mutt_format_s(buf + colorlen, buflen - colorlen, prec, "");
898  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
899  FREE(&tags);
900  break;
901  }
902 
903  case 'l':
904  if (!optional)
905  {
906  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
907  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SIZE);
908  snprintf(buf + colorlen, buflen - colorlen, fmt, (int) e->lines);
909  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
910  }
911  else if (e->lines <= 0)
912  optional = false;
913  break;
914 
915  case 'L':
916  if (!optional)
917  {
918  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
919  make_from(e->env, tmp, sizeof(tmp), true, flags);
920  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
921  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
922  }
923  else if (!check_for_mailing_list(&e->env->to, NULL, NULL, 0) &&
924  !check_for_mailing_list(&e->env->cc, NULL, NULL, 0))
925  {
926  optional = false;
927  }
928  break;
929 
930  case 'm':
931  if (m)
932  {
933  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
934  snprintf(buf, buflen, fmt, m->msg_count);
935  }
936  else
937  mutt_str_copy(buf, "(null)", buflen);
938  break;
939 
940  case 'n':
941  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_AUTHOR);
942  mutt_format_s(buf + colorlen, buflen - colorlen, prec, mutt_get_name(from));
943  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
944  break;
945 
946  case 'M':
947  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
948  if (!optional)
949  {
950  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_COLLAPSED);
951  if (threads && is_index && e->collapsed && (e->num_hidden > 1))
952  {
953  snprintf(buf + colorlen, buflen - colorlen, fmt, e->num_hidden);
954  add_index_color(buf, buflen - colorlen, flags, MT_COLOR_INDEX);
955  }
956  else if (is_index && threads)
957  {
958  mutt_format_s(buf + colorlen, buflen - colorlen, prec, " ");
959  add_index_color(buf, buflen - colorlen, flags, MT_COLOR_INDEX);
960  }
961  else
962  *buf = '\0';
963  }
964  else
965  {
966  if (!(threads && is_index && e->collapsed && (e->num_hidden > 1)))
967  optional = false;
968  }
969  break;
970 
971  case 'N':
972  if (!optional)
973  {
974  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
975  snprintf(buf, buflen, fmt, e->score);
976  }
977  else
978  {
979  if (e->score == 0)
980  optional = false;
981  }
982  break;
983 
984  case 'O':
985  if (!optional)
986  {
987  make_from_addr(e->env, tmp, sizeof(tmp), true);
988  const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
989  if (!c_save_address && (p = strpbrk(tmp, "%@")))
990  *p = '\0';
991  mutt_format_s(buf, buflen, prec, tmp);
992  }
993  else if (!check_for_mailing_list_addr(&e->env->to, NULL, 0) &&
994  !check_for_mailing_list_addr(&e->env->cc, NULL, 0))
995  {
996  optional = false;
997  }
998  break;
999 
1000  case 'P':
1001  mutt_str_copy(buf, hfi->pager_progress, buflen);
1002  break;
1003 
1004 #ifdef USE_NNTP
1005  case 'q':
1006  mutt_format_s(buf, buflen, prec, e->env->newsgroups ? e->env->newsgroups : "");
1007  break;
1008 #endif
1009 
1010  case 'r':
1011  tmp[0] = '\0';
1012  mutt_addrlist_write(&e->env->to, tmp, sizeof(tmp), true);
1013  if (optional && (tmp[0] == '\0'))
1014  optional = false;
1015  mutt_format_s(buf, buflen, prec, tmp);
1016  break;
1017 
1018  case 'R':
1019  tmp[0] = '\0';
1020  mutt_addrlist_write(&e->env->cc, tmp, sizeof(tmp), true);
1021  if (optional && (tmp[0] == '\0'))
1022  optional = false;
1023  mutt_format_s(buf, buflen, prec, tmp);
1024  break;
1025 
1026  case 's':
1027  {
1028  subjrx_apply_mods(e->env);
1029  char *subj = NULL;
1030  if (e->env->disp_subj)
1031  subj = e->env->disp_subj;
1032  else
1033  subj = e->env->subject;
1034  if (flags & MUTT_FORMAT_TREE && !e->collapsed)
1035  {
1036  if (flags & MUTT_FORMAT_FORCESUBJ)
1037  {
1038  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SUBJECT);
1039  mutt_format_s(buf + colorlen, buflen - colorlen, "", NONULL(subj));
1040  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1041  snprintf(tmp, sizeof(tmp), "%s%s", e->tree, buf);
1042  mutt_format_s_tree(buf, buflen, prec, tmp);
1043  }
1044  else
1045  mutt_format_s_tree(buf, buflen, prec, e->tree);
1046  }
1047  else
1048  {
1049  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_SUBJECT);
1050  mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(subj));
1051  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1052  }
1053  break;
1054  }
1055 
1056  case 'S':
1057  {
1058  const char *wch = NULL;
1059  if (e->deleted)
1060  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1061  else if (e->attach_del)
1062  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1063  else if (e->tagged)
1064  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1065  else if (e->flagged)
1066  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1067  else if (e->replied)
1068  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1069  else if (e->read && (msg_in_pager != e->msgno))
1070  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_SEMPTY);
1071  else if (e->old)
1072  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1073  else
1074  wch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1075 
1076  snprintf(tmp, sizeof(tmp), "%s", wch);
1077  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1078  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1079  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1080  break;
1081  }
1082 
1083  case 't':
1084  tmp[0] = '\0';
1085  if (!check_for_mailing_list(&e->env->to, "To ", tmp, sizeof(tmp)) &&
1086  !check_for_mailing_list(&e->env->cc, "Cc ", tmp, sizeof(tmp)))
1087  {
1088  if (to)
1089  snprintf(tmp, sizeof(tmp), "To %s", mutt_get_name(to));
1090  else if (cc)
1091  snprintf(tmp, sizeof(tmp), "Cc %s", mutt_get_name(cc));
1092  }
1093  mutt_format_s(buf, buflen, prec, tmp);
1094  break;
1095 
1096  case 'T':
1097  {
1098  int i;
1099  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1100  snprintf(buf, buflen, fmt,
1101  (c_to_chars && ((i = user_is_recipient(e))) < c_to_chars->len) ?
1102  c_to_chars->chars[i] :
1103  " ");
1104  break;
1105  }
1106 
1107  case 'u':
1108  if (from && from->mailbox)
1109  {
1110  mutt_str_copy(tmp, mutt_addr_for_display(from), sizeof(tmp));
1111  p = strpbrk(tmp, "%@");
1112  if (p)
1113  *p = '\0';
1114  }
1115  else
1116  tmp[0] = '\0';
1117  mutt_format_s(buf, buflen, prec, tmp);
1118  break;
1119 
1120  case 'v':
1121  if (mutt_addr_is_user(from))
1122  {
1123  if (to)
1124  mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(to));
1125  else if (cc)
1126  mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(cc));
1127  else
1128  *tmp = '\0';
1129  }
1130  else
1131  mutt_format_s(tmp, sizeof(tmp), prec, mutt_get_name(from));
1132  p = strpbrk(tmp, " %@");
1133  if (p)
1134  *p = '\0';
1135  mutt_format_s(buf, buflen, prec, tmp);
1136  break;
1137 
1138  case 'W':
1139  if (!optional)
1140  {
1141  mutt_format_s(buf, buflen, prec, e->env->organization ? e->env->organization : "");
1142  }
1143  else if (!e->env->organization)
1144  optional = false;
1145  break;
1146 
1147 #ifdef USE_NNTP
1148  case 'x':
1149  if (!optional)
1150  {
1151  mutt_format_s(buf, buflen, prec, e->env->x_comment_to ? e->env->x_comment_to : "");
1152  }
1153  else if (!e->env->x_comment_to)
1154  optional = false;
1155  break;
1156 #endif
1157 
1158  case 'X':
1159  {
1160  struct Message *msg = mx_msg_open(m, e->msgno);
1161  if (msg)
1162  {
1163  int count = mutt_count_body_parts(m, e, msg->fp);
1164  mx_msg_close(m, &msg);
1165 
1166  /* The recursion allows messages without depth to return 0. */
1167  if (optional)
1168  optional = (count != 0);
1169 
1170  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
1171  snprintf(buf, buflen, fmt, count);
1172  }
1173  break;
1174  }
1175 
1176  case 'y':
1177  if (optional)
1178  optional = (e->env->x_label != NULL);
1179 
1180  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_LABEL);
1181  mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(e->env->x_label));
1182  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1183  break;
1184 
1185  case 'Y':
1186  {
1187  bool label = true;
1188  if (e->env->x_label)
1189  {
1190  struct Email *e_tmp = NULL;
1191  if (flags & MUTT_FORMAT_TREE && (e->thread->prev && e->thread->prev->message &&
1192  e->thread->prev->message->env->x_label))
1193  {
1194  e_tmp = e->thread->prev->message;
1195  }
1196  else if (flags & MUTT_FORMAT_TREE &&
1197  (e->thread->parent && e->thread->parent->message &&
1198  e->thread->parent->message->env->x_label))
1199  {
1200  e_tmp = e->thread->parent->message;
1201  }
1202  if (e_tmp && mutt_istr_equal(e->env->x_label, e_tmp->env->x_label))
1203  label = false;
1204  }
1205  else
1206  label = false;
1207 
1208  if (optional)
1209  optional = label;
1210 
1211  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_LABEL);
1212  if (label)
1213  mutt_format_s(buf + colorlen, buflen - colorlen, prec, NONULL(e->env->x_label));
1214  else
1215  mutt_format_s(buf + colorlen, buflen - colorlen, prec, "");
1216  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1217  break;
1218  }
1219 
1220  case 'z':
1221  if (src[0] == 's') /* status: deleted/new/old/replied */
1222  {
1223  const char *ch = NULL;
1224  if (e->deleted)
1225  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1226  else if (e->attach_del)
1227  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1228  else if (threads && thread_is_new(e))
1229  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW_THREAD);
1230  else if (threads && thread_is_old(e))
1231  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD_THREAD);
1232  else if (e->read && (msg_in_pager != e->msgno))
1233  {
1234  if (e->replied)
1235  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1236  else
1237  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_ZEMPTY);
1238  }
1239  else
1240  {
1241  if (e->old)
1242  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1243  else
1244  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1245  }
1246 
1247  snprintf(tmp, sizeof(tmp), "%s", ch);
1248  src++;
1249  }
1250  else if (src[0] == 'c') /* crypto */
1251  {
1252  const char *ch = "";
1253  if ((WithCrypto != 0) && (e->security & SEC_GOODSIGN))
1254  ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_GOOD_SIGN);
1255  else if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1256  ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_ENCRYPTED);
1257  else if ((WithCrypto != 0) && (e->security & SEC_SIGN))
1258  ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_SIGNED);
1259  else if (((WithCrypto & APPLICATION_PGP) != 0) && ((e->security & PGP_KEY) == PGP_KEY))
1260  {
1261  ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_CONTAINS_KEY);
1262  }
1263  else
1264  ch = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_NO_CRYPTO);
1265 
1266  snprintf(tmp, sizeof(tmp), "%s", ch);
1267  src++;
1268  }
1269  else if (src[0] == 't') /* tagged, flagged, recipient */
1270  {
1271  const char *ch = "";
1272  if (e->tagged)
1273  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1274  else if (e->flagged)
1275  ch = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1276  else
1277  ch = get_nth_wchar(c_to_chars, user_is_recipient(e));
1278 
1279  snprintf(tmp, sizeof(tmp), "%s", ch);
1280  src++;
1281  }
1282  else /* fallthrough */
1283  break;
1284 
1285  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1286  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1287  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1288  break;
1289 
1290  case 'Z':
1291  {
1292  /* New/Old for threads; replied; New/Old for messages */
1293  const char *first = NULL;
1294  if (threads && thread_is_new(e))
1295  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW_THREAD);
1296  else if (threads && thread_is_old(e))
1297  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD_THREAD);
1298  else if (e->read && (msg_in_pager != e->msgno))
1299  {
1300  if (e->replied)
1301  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_REPLIED);
1302  else
1303  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_ZEMPTY);
1304  }
1305  else
1306  {
1307  if (e->old)
1308  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_OLD);
1309  else
1310  first = get_nth_wchar(c_flag_chars, FLAG_CHAR_NEW);
1311  }
1312 
1313  /* Marked for deletion; deleted attachments; crypto */
1314  const char *second = "";
1315  if (e->deleted)
1316  second = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED);
1317  else if (e->attach_del)
1318  second = get_nth_wchar(c_flag_chars, FLAG_CHAR_DELETED_ATTACH);
1319  else if ((WithCrypto != 0) && (e->security & SEC_GOODSIGN))
1320  second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_GOOD_SIGN);
1321  else if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1322  second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_ENCRYPTED);
1323  else if ((WithCrypto != 0) && (e->security & SEC_SIGN))
1324  second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_SIGNED);
1325  else if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & PGP_KEY))
1326  second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_CONTAINS_KEY);
1327  else
1328  second = get_nth_wchar(c_crypt_chars, FLAG_CHAR_CRYPT_NO_CRYPTO);
1329 
1330  /* Tagged, flagged and recipient flag */
1331  const char *third = "";
1332  if (e->tagged)
1333  third = get_nth_wchar(c_flag_chars, FLAG_CHAR_TAGGED);
1334  else if (e->flagged)
1335  third = get_nth_wchar(c_flag_chars, FLAG_CHAR_IMPORTANT);
1336  else
1337  third = get_nth_wchar(c_to_chars, user_is_recipient(e));
1338 
1339  snprintf(tmp, sizeof(tmp), "%s%s%s", first, second, third);
1340  }
1341 
1342  colorlen = add_index_color(buf, buflen, flags, MT_COLOR_INDEX_FLAGS);
1343  mutt_format_s(buf + colorlen, buflen - colorlen, prec, tmp);
1344  add_index_color(buf + colorlen, buflen - colorlen, flags, MT_COLOR_INDEX);
1345  break;
1346 
1347  case '@':
1348  {
1349  if (!m)
1350  break;
1351 
1352  const char *end = src;
1353  static unsigned char recurse = 0;
1354 
1355  while ((*end != '\0') && (*end != '@'))
1356  end++;
1357  if ((*end == '@') && (recurse < 20))
1358  {
1359  recurse++;
1360  mutt_strn_copy(tmp, src, end - src, sizeof(tmp));
1361  mutt_expando_format(tmp, sizeof(tmp), col, cols,
1362  NONULL(mutt_idxfmt_hook(tmp, m, e)),
1363  index_format_str, data, flags);
1364  mutt_format_s_x(buf, buflen, prec, tmp, true);
1365  recurse--;
1366 
1367  src = end + 1;
1368  break;
1369  }
1370  }
1371  /* fallthrough */
1372 
1373  default:
1374  snprintf(buf, buflen, "%%%s%c", prec, op);
1375  break;
1376  }
1377 
1378  if (optional)
1379  {
1380  mutt_expando_format(buf, buflen, col, cols, if_str, index_format_str, data, flags);
1381  }
1382  else if (flags & MUTT_FORMAT_OPTIONAL)
1383  {
1384  mutt_expando_format(buf, buflen, col, cols, else_str, index_format_str, data, flags);
1385  }
1386 
1387  /* We return the format string, unchanged */
1388  return src;
1389 }
1390 
1405 void mutt_make_string(char *buf, size_t buflen, int cols, const char *s,
1406  struct Mailbox *m, int inpgr, struct Email *e,
1407  MuttFormatFlags flags, const char *progress)
1408 {
1409  struct HdrFormatInfo hfi = { 0 };
1410 
1411  hfi.email = e;
1412  hfi.mailbox = m;
1413  hfi.msg_in_pager = inpgr;
1414  hfi.pager_progress = progress;
1415 
1416  mutt_expando_format(buf, buflen, 0, cols, s, index_format_str, (intptr_t) &hfi, flags);
1417 }
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:986
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
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(struct Mailbox *m, struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition: attachments.c:252
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:250
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Color and attribute parsing.
ColorId
List of all colored objects.
Definition: color.h:35
@ MT_COLOR_INDEX_AUTHOR
Index: author field (takes a pattern)
Definition: color.h:77
@ MT_COLOR_INDEX_SIZE
Index: size field.
Definition: color.h:86
@ MT_COLOR_INDEX_TAGS
Index: tags field (g, J)
Definition: color.h:87
@ MT_COLOR_INDEX_SUBJECT
Index: subject field (takes a pattern)
Definition: color.h:79
@ MT_COLOR_INDEX_DATE
Index: date field.
Definition: color.h:83
@ MT_COLOR_INDEX_TAG
Index: tag field (g, takes a pattern)
Definition: color.h:80
@ MT_COLOR_INDEX_LABEL
Index: label field.
Definition: color.h:84
@ MT_COLOR_INDEX
Index: default colour (takes a pattern)
Definition: color.h:76
@ MT_COLOR_INDEX_NUMBER
Index: index number.
Definition: color.h:85
@ MT_COLOR_INDEX_FLAGS
Index: flags field (takes a pattern)
Definition: color.h:78
@ MT_COLOR_INDEX_COLLAPSED
Index: number of messages in collapsed thread.
Definition: color.h:82
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:753
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:802
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:790
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:654
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition: date.c:672
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
#define MUTT_DATE_NOW
Constant representing the 'current time', see: mutt_date_gmtime(), mutt_date_localtime()
Definition: date.h:39
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:777
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
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:1405
static const char * make_from_prefix(enum FieldType disp)
Create a prefix for an author field.
Definition: hdrline.c:185
static const char * get_nth_wchar(const struct MbTable *table, int index)
Extract one char from a multi-byte table.
Definition: hdrline.c:166
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 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:948
Parse and execute user-defined hooks.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
@ 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:796
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:408
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:629
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:1673
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1193
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1147
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:1468
#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
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:104
struct Envelope * env
Envelope information.
Definition: email.h:66
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:120
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:99
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:113
int msgno
Number displayed to the user.
Definition: email.h:111
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:110
short recipient
User_is_recipient()'s return value, cached.
Definition: email.h:116
bool tagged
Email is tagged.
Definition: email.h:107
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:119
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:128
Subject Regex handling.
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
struct HashTable * TagFormats
Hash Table of tag-formats (tag -> format string)
Definition: tags.c:39
char * driver_tags_get_transformed(struct TagList *list)
Get transformed tags.
Definition: tags.c:133