cpstripped: Add braces to make code more legible.
[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         }
243 #endif
244 }
245
246 static char *lmonth[] = {
247         "January",   "February", "March",    "April",
248         "May",       "June",     "July",     "August",
249         "September", "October",  "November", "December"
250 };
251
252 static char *
253 get_x400_friendly(char *mbox, char *buffer, int buffer_len)
254 {
255         char given[BUFSIZ], surname[BUFSIZ];
256
257         if (mbox == NULL)
258                 return NULL;
259         if (*mbox == '"')
260                 mbox++;
261         if (*mbox != '/')
262                 return NULL;
263
264         if (get_x400_comp(mbox, "/PN=", buffer, buffer_len)) {
265                 for (mbox = buffer; (mbox = strchr(mbox, '.')); )
266                         *mbox++ = ' ';
267
268                 return buffer;
269         }
270
271         if (!get_x400_comp(mbox, "/S=", surname, sizeof(surname)))
272                 return NULL;
273
274         if (get_x400_comp(mbox, "/G=", given, sizeof(given)))
275                 snprintf(buffer, buffer_len, "%s %s", given, surname);
276         else
277                 snprintf(buffer, buffer_len, "%s", surname);
278
279         return buffer;
280 }
281
282 static int
283 get_x400_comp(char *mbox, char *key, char *buffer, int buffer_len)
284 {
285         int idx;
286         char *cp;
287
288         if ((idx = stringdex(key, mbox)) < 0
289                 || !(cp = strchr(mbox += idx + strlen(key), '/')))
290                 return 0;
291
292         snprintf(buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
293         return 1;
294 }
295
296 static char *
297 fmt_trim(char *str, int width)
298 {
299         char *xp;
300         int ljust, i;
301         char buffer[BUFSIZ];
302
303         strncpy(buffer, str, sizeof(buffer));
304         buffer[sizeof(buffer)-1] = '\0';
305         str = buffer;
306         while (isspace(*str)) {
307                 str++;
308         }
309         ljust = 0;
310         if ((i = width) < 0) {
311                 i = -i;
312                 ljust++;
313         }
314
315         if (!ljust && i > 0 && (int)strlen(str) > i) {
316                 str[i] = '\0';
317         }
318         xp = str;
319         xp += strlen(str) - 1;
320         while (xp > str && isspace(*xp)) {
321                 *xp-- = '\0';
322         }
323         if (ljust && i > 0 && (int)strlen(str) > i) {
324                 str += strlen(str) - i;
325         }
326         return str;
327 }
328
329 struct format *
330 fmt_scan(struct format *format, char *scanl, int width, int *dat)
331 {
332         char *cp, *ep;
333         unsigned char *sp;
334         char *savestr = NULL;
335         unsigned char *str = NULL;
336         char buffer[BUFSIZ], buffer2[BUFSIZ];
337         int i, c, ljust, n;
338         int value = 0;
339         time_t t;
340         struct format *fmt;
341         struct comp *comp;
342         struct tws *tws;
343         struct mailname *mn;
344
345         cp = scanl;
346         ep = scanl + width - 1;
347
348         for (fmt = format; fmt->f_type != FT_DONE; fmt++)
349                 switch (fmt->f_type) {
350                 case FT_PARSEADDR:
351                 case FT_PARSEDATE:
352                         fmt->f_comp->c_flags &= ~CF_PARSED;
353                         break;
354                 }
355
356         fmt = format;
357
358         while (cp < ep) {
359                 switch (fmt->f_type) {
360
361                 case FT_COMP:
362                         cpstripped(&cp, ep, fmt->f_comp->c_text);
363                         break;
364                 case FT_COMPF:
365                         cptrimmed(&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
366                         break;
367
368                 case FT_LIT:
369                         sp = fmt->f_text;
370                         while( (c = *sp++) && cp < ep)
371                                 *cp++ = c;
372                         break;
373                 case FT_LITF:
374                         sp = fmt->f_text;
375                         ljust = 0;
376                         i = fmt->f_width;
377                         if (i < 0) {
378                                 i = -i;
379                                 ljust++;  /* XXX should do something with this */
380                         }
381                         while( (c = *sp++) && --i >= 0 && cp < ep)
382                                 *cp++ = c;
383                         while( --i >= 0 && cp < ep)
384                                 *cp++ = fmt->f_fill;
385                         break;
386
387                 case FT_STR:
388                         cpstripped(&cp, ep, str);
389                         break;
390                 case FT_STRF:
391                         cptrimmed(&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
392                         break;
393                 case FT_STRFW:
394                         adios(EX_SOFTWARE, NULL, "internal error (FT_STRFW)");
395
396                 case FT_NUM:
397                         n = snprintf(cp, ep - cp + 1, "%d", value);
398                         if (n >= 0) {
399                                 if (n >= ep - cp) {
400                                         cp = ep;
401                                 } else
402                                         cp += n;
403                         }
404                         break;
405                 case FT_NUMF:
406                         cpnumber(&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
407                         break;
408
409                 case FT_CHAR:
410                         *cp++ = fmt->f_char;
411                         break;
412
413                 case FT_DONE:
414                         goto finished;
415
416                 case FT_IF_S:
417                         if (!(value = (str && *str))) {
418                                 fmt += fmt->f_skip;
419                                 continue;
420                         }
421                         break;
422
423                 case FT_IF_S_NULL:
424                         if (!(value = (str == NULL || *str == 0))) {
425                                 fmt += fmt->f_skip;
426                                 continue;
427                         }
428                         break;
429
430                 case FT_IF_V_EQ:
431                         if (value != fmt->f_value) {
432                                 fmt += fmt->f_skip;
433                                 continue;
434                         }
435                         break;
436
437                 case FT_IF_V_NE:
438                         if (value == fmt->f_value) {
439                                 fmt += fmt->f_skip;
440                                 continue;
441                         }
442                         break;
443
444                 case FT_IF_V_GT:
445                         if (value <= fmt->f_value) {
446                                 fmt += fmt->f_skip;
447                                 continue;
448                         }
449                         break;
450
451                 case FT_IF_MATCH:
452                         if (!(value = (str && match(str, fmt->f_text)))) {
453                                 fmt += fmt->f_skip;
454                                 continue;
455                         }
456                         break;
457
458                 case FT_V_MATCH:
459                         if (str)
460                                 value = match(str, fmt->f_text);
461                         else
462                                 value = 0;
463                         break;
464
465                 case FT_IF_AMATCH:
466                         if (!(value = (str && uprf(str, fmt->f_text)))) {
467                                 fmt += fmt->f_skip;
468                                 continue;
469                         }
470                         break;
471
472                 case FT_V_AMATCH:
473                         value = uprf(str, fmt->f_text);
474                         break;
475
476                 case FT_S_NONNULL:
477                         value = (str != NULL && *str != 0);
478                         break;
479
480                 case FT_S_NULL:
481                         value = (str == NULL || *str == 0);
482                         break;
483
484                 case FT_V_EQ:
485                         value = (fmt->f_value == value);
486                         break;
487
488                 case FT_V_NE:
489                         value = (fmt->f_value != value);
490                         break;
491
492                 case FT_V_GT:
493                         value = (fmt->f_value > value);
494                         break;
495
496                 case FT_GOTO:
497                         fmt += fmt->f_skip;
498                         continue;
499
500                 case FT_NOP:
501                         break;
502
503                 case FT_LS_COMP:
504                         str = fmt->f_comp->c_text;
505                         break;
506                 case FT_LS_LIT:
507                         str = fmt->f_text;
508                         break;
509                 case FT_LS_GETENV:
510                         if (!(str = getenv(fmt->f_text)))
511                                 str = "";
512                         break;
513                 case FT_LS_CFIND:
514                         if (!(str = context_find(fmt->f_text)))
515                                 str = "";
516                         break;
517
518                 case FT_LS_DECODECOMP:
519                         if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
520                                 str = buffer2;
521                         else
522                                 str = fmt->f_comp->c_text;
523                         break;
524
525                 case FT_LS_DECODE:
526                         if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
527                                 str = buffer2;
528                         break;
529
530                 case FT_LS_TRIM:
531                         if (str) {
532                                 str = fmt_trim(str, fmt->f_width);
533                         }
534                         break;
535
536                 case FT_LV_COMPFLAG:
537                         value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
538                         break;
539                 case FT_LV_COMP:
540                         value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
541                         break;
542                 case FT_LV_LIT:
543                         value = fmt->f_value;
544                         break;
545                 case FT_LV_DAT:
546                         value = dat[fmt->f_value];
547                         break;
548                 case FT_LV_STRLEN:
549                         if (str != NULL)
550                                 value = strlen(str);
551                         else
552                                 value = 0;
553                         break;
554                 case FT_LV_CHAR_LEFT:
555                         value = width - (cp - scanl);
556                         break;
557                 case FT_LV_PLUS_L:
558                         value += fmt->f_value;
559                         break;
560                 case FT_LV_MINUS_L:
561                         value = fmt->f_value - value;
562                         break;
563                 case FT_LV_DIVIDE_L:
564                         if (fmt->f_value)
565                                 value = value / fmt->f_value;
566                         else
567                                 value = 0;
568                         break;
569                 case FT_LV_MODULO_L:
570                         if (fmt->f_value)
571                                 value = value % fmt->f_value;
572                         else
573                                 value = 0;
574                         break;
575                 case FT_SAVESTR:
576                         savestr = str;
577                         break;
578
579                 case FT_LV_SEC:
580                         value = fmt->f_comp->c_tws->tw_sec;
581                         break;
582                 case FT_LV_MIN:
583                         value = fmt->f_comp->c_tws->tw_min;
584                         break;
585                 case FT_LV_HOUR:
586                         value = fmt->f_comp->c_tws->tw_hour;
587                         break;
588                 case FT_LV_MDAY:
589                         value = fmt->f_comp->c_tws->tw_mday;
590                         break;
591                 case FT_LV_MON:
592                         value = fmt->f_comp->c_tws->tw_mon + 1;
593                         break;
594                 case FT_LS_MONTH:
595                         str = tw_moty[fmt->f_comp->c_tws->tw_mon];
596                         break;
597                 case FT_LS_LMONTH:
598                         str = lmonth[fmt->f_comp->c_tws->tw_mon];
599                         break;
600                 case FT_LS_ZONE:
601                         str = dtwszone(fmt->f_comp->c_tws);
602                         break;
603                 case FT_LV_YEAR:
604                         value = fmt->f_comp->c_tws->tw_year;
605                         break;
606                 case FT_LV_WDAY:
607                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
608                                 set_dotw(tws);
609                         value = tws->tw_wday;
610                         break;
611                 case FT_LS_DAY:
612                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
613                                 set_dotw(tws);
614                         str = tw_dotw[tws->tw_wday];
615                         break;
616                 case FT_LS_WEEKDAY:
617                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
618                                 set_dotw(tws);
619                         str = tw_ldotw[tws->tw_wday];
620                         break;
621                 case FT_LV_YDAY:
622                         value = fmt->f_comp->c_tws->tw_yday;
623                         break;
624                 case FT_LV_ZONE:
625                         value = fmt->f_comp->c_tws->tw_zone;
626                         break;
627                 case FT_LV_CLOCK:
628                         if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
629                                 value = dmktime(fmt->f_comp->c_tws);
630                         break;
631                 case FT_LV_RCLOCK:
632                         if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
633                                 value = dmktime(fmt->f_comp->c_tws);
634                         value = time((time_t *) 0) - value;
635                         break;
636                 case FT_LV_DAYF:
637                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
638                                 set_dotw(tws);
639                         switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
640                         case TW_SEXP:
641                                 value = 1; break;
642                         case TW_SIMP:
643                                 value = 0; break;
644                         default:
645                                 value = -1; break;
646                         }
647                 case FT_LV_ZONEF:
648                         if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
649                                 value = 1;
650                         else
651                                 value = -1;
652                         break;
653                 case FT_LV_DST:
654                         value = fmt->f_comp->c_tws->tw_flags & TW_DST;
655                         break;
656                 case FT_LS_822DATE:
657                 case FT_LS_PRETTY:
658                         str = dasctime(fmt->f_comp->c_tws);
659                         break;
660
661                 case FT_LS_PERS:
662                         str = fmt->f_comp->c_mn->m_pers;
663                         break;
664                 case FT_LS_MBOX:
665                         str = fmt->f_comp->c_mn->m_mbox;
666                         break;
667                 case FT_LS_HOST:
668                         str = fmt->f_comp->c_mn->m_host;
669                         break;
670                 case FT_LS_PATH:
671                         str = fmt->f_comp->c_mn->m_path;
672                         break;
673                 case FT_LS_GNAME:
674                         str = fmt->f_comp->c_mn->m_gname;
675                         break;
676                 case FT_LS_NOTE:
677                         str = fmt->f_comp->c_mn->m_note;
678                         break;
679                 case FT_LS_822ADDR:
680                         str = adrformat( fmt->f_comp->c_mn );
681                         break;
682                 case FT_LV_HOSTTYPE:
683                         value = fmt->f_comp->c_mn->m_type;
684                         break;
685                 case FT_LV_INGRPF:
686                         value = fmt->f_comp->c_mn->m_ingrp;
687                         break;
688                 case FT_LV_NOHOSTF:
689                         value = fmt->f_comp->c_mn->m_nohost;
690                         break;
691                 case FT_LS_ADDR:
692                 case FT_LS_FRIENDLY:
693                         if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
694                                 str = fmt->f_comp->c_text;
695                                 break;
696                         }
697                         if (fmt->f_type == FT_LS_ADDR)
698                                 goto unfriendly;
699                         if ((str = mn->m_pers) == NULL) {
700                                 if ((str = mn->m_note)) {
701                                         strncpy(buffer, str, sizeof(buffer));
702                                         buffer[sizeof(buffer)-1] = '\0';
703                                         str = buffer;
704                                         if (*str == '(')
705                                                         str++;
706                                         sp = str + strlen(str) - 1;
707                                         if (*sp == ')') {
708                                                 *sp-- = '\0';
709                                                 while (sp >= str)
710                                                         if (*sp == ' ')
711                                                                 *sp-- = '\0';
712                                                         else
713                                                                 break;
714                                         }
715                                 } else if (!(str = get_x400_friendly(mn->m_mbox,
716                                         buffer, sizeof(buffer)))) {
717                 unfriendly: ;
718                                         switch (mn->m_type) {
719                                         case LOCALHOST:
720                                                 str = mn->m_mbox;
721                                                 break;
722                                         default:
723                                                 if (mn->m_mbox) {
724                                                         snprintf(buffer, sizeof(buffer), "%s@%s", mn->m_mbox, mn->m_host);
725                                                         str= buffer;
726                                                 } else
727                                                         str = mn->m_text;
728                                                 break;
729                                         }
730                                 }
731                         }
732                         break;
733
734
735                 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
736                 case FT_LS_UNQUOTE:
737                         if (str) {
738                                 strncpy(buffer, str, sizeof(buffer));
739                                 /* strncpy doesn't NUL-terminate if it fills the buffer */
740                                 buffer[sizeof(buffer)-1] = '\0';
741                                 unquote_string(buffer, buffer2);
742                                 str = buffer2;
743                         }
744                         break;
745
746                 case FT_LOCALDATE:
747                         comp = fmt->f_comp;
748                         if ((t = comp->c_tws->tw_clock) == 0)
749                                 t = dmktime(comp->c_tws);
750                         tws = dlocaltime(&t);
751                         *comp->c_tws = *tws;
752                         break;
753
754                 case FT_GMTDATE:
755                         comp = fmt->f_comp;
756                         if ((t = comp->c_tws->tw_clock) == 0)
757                                 t = dmktime(comp->c_tws);
758                         tws = dgmtime(&t);
759                         *comp->c_tws = *tws;
760                         break;
761
762                 case FT_PARSEDATE:
763                         comp = fmt->f_comp;
764                         if (comp->c_flags & CF_PARSED)
765                                 break;
766                         if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
767                                 *comp->c_tws = *tws;
768                                 comp->c_flags &= ~CF_TRUE;
769                         } else if ((comp->c_flags & CF_DATEFAB) == 0) {
770                                 memset((char *) comp->c_tws, 0, sizeof *comp->c_tws);
771                                 comp->c_flags = CF_TRUE;
772                         }
773                         comp->c_flags |= CF_PARSED;
774                         break;
775
776                 case FT_FORMATADDR:
777                         /*
778                         ** hook for custom address list formatting
779                         ** (see replsbr.c)
780                         */
781                         str = formataddr(savestr, str);
782                         break;
783
784                 case FT_PUTADDR:
785                         /*
786                         ** output the str register as an address component,
787                         ** splitting it into multiple lines if necessary.  The
788                         ** value reg. contains the max line length.  The lit.
789                         ** field may contain a string to prepend to the result
790                         ** (e.g., "To: ")
791                         */
792                         {
793                         unsigned char *lp;
794                         char *lastb;
795                         int indent, wid, len;
796
797                         lp = str;
798                         wid = value;
799                         len = strlen(str);
800                         sp = fmt->f_text;
801                         indent = strlen(sp);
802                         wid -= indent;
803                         if (wid <= 0) {
804                                 adios(EX_SOFTWARE, NULL, "putaddr -- num register (%d) "
805                                                 "must be greater than label "
806                                                 "width (%d)", value, indent);
807                         }
808                         while( (c = *sp++) && cp < ep)
809                                 *cp++ = c;
810                         while (len > wid) {
811                                 /*
812                                 ** try to break at a comma; failing that,
813                                 ** break at a space.
814                                 */
815                                 lastb = 0; sp = lp + wid;
816                                 while (sp > lp && (c = *--sp) != ',') {
817                                         if (! lastb && isspace(c))
818                                                 lastb = sp - 1;
819                                 }
820                                 if (sp == lp) {
821                                         if (! (sp = lastb)) {
822                                                 sp = lp + wid - 1;
823                                                 while (*sp && *sp != ',' && !isspace(*sp))
824                                                         sp++;
825                                                 if (*sp != ',')
826                                                         sp--;
827                                         }
828                                 }
829                                 len -= sp - lp + 1;
830                                 while (cp < ep && lp <= sp)
831                                         *cp++ = *lp++;
832                                 while (isspace(*lp))
833                                         lp++, len--;
834                                 if (*lp) {
835                                         if (cp < ep)
836                                                 *cp++ = '\n';
837                                         for (i=indent; cp < ep && i > 0; i--)
838                                                 *cp++ = ' ';
839                                 }
840                         }
841                         cpstripped(&cp, ep, lp);
842                         }
843                         break;
844
845                 case FT_PARSEADDR:
846                         comp = fmt->f_comp;
847                         if (comp->c_flags & CF_PARSED)
848                                 break;
849                         if (comp->c_mn != &fmt_mnull)
850                                 mnfree(comp->c_mn);
851                         if ((sp = comp->c_text) && (sp = getname(sp)) &&
852                                 (mn = getm(sp, NULL, 0, fmt_norm, NULL))) {
853                                 comp->c_mn = mn;
854                                 while (getname(""))
855                                         ;
856                                 comp->c_flags |= CF_PARSED;
857                         } else {
858                                 while (getname(""))  /* XXX */
859                                         ;
860                                 comp->c_mn = &fmt_mnull;
861                         }
862                         break;
863
864                 case FT_MYMBOX:
865                         /*
866                         ** if there's no component, we say true.  Otherwise we
867                         ** say "true" only if we can parse the address and it
868                         ** matches one of our addresses.
869                         */
870                         comp = fmt->f_comp;
871                         if (comp->c_mn != &fmt_mnull)
872                                 mnfree(comp->c_mn);
873                         if ((sp = comp->c_text) && (sp = getname(sp)) &&
874                                 (mn = getm(sp, NULL, 0, AD_NAME, NULL))) {
875                                 comp->c_mn = mn;
876                                 if (ismymbox(mn))
877                                         comp->c_flags |= CF_TRUE;
878                                 else
879                                         comp->c_flags &= ~CF_TRUE;
880                                 while ((sp = getname(sp)))
881                                         if ((comp->c_flags & CF_TRUE) == 0 &&
882                                                 (mn = getm(sp, NULL, 0, AD_NAME, NULL)))
883                                                 if (ismymbox(mn))
884                                                         comp->c_flags |= CF_TRUE;
885                         } else {
886                                 while (getname(""))  /* XXX */
887                                         ;
888                                 if (comp->c_text == 0)
889                                         comp->c_flags |= CF_TRUE;
890                                 else
891                                         comp->c_flags &= ~CF_TRUE;
892                                 comp->c_mn = &fmt_mnull;
893                         }
894                         break;
895
896                 case FT_LS_UNMAILTO:
897                         if (!str) {
898                                 break;
899                         }
900                         str = trim(str);
901                         if (*str == '<' && str[strlen(str)-1] == '>') {
902                                 str++;
903                                 str[strlen(str)-1] = '\0';
904                         }
905                         if (strncmp("mailto:", str, 7)==0) {
906                                 str += 7;
907                         }
908                         break;
909
910                 }
911                 fmt++;
912         }
913 finished:;
914         if (cp[-1] != '\n')
915                 *cp++ = '\n';
916         *cp = '\0';
917         return ((struct format *)0);
918 }