]> git.llucax.com Git - software/mutt-debian.git/blob - hdrline.c
Remove maildir-mtime patch, upstream has a different implementation (though with...
[software/mutt-debian.git] / hdrline.c
1 /*
2  * Copyright (C) 1996-2000,2002,2007 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 #ifdef HAVE_ALLOCA_H
36 #include <alloca.h>
37 #endif
38
39 int mutt_is_mail_list (ADDRESS *addr)
40 {
41   if (!mutt_match_rx_list (addr->mailbox, UnMailLists))
42     return mutt_match_rx_list (addr->mailbox, MailLists);
43   return 0;
44 }
45
46 int mutt_is_subscribed_list (ADDRESS *addr)
47 {
48   if (!mutt_match_rx_list (addr->mailbox, UnMailLists)
49       && !mutt_match_rx_list (addr->mailbox, UnSubscribedLists))
50     return mutt_match_rx_list (addr->mailbox, SubscribedLists);
51   return 0;
52 }
53
54 /* Search for a mailing list in the list of addresses pointed to by adr.
55  * If one is found, print pfx and the name of the list into buf, then
56  * return 1.  Otherwise, simply return 0.
57  */
58 static int
59 check_for_mailing_list (ADDRESS *adr, char *pfx, char *buf, int buflen)
60 {
61   for (; adr; adr = adr->next)
62   {
63     if (mutt_is_subscribed_list (adr))
64     {
65       if (pfx && buf && buflen)
66         snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
67       return 1;
68     }
69   }
70   return 0;
71 }
72
73 /* Search for a mailing list in the list of addresses pointed to by adr.
74  * If one is found, print the address of the list into buf, then return 1.
75  * Otherwise, simply return 0.
76  */
77 static int
78 check_for_mailing_list_addr (ADDRESS *adr, char *buf, int buflen)
79 {
80   for (; adr; adr = adr->next)
81   {
82     if (mutt_is_subscribed_list (adr))
83     {
84       if (buf && buflen)
85         snprintf (buf, buflen, "%s", adr->mailbox);
86       return 1;
87     }
88   }
89   return 0;
90 }
91
92
93 static int first_mailing_list (char *buf, size_t buflen, ADDRESS *a)
94 {
95   for (; a; a = a->next)
96   {
97     if (mutt_is_subscribed_list (a))
98     {
99       mutt_save_path (buf, buflen, a);
100       return 1;
101     }
102   }
103   return 0;
104 }
105
106 static void make_from (ENVELOPE *hdr, char *buf, size_t len, int do_lists)
107 {
108   int me;
109
110   me = mutt_addr_is_user (hdr->from);
111
112   if (do_lists || me)
113   {
114     if (check_for_mailing_list (hdr->to, "To ", buf, len))
115       return;
116     if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
117       return;
118   }
119
120   if (me && hdr->to)
121     snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
122   else if (me && hdr->cc)
123     snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
124   else if (me && hdr->bcc)
125     snprintf (buf, len, "Bcc %s", mutt_get_name (hdr->bcc));
126   else if (hdr->from)
127     strfcpy (buf, mutt_get_name (hdr->from), len);
128   else
129     *buf = 0;
130 }
131
132 static void make_from_addr (ENVELOPE *hdr, char *buf, size_t len, int do_lists)
133 {
134   int me;
135
136   me = mutt_addr_is_user (hdr->from);
137
138   if (do_lists || me)
139   {
140     if (check_for_mailing_list_addr (hdr->to, buf, len))
141       return;
142     if (check_for_mailing_list_addr (hdr->cc, buf, len))
143       return;
144   }
145
146   if (me && hdr->to)
147     snprintf (buf, len, "%s", hdr->to->mailbox);
148   else if (me && hdr->cc)
149     snprintf (buf, len, "%s", hdr->cc->mailbox);
150   else if (hdr->from)
151     strfcpy (buf, hdr->from->mailbox, len);
152   else
153     *buf = 0;
154 }
155
156 static int user_in_addr (ADDRESS *a)
157 {
158   for (; a; a = a->next)
159     if (mutt_addr_is_user (a))
160       return 1;
161   return 0;
162 }
163
164 /* Return values:
165  * 0: user is not in list
166  * 1: user is unique recipient
167  * 2: user is in the TO list
168  * 3: user is in the CC list
169  * 4: user is originator
170  * 5: sent to a subscribed mailinglist
171  */
172 int mutt_user_is_recipient (HEADER *h)
173 {
174   ENVELOPE *env = h->env;
175
176   if(!h->recip_valid)
177   {
178     h->recip_valid = 1;
179     
180     if (mutt_addr_is_user (env->from))
181       h->recipient = 4;
182     else if (user_in_addr (env->to))
183     {
184       if (env->to->next || env->cc)
185         h->recipient = 2; /* non-unique recipient */
186       else
187         h->recipient = 1; /* unique recipient */
188     }
189     else if (user_in_addr (env->cc))
190       h->recipient = 3;
191     else if (check_for_mailing_list (env->to, NULL, NULL, 0))
192       h->recipient = 5;
193     else if (check_for_mailing_list (env->cc, NULL, NULL, 0))
194       h->recipient = 5;
195     else
196       h->recipient = 0;
197   }
198   
199   return h->recipient;
200 }
201
202 /* %a = address of author
203  * %A = reply-to address (if present; otherwise: address of author
204  * %b = filename of the originating folder
205  * %B = the list to which the letter was sent
206  * %c = size of message in bytes
207  * %C = current message number
208  * %d = date and time of message using $date_format and sender's timezone
209  * %D = date and time of message using $date_format and local timezone
210  * %e = current message number in thread
211  * %E = number of messages in current thread
212  * %f = entire from line
213  * %F = like %n, unless from self
214  * %i = message-id
215  * %l = number of lines in the message
216  * %L = like %F, except `lists' are displayed first
217  * %m = number of messages in the mailbox
218  * %n = name of author
219  * %N = score
220  * %O = like %L, except using address instead of name
221  * %P = progress indicator for builtin pager
222  * %s = subject
223  * %S = short message status (e.g., N/O/D/!/r/-)
224  * %t = `to:' field (recipients)
225  * %T = $to_chars
226  * %u = user (login) name of author
227  * %v = first name of author, unless from self
228  * %X = number of MIME attachments
229  * %y = `x-label:' field (if present)
230  * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
231  * %Z = status flags    */
232
233 static const char *
234 hdr_format_str (char *dest,
235                 size_t destlen,
236                 size_t col,
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[LONG_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     case 'H':
449       /* (Hormel) spam score */
450       if (optional)
451         optional = hdr->env->spam ? 1 : 0;
452
453        if (hdr->env->spam)
454          mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
455        else
456          mutt_format_s (dest, destlen, prefix, "");
457
458       break;
459
460     case 'i':
461       mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
462       break;
463
464     case 'l':
465       if (!optional)
466       {
467         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
468         snprintf (dest, destlen, fmt, (int) hdr->lines);
469       }
470       else if (hdr->lines <= 0)
471         optional = 0;
472       break;
473
474     case 'L':
475       if (!optional)
476       {
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       {
483         optional = 0;
484       }
485       break;
486
487     case 'm':
488       if(ctx)
489       {
490         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
491         snprintf (dest, destlen, fmt, ctx->msgcount);
492       }
493       else
494         strfcpy(dest, "(null)", destlen);
495       break;
496
497     case 'n':
498       mutt_format_s (dest, destlen, prefix, mutt_get_name (hdr->env->from));
499       break;
500
501     case 'N':
502       if (!optional)
503       {
504         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
505         snprintf (dest, destlen, fmt, hdr->score);
506       }
507       else
508       {
509         if (hdr->score == 0)
510           optional = 0;
511       }
512       break;
513
514     case 'O':
515       if (!optional)
516       {
517         make_from_addr (hdr->env, buf2, sizeof (buf2), 1);
518         if (!option (OPTSAVEADDRESS) && (p = strpbrk (buf2, "%@")))
519           *p = 0;
520         mutt_format_s (dest, destlen, prefix, buf2);
521       }
522       else if (!check_for_mailing_list_addr (hdr->env->to, NULL, 0) &&
523                !check_for_mailing_list_addr (hdr->env->cc, NULL, 0))
524       {
525         optional = 0;
526       }
527       break;
528
529     case 'M':
530       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
531       if (!optional)
532       {
533         if (threads && is_index && hdr->collapsed && hdr->num_hidden > 1)
534           snprintf (dest, destlen, fmt, hdr->num_hidden);
535         else if (is_index && threads)
536           mutt_format_s (dest, destlen, prefix, " ");
537         else
538           *dest = '\0';
539       }
540       else
541       {
542         if (!(threads && is_index && hdr->collapsed && hdr->num_hidden > 1))
543           optional = 0;
544       }
545       break;
546
547     case 'P':
548       strfcpy(dest, NONULL(hfi->pager_progress), destlen);
549       break;
550
551     case 's':
552       
553       if (flags & M_FORMAT_TREE && !hdr->collapsed)
554       {
555         if (flags & M_FORMAT_FORCESUBJ)
556         {
557           mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject));
558           snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
559           mutt_format_s_tree (dest, destlen, prefix, buf2);
560         }
561         else
562           mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
563       }
564       else
565         mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject));
566       break;
567
568     case 'S':
569       if (hdr->deleted)
570         ch = 'D';
571       else if (hdr->attach_del)
572         ch = 'd';
573       else if (hdr->tagged)
574         ch = '*';
575       else if (hdr->flagged)
576         ch = '!';
577       else if (hdr->replied)
578         ch = 'r';
579       else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
580         ch = '-';
581       else if (hdr->old)
582         ch = 'O';
583       else
584         ch = 'N';
585
586       /* FOO - this is probably unsafe, but we are not likely to have such
587          a short string passed into this routine */
588       *dest = ch;
589       *(dest + 1) = 0;
590       break;
591
592     case 't':
593       buf2[0] = 0;
594       if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
595           !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2)))
596       {
597         if (hdr->env->to)
598           snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
599         else if (hdr->env->cc)
600           snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
601       }
602       mutt_format_s (dest, destlen, prefix, buf2);
603       break;
604
605     case 'T':
606       snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
607       snprintf (dest, destlen, fmt,
608                 (Tochars && ((i = mutt_user_is_recipient (hdr))) < mutt_strlen (Tochars)) ? Tochars[i] : ' ');
609       break;
610
611     case 'u':
612       if (hdr->env->from && hdr->env->from->mailbox)
613       {
614         strfcpy (buf2, mutt_addr_for_display (hdr->env->from), sizeof (buf2));
615         if ((p = strpbrk (buf2, "%@")))
616           *p = 0;
617       }
618       else
619         buf2[0] = 0;
620       mutt_format_s (dest, destlen, prefix, buf2);
621       break;
622
623     case 'v':
624       if (mutt_addr_is_user (hdr->env->from)) 
625       {
626         if (hdr->env->to)
627           mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->to));
628         else if (hdr->env->cc)
629           mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->cc));
630         else
631           *buf2 = 0;
632       }
633       else
634         mutt_format_s (buf2, sizeof (buf2), prefix, mutt_get_name (hdr->env->from));
635       if ((p = strpbrk (buf2, " %@")))
636         *p = 0;
637       mutt_format_s (dest, destlen, prefix, buf2);
638       break;
639
640     case 'Z':
641     
642       ch = ' ';
643
644       if (WithCrypto && hdr->security & GOODSIGN)
645         ch = 'S';
646       else if (WithCrypto && hdr->security & ENCRYPT)
647         ch = 'P';
648       else if (WithCrypto && hdr->security & SIGN)
649         ch = 's';
650       else if ((WithCrypto & APPLICATION_PGP) && hdr->security & PGPKEY)
651         ch = 'K';
652
653       snprintf (buf2, sizeof (buf2),
654                 "%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' : 
655                 ((hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
656                 ? (hdr->replied ? 'r' : ' ') : (hdr->old ? 'O' : 'N')))),
657                 hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
658                 hdr->tagged ? '*' :
659                 (hdr->flagged ? '!' :
660                  (Tochars && ((i = mutt_user_is_recipient (hdr)) < mutt_strlen (Tochars)) ? Tochars[i] : ' ')));
661       mutt_format_s (dest, destlen, prefix, buf2);
662       break;
663
664     case 'X':
665       {
666         int count = mutt_count_body_parts (ctx, hdr);
667
668         /* The recursion allows messages without depth to return 0. */
669         if (optional)
670           optional = count != 0;
671
672         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
673         snprintf (dest, destlen, fmt, count);
674       }
675       break;
676
677      case 'y':
678        if (optional)
679          optional = hdr->env->x_label ? 1 : 0;
680
681        mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
682        break;
683  
684     case 'Y':
685       if (hdr->env->x_label)
686       {
687         i = 1;  /* reduce reuse recycle */
688         htmp = NULL;
689         if (flags & M_FORMAT_TREE
690             && (hdr->thread->prev && hdr->thread->prev->message
691                 && hdr->thread->prev->message->env->x_label))
692           htmp = hdr->thread->prev->message;
693         else if (flags & M_FORMAT_TREE
694                  && (hdr->thread->parent && hdr->thread->parent->message
695                      && hdr->thread->parent->message->env->x_label))
696           htmp = hdr->thread->parent->message;
697         if (htmp && mutt_strcasecmp (hdr->env->x_label,
698                                      htmp->env->x_label) == 0)
699           i = 0;
700       }
701       else
702         i = 0;
703
704       if (optional)
705         optional = i;
706
707       if (i)
708         mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->x_label));
709       else
710         mutt_format_s (dest, destlen, prefix, "");
711
712       break;
713
714     default:
715       snprintf (dest, destlen, "%%%s%c", prefix, op);
716       break;
717   }
718
719   if (optional)
720     mutt_FormatString (dest, destlen, col, ifstring, hdr_format_str, (unsigned long) hfi, flags);
721   else if (flags & M_FORMAT_OPTIONAL)
722     mutt_FormatString (dest, destlen, col, elsestring, hdr_format_str, (unsigned long) hfi, flags);
723
724   return (src);
725 #undef THREAD_NEW
726 #undef THREAD_OLD
727 }
728
729 void
730 _mutt_make_string (char *dest, size_t destlen, const char *s, CONTEXT *ctx, HEADER *hdr, format_flag flags)
731 {
732   struct hdr_format_info hfi;
733
734   hfi.hdr = hdr;
735   hfi.ctx = ctx;
736   hfi.pager_progress = 0;
737
738   mutt_FormatString (dest, destlen, 0, s, hdr_format_str, (unsigned long) &hfi, flags);
739 }
740
741 void
742 mutt_make_string_info (char *dst, size_t dstlen, const char *s, struct hdr_format_info *hfi, format_flag flags)
743 {
744   mutt_FormatString (dst, dstlen, 0, s, hdr_format_str, (unsigned long) hfi, flags);
745 }