don't hide macro.
[apps/madmutt.git] / hdrline.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #include "mutt_curses.h"
16 #include "sort.h"
17 #include "thread.h"
18 #include "charset.h"
19 #include "mutt_crypt.h"
20 #include "mutt_idna.h"
21
22 #include <lib-lib/str.h>
23
24 #include <lib-mime/mime.h>
25
26 #include "lib/rx.h"
27
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <locale.h>
32
33 int mutt_is_mail_list (address_t * addr)
34 {
35   if (!rx_list_match (UnMailLists, addr->mailbox))
36     return rx_list_match (MailLists, addr->mailbox);
37   return 0;
38 }
39
40 int mutt_is_subscribed_list (address_t * addr)
41 {
42   if (!rx_list_match (UnMailLists, addr->mailbox)
43       && !rx_list_match (UnSubscribedLists, addr->mailbox))
44     return rx_list_match (SubscribedLists, addr->mailbox);
45   return 0;
46 }
47
48 /* Search for a mailing list in the list of addresses pointed to by adr.
49  * If one is found, print pfx and the name of the list into buf, then
50  * return 1.  Otherwise, simply return 0.
51  */
52 static int
53 check_for_mailing_list (address_t * adr, const char *pfx, char *buf, int buflen)
54 {
55   for (; adr; adr = adr->next) {
56     if (mutt_is_subscribed_list (adr)) {
57       if (pfx && buf && buflen)
58         snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
59       return 1;
60     }
61   }
62   return 0;
63 }
64
65 /* Search for a mailing list in the list of addresses pointed to by adr.
66  * If one is found, print the address of the list into buf, then return 1.
67  * Otherwise, simply return 0.
68  */
69 static int check_for_mailing_list_addr (address_t * adr, char *buf, int buflen)
70 {
71   for (; adr; adr = adr->next) {
72     if (mutt_is_subscribed_list (adr)) {
73       if (buf && buflen)
74         snprintf (buf, buflen, "%s", adr->mailbox);
75       return 1;
76     }
77   }
78   return 0;
79 }
80
81
82 static int first_mailing_list (char *buf, size_t buflen, address_t * a)
83 {
84   for (; a; a = a->next) {
85     if (mutt_is_subscribed_list (a)) {
86       mutt_save_path (buf, buflen, a);
87       return 1;
88     }
89   }
90   return 0;
91 }
92
93 static void make_from (ENVELOPE * hdr, char *buf, size_t len, int do_lists)
94 {
95   int me;
96
97   me = mutt_addr_is_user (hdr->from);
98
99   if (do_lists || me) {
100     if (check_for_mailing_list (hdr->to, "To ", buf, len))
101       return;
102     if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
103       return;
104   }
105
106   if (me && hdr->to)
107     snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
108   else if (me && hdr->cc)
109     snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
110   else if (hdr->from)
111     m_strcpy(buf, len, mutt_get_name(hdr->from));
112   else
113     *buf = 0;
114 }
115
116 static void make_from_addr (ENVELOPE * hdr, char *buf, size_t len,
117                             int do_lists)
118 {
119   int me;
120
121   me = mutt_addr_is_user (hdr->from);
122
123   if (do_lists || me) {
124     if (check_for_mailing_list_addr (hdr->to, buf, len))
125       return;
126     if (check_for_mailing_list_addr (hdr->cc, buf, len))
127       return;
128   }
129
130   if (me && hdr->to)
131     snprintf (buf, len, "%s", hdr->to->mailbox);
132   else if (me && hdr->cc)
133     snprintf (buf, len, "%s", hdr->cc->mailbox);
134   else if (hdr->from)
135     m_strcpy(buf, len, hdr->from->mailbox);
136   else
137     *buf = 0;
138 }
139
140 static int user_in_addr (address_t * a)
141 {
142   for (; a; a = a->next)
143     if (mutt_addr_is_user (a))
144       return 1;
145   return 0;
146 }
147
148 /* Return values:
149  * 0: user is not in list
150  * 1: user is unique recipient
151  * 2: user is in the TO list
152  * 3: user is in the CC list
153  * 4: user is originator
154  * 5: sent to a subscribed mailinglist
155  */
156 int mutt_user_is_recipient (HEADER * h)
157 {
158   ENVELOPE *env = h->env;
159
160   if (!h->recip_valid) {
161     h->recip_valid = 1;
162
163     if (mutt_addr_is_user (env->from))
164       h->recipient = 4;
165     else if (user_in_addr (env->to)) {
166       if (env->to->next || env->cc)
167         h->recipient = 2;       /* non-unique recipient */
168       else
169         h->recipient = 1;       /* unique recipient */
170     }
171     else if (user_in_addr (env->cc))
172       h->recipient = 3;
173     else if (check_for_mailing_list (env->to, NULL, NULL, 0))
174       h->recipient = 5;
175     else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
176       h->recipient = 5;
177     else
178       h->recipient = 0;
179   }
180
181   return h->recipient;
182 }
183
184 /* %a = address of author
185  * %A = reply-to address (if present; otherwise: address of author
186  * %b = filename of the originating folder
187  * %B = the list to which the letter was sent
188  * %c = size of message in bytes
189  * %C = current message number
190  * %d = date and time of message using $date_format and sender's timezone
191  * %D = date and time of message using $date_format and local timezone
192  * %e = current message number in thread
193  * %E = number of messages in current thread
194  * %f = entire from line
195  * %F = like %n, unless from self
196  * %g = newsgroup name (if compiled with nntp support)
197  * %i = message-id
198  * %I = initials of author
199  * %l = number of lines in the message
200  * %L = like %F, except `lists' are displayed first
201  * %m = number of messages in the mailbox
202  * %n = name of author
203  * %N = score
204  * %O = like %L, except using address instead of name
205  * %s = subject
206  * %S = short message status (e.g., N/O/D/!/r/-)
207  * %t = `to:' field (recipients)
208  * %T = $to_chars
209  * %u = user (login) name of author
210  * %v = first name of author, unless from self
211  * %W = where user is (organization)
212  * %X = number of MIME attachments
213  * %y = `x-label:' field (if present)
214  * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
215  * %Z = status flags    */
216
217 struct hdr_format_info {
218   CONTEXT *ctx;
219   HEADER *hdr;
220 };
221
222 static const char *hdr_format_str (char *dest,
223                                    size_t destlen,
224                                    char op,
225                                    const char *src,
226                                    const char *prefix,
227                                    const char *ifstring,
228                                    const char *elsestring,
229                                    unsigned long data, format_flag flags)
230 {
231   struct hdr_format_info *hfi = (struct hdr_format_info *) data;
232   HEADER *hdr, *htmp;
233   CONTEXT *ctx;
234   char fmt[SHORT_STRING], buf2[SHORT_STRING], ch, *p;
235   int do_locales, i;
236   int optional = (flags & M_FORMAT_OPTIONAL);
237   int threads = ((Sort & SORT_MASK) == SORT_THREADS);
238   int is_index = (flags & M_FORMAT_INDEX);
239
240 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 1)
241 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && mutt_thread_contains_unread (ctx, hdr) == 2)
242   size_t len;
243
244   hdr = hfi->hdr;
245   ctx = hfi->ctx;
246
247   dest[0] = 0;
248   switch (op) {
249   case 'A':
250     if (hdr->env->reply_to && hdr->env->reply_to->mailbox) {
251       mutt_format_s (dest, destlen, prefix,
252                      mutt_addr_for_display (hdr->env->reply_to));
253       break;
254     }
255     /* fall through if 'A' returns nothing */
256
257   case 'a':
258     if (hdr->env->from && hdr->env->from->mailbox) {
259       mutt_format_s (dest, destlen, prefix,
260                      mutt_addr_for_display (hdr->env->from));
261     }
262     else
263       dest[0] = '\0';
264     break;
265
266   case 'B':
267     if (!first_mailing_list (dest, destlen, hdr->env->to) &&
268         !first_mailing_list (dest, destlen, hdr->env->cc))
269       dest[0] = 0;
270     if (dest[0]) {
271       m_strcpy(buf2, sizeof(buf2), dest);
272       mutt_format_s (dest, destlen, prefix, buf2);
273       break;
274     }
275     /* fall through if 'B' returns nothing */
276
277   case 'b':
278     if (ctx) {
279       if ((p = strrchr (ctx->path, '/')))
280         m_strcpy(dest, destlen, p + 1);
281       else
282         m_strcpy(dest, destlen, ctx->path);
283     }
284     else
285       m_strcpy(dest, destlen, "(null)");
286     m_strcpy(buf2, sizeof(buf2), dest);
287     mutt_format_s (dest, destlen, prefix, buf2);
288     break;
289
290   case 'c':
291     mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
292     mutt_format_s (dest, destlen, prefix, buf2);
293     break;
294
295   case 'C':
296     snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
297     snprintf (dest, destlen, fmt, hdr->msgno + 1);
298     break;
299
300   case 'd':
301   case 'D':
302   case '{':
303   case '[':
304   case '(':
305   case '<':
306
307     /* preprocess $date_format to handle %Z */
308     {
309       const char *cp;
310       struct tm *tm;
311       time_t T;
312
313       p = dest;
314
315       cp = (op == 'd' || op == 'D') ? (NONULL (DateFmt)) : src;
316       if (*cp == '!') {
317         do_locales = 0;
318         cp++;
319       }
320       else
321         do_locales = 1;
322
323       len = destlen - 1;
324       while (len > 0 && (((op == 'd' || op == 'D') && *cp) ||
325                          (op == '{' && *cp != '}') ||
326                          (op == '[' && *cp != ']') ||
327                          (op == '(' && *cp != ')') ||
328                          (op == '<' && *cp != '>'))) {
329         if (*cp == '%') {
330           cp++;
331           if ((*cp == 'Z' || *cp == 'z') && (op == 'd' || op == '{')) {
332             if (len >= 5) {
333               sprintf (p, "%c%02u%02u", hdr->zoccident ? '-' : '+',
334                        hdr->zhours, hdr->zminutes);
335               p += 5;
336               len -= 5;
337             }
338             else
339               break;            /* not enough space left */
340           }
341           else {
342             if (len >= 2) {
343               *p++ = '%';
344               *p++ = *cp;
345               len -= 2;
346             }
347             else
348               break;            /* not enough space */
349           }
350           cp++;
351         }
352         else {
353           *p++ = *cp++;
354           len--;
355         }
356       }
357       *p = 0;
358
359       if (do_locales && Locale)
360         setlocale (LC_TIME, Locale);
361
362       if (op == '[' || op == 'D')
363         tm = localtime (&hdr->date_sent);
364       else if (op == '(')
365         tm = localtime (&hdr->received);
366       else if (op == '<') {
367         T = time (NULL);
368         tm = localtime (&T);
369       }
370       else {
371         /* restore sender's time zone */
372         T = hdr->date_sent;
373         if (hdr->zoccident)
374           T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
375         else
376           T += (hdr->zhours * 3600 + hdr->zminutes * 60);
377         tm = gmtime (&T);
378       }
379
380       strftime (buf2, sizeof (buf2), dest, tm);
381
382       if (do_locales)
383         setlocale (LC_TIME, "C");
384
385       mutt_format_s (dest, destlen, prefix, buf2);
386       if (len > 0 && op != 'd' && op != 'D')    /* Skip ending op */
387         src = cp + 1;
388     }
389     break;
390
391   case 'e':
392     snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
393     snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 1));
394     break;
395
396   case 'E':
397     if (!optional) {
398       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
399       snprintf (dest, destlen, fmt, mutt_messages_in_thread (ctx, hdr, 0));
400     }
401     else if (mutt_messages_in_thread (ctx, hdr, 0) <= 1)
402       optional = 0;
403     break;
404
405   case 'f':
406     buf2[0] = 0;
407     rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
408     mutt_format_s (dest, destlen, prefix, buf2);
409     break;
410
411   case 'F':
412     if (!optional) {
413       make_from (hdr->env, buf2, sizeof (buf2), 0);
414       mutt_format_s (dest, destlen, prefix, buf2);
415     }
416     else if (mutt_addr_is_user (hdr->env->from))
417       optional = 0;
418     break;
419
420 #ifdef USE_NNTP
421   case 'g':
422     mutt_format_s (dest, destlen, prefix,
423                    hdr->env->newsgroups ? hdr->env->newsgroups : "");
424     break;
425 #endif
426
427   case 'H':
428     /* (Hormel) spam score */
429     if (optional)
430       optional = hdr->env->spam ? 1 : 0;
431
432     if (hdr->env->spam)
433       mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
434     else
435       mutt_format_s (dest, destlen, prefix, "");
436
437     break;
438
439   case 'i':
440     mutt_format_s (dest, destlen, prefix,
441                    hdr->env->message_id ? hdr->env->message_id : "<no.id>");
442     break;
443
444   case 'I':
445     {
446       int iflag = FALSE;
447       int j = 0;
448
449       for (i = 0; hdr->env->from && hdr->env->from->personal &&
450            hdr->env->from->personal[i] && j < SHORT_STRING - 1; i++) {
451         if (isalpha ((int) hdr->env->from->personal[i])) {
452           if (!iflag) {
453             buf2[j++] = hdr->env->from->personal[i];
454             iflag = TRUE;
455           }
456         }
457         else
458           iflag = FALSE;
459       }
460
461       buf2[j] = '\0';
462     }
463     mutt_format_s (dest, destlen, prefix, buf2);
464     break;
465
466   case 'l':
467     if (!optional) {
468       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
469       snprintf (dest, destlen, fmt, (int) hdr->lines);
470     }
471     else if (hdr->lines <= 0)
472       optional = 0;
473     break;
474
475   case 'L':
476     if (!optional) {
477       make_from (hdr->env, buf2, sizeof (buf2), 1);
478       mutt_format_s (dest, destlen, prefix, buf2);
479     }
480     else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
481              !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0)) {
482       optional = 0;
483     }
484     break;
485
486   case 'm':
487     if (ctx) {
488       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
489       snprintf (dest, destlen, fmt, ctx->msgcount);
490     }
491     else
492       m_strcpy(dest, destlen, "(null)");
493     break;
494
495   case 'n':
496     mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
497     break;
498
499   case 'N':
500     if (!optional) {
501       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
502       snprintf (dest, destlen, fmt, hdr->score);
503     }
504     else {
505       if (hdr->score == 0)
506         optional = 0;
507     }
508     break;
509
510   case 'O':
511     if (!optional) {
512       make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
513       if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
514         *p = 0;
515       mutt_format_s (dest, destlen, prefix, buf2);
516     }
517     else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
518              !check_for_mailing_list_addr (hdr->env->cc, NULL, 0)) {
519       optional = 0;
520     }
521     break;
522
523   case 'M':
524     snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
525     if (!optional) {
526       if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
527         snprintf (dest, destlen, fmt, hdr->num_hidden);
528       else if (is_index && threads)
529         mutt_format_s (dest, destlen, prefix, " ");
530       else
531         *dest = '\0';
532     }
533     else {
534       if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
535         optional = 0;
536     }
537     break;
538
539   case 's':
540
541     if (flags & M_FORMAT_TREE && !hdr->collapsed) {
542       if (flags & M_FORMAT_FORCESUBJ) {
543         mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
544         snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
545         mutt_format_s_tree (dest, destlen, prefix, buf2);
546       }
547       else
548         mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
549     }
550     else
551       mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
552     break;
553
554   case 'S':
555     if (hdr->deleted)
556       ch = 'D';
557     else if (hdr->attach_del)
558       ch = 'd';
559     else if (hdr->tagged)
560       ch = '*';
561     else if (hdr->flagged)
562       ch = '!';
563     else if (hdr->replied)
564       ch = 'r';
565     else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
566       ch = '-';
567     else if (hdr->old)
568       ch = 'O';
569     else
570       ch = 'N';
571
572     /* FOO - this is probably unsafe, but we are not likely to have such
573        a short string passed into this routine */
574     *dest = ch;
575     *(dest + 1) = 0;
576     break;
577
578   case 't':
579     buf2[0] = 0;
580     if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
581         !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2))) {
582       if (hdr->env->to)
583         snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
584       else if (hdr->env->cc)
585         snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
586     }
587     mutt_format_s (dest, destlen, prefix, buf2);
588     break;
589
590   case 'T':
591     snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
592     snprintf (dest, destlen, fmt,
593               (Tochars
594                && ((i = mutt_user_is_recipient (hdr))) <
595                m_strlen(Tochars)) ? Tochars[i] : ' ');
596     break;
597
598   case 'u':
599     if (hdr->env->from && hdr->env->from->mailbox) {
600       m_strcpy(buf2, sizeof(buf2), mutt_addr_for_display(hdr->env->from));
601       if ((p = strpbrk (buf2, "%@")))
602         *p = 0;
603     }
604     else
605       buf2[0] = 0;
606     mutt_format_s (dest, destlen, prefix, buf2);
607     break;
608
609   case 'v':
610     if (mutt_addr_is_user (hdr->env->from)) {
611       if (hdr->env->to)
612         mutt_format_s (buf2, sizeof (buf2), prefix,
613                        mutt_get_name (hdr->env->to));
614       else if (hdr->env->cc)
615         mutt_format_s (buf2, sizeof (buf2), prefix,
616                        mutt_get_name (hdr->env->cc));
617       else
618         *buf2 = 0;
619     }
620     else
621       mutt_format_s (buf2, sizeof (buf2), prefix,
622                      mutt_get_name (hdr->env->from));
623     if ((p = strpbrk (buf2, " %@")))
624       *p = 0;
625     mutt_format_s (dest, destlen, prefix, buf2);
626     break;
627
628   case 'W':
629     if (!optional)
630       mutt_format_s (dest, destlen, prefix,
631                      hdr->env->organization ? hdr->env->organization : "");
632     else if (!hdr->env->organization)
633       optional = 0;
634     break;
635
636   case 'X':
637     {
638       int count = 0;
639
640       if (option (OPTCOUNTATTACH)) {
641         if (!hdr->content->parts)
642           mutt_parse_mime_message(ctx, hdr);
643         count = mutt_count_body_parts(hdr, 0);
644       }
645
646       /* The recursion allows messages without depth to return 0. */
647       if (optional)
648         optional = count != 0;
649       
650       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
651       snprintf (dest, destlen, fmt, count);
652     }
653     break;
654
655   case 'Z':
656
657     ch = ' ';
658
659     if (WithCrypto && hdr->security & GOODSIGN)
660       ch = 'S';
661     else if (WithCrypto && hdr->security & ENCRYPT)
662       ch = 'P';
663     else if (WithCrypto && hdr->security & SIGN)
664       ch = 's';
665     else if ((WithCrypto & APPLICATION_PGP) && hdr->security & PGPKEY)
666       ch = 'K';
667
668     snprintf (buf2, sizeof (buf2),
669               "%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' :
670                                              ((hdr->read
671                                                && (ctx
672                                                    && ctx->msgnotreadyet !=
673                                                    hdr->msgno))
674                                               ? (hdr->
675                                                  replied ? 'r' : ' ') : (hdr->
676                                                                          old ?
677                                                                          'O' :
678                                                                          'N')))),
679               hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
680               hdr->tagged ? '*' : (hdr->
681                                    flagged ? '!' : (Tochars
682                                                     &&
683                                                     ((i =
684                                                       mutt_user_is_recipient
685                                                       (hdr)) <
686                                                      m_strlen(Tochars)) ?
687                                                     Tochars[i] : ' ')));
688     mutt_format_s (dest, destlen, prefix, buf2);
689     break;
690
691   case 'y':
692     if (optional)
693       optional = hdr->env->x_label ? 1 : 0;
694
695     mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
696     break;
697
698   case 'Y':
699     if (hdr->env->x_label) {
700       i = 1;                    /* reduce reuse recycle */
701       htmp = NULL;
702       if (flags & M_FORMAT_TREE
703           && (hdr->thread->prev && hdr->thread->prev->message
704               && hdr->thread->prev->message->env->x_label))
705         htmp = hdr->thread->prev->message;
706       else if (flags & M_FORMAT_TREE
707                && (hdr->thread->parent && hdr->thread->parent->message
708                    && hdr->thread->parent->message->env->x_label))
709         htmp = hdr->thread->parent->message;
710       if (htmp && m_strcasecmp(hdr->env->x_label,
711                                    htmp->env->x_label) == 0)
712         i = 0;
713     }
714     else
715       i = 0;
716
717     if (optional)
718       optional = i;
719
720     if (i)
721       mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
722     else
723       mutt_format_s (dest, destlen, prefix, "");
724
725     break;
726
727   default:
728     snprintf (dest, destlen, "%%%s%c", prefix, op);
729     break;
730   }
731
732   if (optional)
733     mutt_FormatString (dest, destlen, ifstring, hdr_format_str,
734                        (unsigned long) hfi, flags);
735   else if (flags & M_FORMAT_OPTIONAL)
736     mutt_FormatString (dest, destlen, elsestring, hdr_format_str,
737                        (unsigned long) hfi, flags);
738
739   return (src);
740 #undef THREAD_NEW
741 #undef THREAD_OLD
742 }
743
744 void
745 _mutt_make_string (char *dest, size_t destlen, const char *s, CONTEXT * ctx,
746                    HEADER * hdr, format_flag flags)
747 {
748   struct hdr_format_info hfi;
749
750   hfi.hdr = hdr;
751   hfi.ctx = ctx;
752
753   mutt_FormatString (dest, destlen, s, hdr_format_str, (unsigned long) &hfi,
754                      flags);
755 }