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