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