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