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