Pass int instead of char to iscntrl() and isspace() because that's what they require...
[mmh] / sbr / fmt_scan.c
1
2 /*
3  * fmt_scan.c -- format string interpretation
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  *
9  * This is the engine that processes the format instructions created by
10  * fmt_compile (found in fmt_compile.c).
11  */
12
13 #include <h/mh.h>
14 #include <h/addrsbr.h>
15 #include <h/fmt_scan.h>
16 #include <h/tws.h>
17 #include <h/fmt_compile.h>
18
19 #ifdef HAVE_SYS_TIME_H
20 # include <sys/time.h>
21 #endif
22 #include <time.h>
23 #ifdef MULTIBYTE_SUPPORT
24 #  include <wctype.h>
25 #  include <wchar.h>
26 #endif
27
28 extern char *formataddr ();     /* hook for custom address formatting */
29 extern char *concataddr ();     /* address formatting but allowing duplicates */
30
31 #ifdef LBL
32 struct msgs *fmt_current_folder; /* current folder (set by main program) */
33 #endif
34
35 extern int fmt_norm;            /* defined in sbr/fmt_def.c = AD_NAME */
36 struct mailname fmt_mnull;
37
38 /*
39  * static prototypes
40  */
41 static int match (char *, char *);
42 static char *get_x400_friendly (char *, char *, int);
43 static int get_x400_comp (char *, char *, char *, int);
44
45
46 /*
47  * test if string "sub" appears anywhere in
48  * string "str" (case insensitive).
49  */
50
51 static int
52 match (char *str, char *sub)
53 {
54     int c1, c2;
55     char *s1, *s2;
56
57 #ifdef LOCALE
58     while ((c1 = *sub)) {
59         c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
60         while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
61             ;
62         if (! c2)
63             return 0;
64         s1 = sub + 1; s2 = str;
65         while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
66             ;
67         if (! c1)
68             return 1;
69     }
70 #else
71     while ((c1 = *sub)) {
72         while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
73             ;
74         if (! c2)
75             return 0;
76         s1 = sub + 1; s2 = str;
77         while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
78             ;
79         if (! c1)
80             return 1;
81     }
82 #endif
83     return 1;
84 }
85
86 /*
87  * copy a number to the destination subject to a maximum width
88  */
89 static void
90 cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
91     int i, c;
92     char *sp;
93     char *cp = *dest;
94     char *ep = cp + n;
95
96     if (cp + wid < ep) {
97         if ((i = (num)) < 0)
98             i = -(num);
99         if ((c = (wid)) < 0)
100             c = -c;
101         sp = cp + c;
102         do {
103             *--sp = (i % 10) + '0';
104             i /= 10;
105         } while (i > 0 && sp > cp);
106         if (i > 0)
107             *sp = '?';
108         else if ((num) < 0 && sp > cp)
109             *--sp = '-';
110         while (sp > cp)
111             *--sp = fill;
112         cp += c;
113     }
114     *dest = cp;
115 }
116
117 /*
118  * copy string from str to dest padding with the fill character to a size
119  * of wid characters. if wid is negative, the string is right aligned
120  * no more than n bytes are copied
121  */
122 static void
123 cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
124     int remaining;     /* remaining output width available */
125     int c, ljust;
126     int end;           /* number of input bytes remaining in str */
127 #ifdef MULTIBYTE_SUPPORT
128     int char_len;      /* bytes in current character */
129     int w;
130     wchar_t wide_char;
131 #endif
132     char *sp;          /* current position in source string */
133     char *cp = *dest;  /* current position in destination string */
134     char *ep = cp + n; /* end of destination buffer */
135     int prevCtrl = 1;
136
137     /* get alignment */
138     ljust = 0;
139     if ((remaining = (wid)) < 0) {
140         remaining = -remaining;
141         ljust++;
142     }
143     if ((sp = (str))) {
144         mbtowc(NULL, NULL, 0); /* reset shift state */
145         end = strlen(str);
146         while (*sp && remaining > 0 && end > 0) {
147 #ifdef MULTIBYTE_SUPPORT
148             char_len = mbtowc(&wide_char, sp, end);
149             if (char_len <= 0 || (cp + char_len > ep))
150                 break;
151
152             end -= char_len;
153
154             if (iswcntrl(wide_char) || iswspace(wide_char)) {
155                 sp += char_len;
156 #else
157             end--;
158             /* isnctrl(), etc., take an int argument.  Cygwin's ctype.h
159                intentionally warns if they are passed a char. */
160             int c = *sp;
161             if (iscntrl(c) || isspace(c)) {
162                 sp++;
163 #endif
164                 if (!prevCtrl) {
165                     *cp++ = ' ';
166                     remaining--;
167                 }
168
169                 prevCtrl = 1;
170                 continue;
171             }
172             prevCtrl = 0;
173
174 #ifdef MULTIBYTE_SUPPORT
175             w = wcwidth(wide_char);
176             if (w >= 0 && remaining >= w) {
177                 strncpy(cp, sp, char_len);
178                 cp += char_len;
179                 remaining -= w;
180             }
181             sp += char_len;
182 #else
183             *cp++ = *sp++;
184             remaining--;
185 #endif
186         }
187     }
188
189     if (ljust) {
190         if (cp + remaining > ep)
191             remaining = ep - cp;
192         ep = cp + remaining;
193         if (remaining > 0) {
194             /* copy string to the right */
195             while (--cp >= *dest)
196                 *(cp + remaining) = *cp;
197             /* add padding at the beginning */
198             cp += remaining;
199             for (c=remaining; c>0; c--)
200                 *cp-- = fill;
201         }
202         *dest = ep;
203     } else {
204         /* pad remaining space */
205         while (remaining-- > 0 && cp < ep)
206                 *cp++ = fill;
207         *dest = cp;
208     }
209 }
210
211 static void
212 cpstripped (char **start, char *end, char *str)
213 {
214     int c;
215     char *s = str;
216
217     if (!s)
218         return;
219
220     /* skip any initial control characters or spaces */
221     while ((c = (unsigned char) *s) &&
222 #ifdef LOCALE
223             (iscntrl(c) || isspace(c)))
224 #else
225             (c <= 32))
226 #endif
227         s++;
228
229     /* compact repeated control characters and spaces into a single space */
230     while((c = (unsigned char) *s++) && *start < end)
231         if (!iscntrl(c) && !isspace(c))
232             *(*start)++ = c;
233         else {
234             while ((c = (unsigned char) *s) &&
235 #ifdef LOCALE
236                     (iscntrl(c) || isspace(c)))
237 #else
238                     (c <= 32))
239 #endif
240                 s++;
241             *(*start)++ = ' ';
242         }
243 }
244
245 static char *lmonth[] = { "January",  "February","March",   "April",
246                           "May",      "June",    "July",    "August",
247                           "September","October", "November","December" };
248
249 static char *
250 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
251 {
252     char given[BUFSIZ], surname[BUFSIZ];
253
254     if (mbox == NULL)
255         return NULL;
256     if (*mbox == '"')
257         mbox++;
258     if (*mbox != '/')
259         return NULL;
260
261     if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
262         for (mbox = buffer; (mbox = strchr(mbox, '.')); )
263             *mbox++ = ' ';
264
265         return buffer;
266     }
267
268     if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
269         return NULL;
270
271     if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
272         snprintf (buffer, buffer_len, "%s %s", given, surname);
273     else
274         snprintf (buffer, buffer_len, "%s", surname);
275
276     return buffer;
277 }
278
279 static int
280 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
281 {
282     int idx;
283     char *cp;
284
285     if ((idx = stringdex (key, mbox)) < 0
286             || !(cp = strchr(mbox += idx + strlen (key), '/')))
287         return 0;
288
289     snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
290     return 1;
291 }
292
293 struct format *
294 fmt_scan (struct format *format, char *scanl, int width, int *dat)
295 {
296     char *cp, *ep;
297     unsigned char *sp;
298     char *savestr = NULL;
299     unsigned char *str = NULL;
300     char buffer[BUFSIZ], buffer2[BUFSIZ];
301     int i, c, ljust, n;
302     int value = 0;
303     time_t t;
304     struct format *fmt;
305     struct comp *comp;
306     struct tws *tws;
307     struct mailname *mn;
308
309     cp = scanl;
310     ep = scanl + width - 1;
311
312     for (fmt = format; fmt->f_type != FT_DONE; fmt++)
313         switch (fmt->f_type) {
314         case FT_PARSEADDR:
315         case FT_PARSEDATE:
316             fmt->f_comp->c_flags &= ~CF_PARSED;
317             break;
318         }
319
320     fmt = format;
321
322     while (cp < ep) {
323         switch (fmt->f_type) {
324
325         case FT_COMP:
326             cpstripped (&cp, ep, fmt->f_comp->c_text);
327             break;
328         case FT_COMPF:
329             cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
330             break;
331
332         case FT_LIT:
333             sp = fmt->f_text;
334             while( (c = *sp++) && cp < ep)
335                 *cp++ = c;
336             break;
337         case FT_LITF:
338             sp = fmt->f_text;
339             ljust = 0;
340             i = fmt->f_width;
341             if (i < 0) {
342                 i = -i;
343                 ljust++;                /* XXX should do something with this */
344             }
345             while( (c = *sp++) && --i >= 0 && cp < ep)
346                 *cp++ = c;
347             while( --i >= 0 && cp < ep)
348                 *cp++ = fmt->f_fill;
349             break;
350
351         case FT_STR:
352             cpstripped (&cp, ep, str);
353             break;
354         case FT_STRF:
355             cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
356             break;
357         case FT_STRLIT:
358             sp = str;
359             while ((c = *sp++) && cp < ep)
360                 *cp++ = c;
361             break;
362         case FT_STRFW:
363             adios (NULL, "internal error (FT_STRFW)");
364
365         case FT_NUM:
366             n = snprintf(cp, ep - cp + 1, "%d", value);
367             if (n >= 0) {
368                 if (n >= ep - cp) {
369                     cp = ep;
370                 } else
371                     cp += n;
372             }
373             break;
374         case FT_NUMF:
375             cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
376             break;
377
378         case FT_CHAR:
379             *cp++ = fmt->f_char;
380             break;
381
382         case FT_DONE:
383             goto finished;
384
385         case FT_IF_S:
386             if (!(value = (str && *str))) {
387                 fmt += fmt->f_skip;
388                 continue;
389             }
390             break;
391
392         case FT_IF_S_NULL:
393             if (!(value = (str == NULL || *str == 0))) {
394                 fmt += fmt->f_skip;
395                 continue;
396             }
397             break;
398
399         case FT_IF_V_EQ:
400             if (value != fmt->f_value) {
401                 fmt += fmt->f_skip;
402                 continue;
403             }
404             break;
405
406         case FT_IF_V_NE:
407             if (value == fmt->f_value) {
408                 fmt += fmt->f_skip;
409                 continue;
410             }
411             break;
412
413         case FT_IF_V_GT:
414             if (value <= fmt->f_value) {
415                 fmt += fmt->f_skip;
416                 continue;
417             }
418             break;
419
420         case FT_IF_MATCH:
421             if (!(value = (str && match (str, fmt->f_text)))) {
422                 fmt += fmt->f_skip;
423                 continue;
424             }
425             break;
426
427         case FT_V_MATCH:
428             if (str)
429                 value = match (str, fmt->f_text);
430             else
431                 value = 0;
432             break;
433
434         case FT_IF_AMATCH:
435             if (!(value = (str && uprf (str, fmt->f_text)))) {
436                 fmt += fmt->f_skip;
437                 continue;
438             }
439             break;
440
441         case FT_V_AMATCH:
442             value = uprf (str, fmt->f_text);
443             break;
444
445         case FT_S_NONNULL:
446             value = (str != NULL && *str != 0);
447             break;
448
449         case FT_S_NULL:
450             value = (str == NULL || *str == 0);
451             break;
452
453         case FT_V_EQ:
454             value = (fmt->f_value == value);
455             break;
456
457         case FT_V_NE:
458             value = (fmt->f_value != value);
459             break;
460
461         case FT_V_GT:
462             value = (fmt->f_value > value);
463             break;
464
465         case FT_GOTO:
466             fmt += fmt->f_skip;
467             continue;
468
469         case FT_NOP:
470             break;
471
472         case FT_LS_COMP:
473             str = fmt->f_comp->c_text;
474             break;
475         case FT_LS_LIT:
476             str = fmt->f_text;
477             break;
478         case FT_LS_GETENV:
479             if (!(str = getenv (fmt->f_text)))
480                 str = "";
481             break;
482         case FT_LS_CFIND:
483             if (!(str = context_find (fmt->f_text)))
484                 str = "";
485             break;
486
487         case FT_LS_DECODECOMP:
488             if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
489                 str = buffer2;
490             else
491                 str = fmt->f_comp->c_text;
492             break;
493
494         case FT_LS_DECODE:
495             if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
496                 str = buffer2;
497             break;
498
499         case FT_LS_TRIM:
500             if (str) {
501                     unsigned char *xp;
502
503                     strncpy(buffer, str, sizeof(buffer));
504                     buffer[sizeof(buffer)-1] = '\0';
505                     str = buffer;
506                     while (isspace(*str))
507                             str++;
508                     ljust = 0;
509                     if ((i = fmt->f_width) < 0) {
510                             i = -i;
511                             ljust++;
512                     }
513
514                     if (!ljust && i > 0 && (int) strlen(str) > i)
515                             str[i] = '\0';
516                     xp = str;
517                     xp += strlen(str) - 1;
518                     while (xp > str && isspace(*xp))
519                             *xp-- = '\0';
520                     if (ljust && i > 0 && (int) strlen(str) > i)
521                         str += strlen(str) - i;
522             }
523             break;
524
525         case FT_LV_COMPFLAG:
526             value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
527             break;
528         case FT_LV_COMP:
529             value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
530             break;
531         case FT_LV_LIT:
532             value = fmt->f_value;
533             break;
534         case FT_LV_DAT:
535             value = dat[fmt->f_value];
536             break;
537         case FT_LV_STRLEN:
538             if (str != NULL)
539                     value = strlen(str);
540             else
541                     value = 0;
542             break;
543         case FT_LV_CHAR_LEFT:
544             value = width - (cp - scanl);
545             break;
546         case FT_LV_PLUS_L:
547             value += fmt->f_value;
548             break;
549         case FT_LV_MINUS_L:
550             value = fmt->f_value - value;
551             break;
552         case FT_LV_DIVIDE_L:
553             if (fmt->f_value)
554                 value = value / fmt->f_value;
555             else
556                 value = 0;
557             break;
558         case FT_LV_MODULO_L:
559             if (fmt->f_value)
560                 value = value % fmt->f_value;
561             else
562                 value = 0;
563             break;
564         case FT_SAVESTR:
565             savestr = str;
566             break;
567
568         case FT_LV_SEC:
569             value = fmt->f_comp->c_tws->tw_sec;
570             break;
571         case FT_LV_MIN:
572             value = fmt->f_comp->c_tws->tw_min;
573             break;
574         case FT_LV_HOUR:
575             value = fmt->f_comp->c_tws->tw_hour;
576             break;
577         case FT_LV_MDAY:
578             value = fmt->f_comp->c_tws->tw_mday;
579             break;
580         case FT_LV_MON:
581             value = fmt->f_comp->c_tws->tw_mon + 1;
582             break;
583         case FT_LS_MONTH:
584             str = tw_moty[fmt->f_comp->c_tws->tw_mon];
585             break;
586         case FT_LS_LMONTH:
587             str = lmonth[fmt->f_comp->c_tws->tw_mon];
588             break;
589         case FT_LS_ZONE:
590             str = dtwszone (fmt->f_comp->c_tws);
591             break;
592         case FT_LV_YEAR:
593             value = fmt->f_comp->c_tws->tw_year;
594             break;
595         case FT_LV_WDAY:
596             if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
597                 set_dotw (tws);
598             value = tws->tw_wday;
599             break;
600         case FT_LS_DAY:
601             if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
602                 set_dotw (tws);
603             str = tw_dotw[tws->tw_wday];
604             break;
605         case FT_LS_WEEKDAY:
606             if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
607                 set_dotw (tws);
608             str = tw_ldotw[tws->tw_wday];
609             break;
610         case FT_LV_YDAY:
611             value = fmt->f_comp->c_tws->tw_yday;
612             break;
613         case FT_LV_ZONE:
614             value = fmt->f_comp->c_tws->tw_zone;
615             break;
616         case FT_LV_CLOCK:
617             if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
618                 value = dmktime(fmt->f_comp->c_tws);
619             break;
620         case FT_LV_RCLOCK:
621             if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
622                 value = dmktime(fmt->f_comp->c_tws);
623             value = time((time_t *) 0) - value;
624             break;
625         case FT_LV_DAYF:
626             if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
627                 set_dotw (tws);
628             switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
629                 case TW_SEXP:
630                     value = 1; break;
631                 case TW_SIMP:
632                     value = 0; break;
633                 default:
634                     value = -1; break;
635             }
636         case FT_LV_ZONEF:
637             if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
638                     value = 1;
639             else
640                     value = -1;
641             break;
642         case FT_LV_DST:
643             value = fmt->f_comp->c_tws->tw_flags & TW_DST;
644             break;
645         case FT_LS_822DATE:
646             str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
647             break;
648         case FT_LS_PRETTY:
649             str = dasctime (fmt->f_comp->c_tws, TW_NULL);
650             break;
651
652         case FT_LS_PERS:
653             str = fmt->f_comp->c_mn->m_pers;
654             break;
655         case FT_LS_MBOX:
656             str = fmt->f_comp->c_mn->m_mbox;
657             break;
658         case FT_LS_HOST:
659             str = fmt->f_comp->c_mn->m_host;
660             break;
661         case FT_LS_PATH:
662             str = fmt->f_comp->c_mn->m_path;
663             break;
664         case FT_LS_GNAME:
665             str = fmt->f_comp->c_mn->m_gname;
666             break;
667         case FT_LS_NOTE:
668             str = fmt->f_comp->c_mn->m_note;
669             break;
670         case FT_LS_822ADDR:
671             str = adrformat( fmt->f_comp->c_mn );
672             break;
673         case FT_LV_HOSTTYPE:
674             value = fmt->f_comp->c_mn->m_type;
675             break;
676         case FT_LV_INGRPF:
677             value = fmt->f_comp->c_mn->m_ingrp;
678             break;
679         case FT_LV_NOHOSTF:
680             value = fmt->f_comp->c_mn->m_nohost;
681             break;
682         case FT_LS_ADDR:
683         case FT_LS_FRIENDLY:
684             if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
685                 str = fmt->f_comp->c_text;
686                 break;
687             }
688             if (fmt->f_type == FT_LS_ADDR)
689                 goto unfriendly;
690             if ((str = mn->m_pers) == NULL) {
691                 if ((str = mn->m_note)) {
692                     strncpy (buffer, str, sizeof(buffer));
693                     buffer[sizeof(buffer)-1] = '\0';
694                     str = buffer;
695                     if (*str == '(')
696                         str++;
697                     sp = str + strlen(str) - 1;
698                     if (*sp == ')') {
699                         *sp-- = '\0';
700                         while (sp >= str)
701                             if (*sp == ' ')
702                                 *sp-- = '\0';
703                             else
704                                 break;
705                     }
706                 } else if (!(str = get_x400_friendly (mn->m_mbox,
707                                 buffer, sizeof(buffer)))) {
708         unfriendly: ;
709                   switch (mn->m_type) {
710                     case LOCALHOST:
711                         str = mn->m_mbox;
712                         break;
713                     case UUCPHOST:
714                         snprintf (buffer, sizeof(buffer), "%s!%s",
715                                 mn->m_host, mn->m_mbox);
716                         str = buffer;
717                         break;
718                     default:
719                         if (mn->m_mbox) {
720                             snprintf (buffer, sizeof(buffer), "%s@%s",
721                                 mn->m_mbox, mn->m_host);
722                             str= buffer;
723                         }
724                         else
725                             str = mn->m_text;
726                         break;
727                   }
728                 }
729             }
730             break;
731
732
733                 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
734         case FT_LS_UNQUOTE:
735             if (str) {          
736                 int m;
737                 strncpy(buffer, str, sizeof(buffer));
738                 /* strncpy doesn't NUL-terminate if it fills the buffer */
739                 buffer[sizeof(buffer)-1] = '\0';
740                 str = buffer;
741         
742                 /* we will parse from buffer to buffer2 */
743                 n = 0; /* n is the input position in str */
744                 m = 0; /* m is the ouput position in buffer2 */
745
746                 while ( str[n] != '\0') {
747                     switch ( str[n] ) {
748                         case '\\':
749                             n++;
750                             if ( str[n] != '\0')
751                                 buffer2[m++] = str[n++];
752                             break;
753                         case '"':
754                             n++;
755                             break;
756                         default:
757                             buffer2[m++] = str[n++];
758                             break;
759                         }
760                 }
761                 buffer2[m] = '\0';
762                 str = buffer2;
763             }
764             break;
765
766         case FT_LOCALDATE:
767             comp = fmt->f_comp;
768             if ((t = comp->c_tws->tw_clock) == 0)
769                 t = dmktime(comp->c_tws);
770             tws = dlocaltime(&t);
771             *comp->c_tws = *tws;
772             break;
773
774         case FT_GMTDATE:
775             comp = fmt->f_comp;
776             if ((t = comp->c_tws->tw_clock) == 0)
777                 t = dmktime(comp->c_tws);
778             tws = dgmtime(&t);
779             *comp->c_tws = *tws;
780             break;
781
782         case FT_PARSEDATE:
783             comp = fmt->f_comp;
784             if (comp->c_flags & CF_PARSED)
785                 break;
786             if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
787                 *comp->c_tws = *tws;
788                 comp->c_flags &= ~CF_TRUE;
789             } else if ((comp->c_flags & CF_DATEFAB) == 0) {
790                 memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
791                 comp->c_flags = CF_TRUE;
792             }
793             comp->c_flags |= CF_PARSED;
794             break;
795
796         case FT_FORMATADDR:
797             /* hook for custom address list formatting (see replsbr.c) */
798             str = formataddr (savestr, str);
799             break;
800
801         case FT_CONCATADDR:
802             /* The same as formataddr, but doesn't do duplicate suppression */
803             str = concataddr (savestr, str);
804             break;
805
806         case FT_PUTADDR:
807             /* output the str register as an address component,
808              * splitting it into multiple lines if necessary.  The
809              * value reg. contains the max line length.  The lit.
810              * field may contain a string to prepend to the result
811              * (e.g., "To: ")
812              */
813             {
814             unsigned char *lp;
815             char *lastb;
816             int indent, wid, len;
817
818             lp = str;
819             wid = value;
820             len = strlen (str);
821             sp = fmt->f_text;
822             indent = strlen (sp);
823             wid -= indent;
824             while( (c = *sp++) && cp < ep)
825                 *cp++ = c;
826             while (len > wid) {
827                 /* try to break at a comma; failing that, break at a
828                  * space.
829                  */
830                 lastb = 0; sp = lp + wid;
831                 while (sp > lp && (c = *--sp) != ',') {
832                     if (! lastb && isspace(c))
833                         lastb = sp - 1;
834                 }
835                 if (sp == lp) {
836                     if (! (sp = lastb)) {
837                         sp = lp + wid - 1;
838                         while (*sp && *sp != ',' && !isspace(*sp))
839                             sp++;
840                         if (*sp != ',')
841                             sp--;
842                     }
843                 }
844                 len -= sp - lp + 1;
845                 while (cp < ep && lp <= sp)
846                     *cp++ = *lp++;
847                 while (isspace(*lp))
848                     lp++, len--;
849                 if (*lp) {
850                     if (cp < ep)
851                         *cp++ = '\n';
852                     for (i=indent; cp < ep && i > 0; i--)
853                         *cp++ = ' ';
854                 }
855             }
856             cpstripped (&cp, ep, lp);
857             }
858             break;
859
860         case FT_PARSEADDR:
861             comp = fmt->f_comp;
862             if (comp->c_flags & CF_PARSED)
863                 break;
864             if (comp->c_mn != &fmt_mnull)
865                 mnfree (comp->c_mn);
866             if ((sp = comp->c_text) && (sp = getname(sp)) &&
867                 (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
868                 comp->c_mn = mn;
869                 while (getname(""))
870                     ;
871                 comp->c_flags |= CF_PARSED;
872             } else {
873                 while (getname(""))             /* XXX */
874                     ;
875                 comp->c_mn = &fmt_mnull;
876             }
877             break;
878
879         case FT_MYMBOX:
880             /*
881              * if there's no component, we say true.  Otherwise we
882              * say "true" only if we can parse the address and it
883              * matches one of our addresses.
884              */
885             comp = fmt->f_comp;
886             if (comp->c_mn != &fmt_mnull)
887                 mnfree (comp->c_mn);
888             if ((sp = comp->c_text) && (sp = getname(sp)) &&
889                 (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
890                 comp->c_mn = mn;
891                 if (ismymbox(mn))
892                     comp->c_flags |= CF_TRUE;
893                 else
894                     comp->c_flags &= ~CF_TRUE;
895                 while ((sp = getname(sp)))
896                     if ((comp->c_flags & CF_TRUE) == 0 &&
897                         (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
898                         if (ismymbox(mn))
899                             comp->c_flags |= CF_TRUE;
900             } else {
901                 while (getname(""))             /* XXX */
902                     ;
903                 if (comp->c_text == 0)
904                     comp->c_flags |= CF_TRUE;
905                 else
906                     comp->c_flags &= ~CF_TRUE;
907                 comp->c_mn = &fmt_mnull;
908             }
909             break;
910
911         case FT_ADDTOSEQ:
912 #ifdef LBL
913             /* If we're working on a folder (as opposed to a file), add the
914              * current msg to sequence given in literal field.  Don't
915              * disturb string or value registers.
916              */
917             if (fmt_current_folder)
918                     seq_addmsg(fmt_current_folder, fmt->f_text, dat[0], -1);
919 #endif
920             break;
921         }
922         fmt++;
923     }
924 #ifndef JLR
925     finished:;
926     if (cp[-1] != '\n')
927         *cp++ = '\n';
928     *cp   = 0;
929     return ((struct format *)0);
930 #else /* JLR */
931     if (cp[-1] != '\n')
932         *cp++ = '\n';
933     while (fmt->f_type != FT_DONE)
934         fmt++;
935
936     finished:;
937     *cp = '\0';
938     return (fmt->f_value ? ++fmt : (struct format *) 0);
939
940 #endif /* JLR */
941 }