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