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