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