Added support for optional Content_Disposition header in mhbuild directive.s
[mmh] / sbr / fmt_scan.c
1
2 /*
3  * fmt_scan.c -- format string interpretation
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
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
18 #ifdef TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # ifdef TM_IN_SYS_TIME
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28 #ifdef MULTIBYTE_SUPPORT
29 #  include <wctype.h>
30 #  include <wchar.h>
31 #endif
32
33 #define NFMTS MAXARGS
34
35 extern char *formataddr ();     /* hook for custom address formatting */
36
37 #ifdef LBL
38 struct msgs *fmt_current_folder; /* current folder (set by main program) */
39 #endif
40
41 extern int fmt_norm;            /* defined in sbr/fmt_def.c = AD_NAME */
42 struct mailname fmt_mnull;
43
44 /*
45  * static prototypes
46  */
47 static int match (char *, char *);
48 static char *get_x400_friendly (char *, char *, int);
49 static int get_x400_comp (char *, char *, char *, int);
50
51
52 /*
53  * test if string "sub" appears anywhere in
54  * string "str" (case insensitive).
55  */
56
57 static int
58 match (char *str, char *sub)
59 {
60     int c1, c2;
61     char *s1, *s2;
62
63 #ifdef LOCALE
64     while ((c1 = *sub)) {
65         c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
66         while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
67             ;
68         if (! c2)
69             return 0;
70         s1 = sub + 1; s2 = str;
71         while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
72             ;
73         if (! c1)
74             return 1;
75     }
76 #else
77     while ((c1 = *sub)) {
78         while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
79             ;
80         if (! c2)
81             return 0;
82         s1 = sub + 1; s2 = str;
83         while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
84             ;
85         if (! c1)
86             return 1;
87     }
88 #endif
89     return 1;
90 }
91
92 /*
93  * copy a number to the destination subject to a maximum width
94  */
95 static void
96 cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
97     int i, c;
98     char *sp;
99     char *cp = *dest;
100     char *ep = cp + n;
101
102     if (cp + wid < ep) {
103         if ((i = (num)) < 0)
104             i = -(num);
105         if ((c = (wid)) < 0)
106             c = -c;
107         sp = cp + c;
108         do {
109             *--sp = (i % 10) + '0';
110             i /= 10;
111         } while (i > 0 && sp > cp);
112         if (i > 0)
113             *sp = '?';
114         else if ((num) < 0 && sp > cp)
115             *--sp = '-';
116         while (sp > cp)
117             *--sp = fill;
118         cp += c;
119     }
120     *dest = cp;
121 }
122
123 /*
124  * copy string from str to dest padding with the fill character to a size
125  * of wid characters. if wid is negative, the string is right aligned
126  * no more than n bytes are copied
127  */
128 static void
129 cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
130     int remaining;     /* remaining output width available */
131     int c, ljust, w;
132     int end;           /* number of input bytes remaining in str */
133 #ifdef MULTIBYTE_SUPPORT
134     int char_len;      /* bytes in current character */
135     wchar_t wide_char;
136 #endif
137     char *sp;          /* current position in source string */
138     char *cp = *dest;  /* current position in destination string */
139     char *ep = cp + n; /* end of destination buffer */
140     int prevCtrl = 1;
141
142     /* get alignment */
143     ljust = 0;
144     if ((remaining = (wid)) < 0) {
145         remaining = -remaining;
146         ljust++;
147     }
148     if ((sp = (str))) {
149         mbtowc(NULL, NULL, 0); /* reset shift state */
150         end = strlen(str);
151         while (*sp && remaining > 0 && end > 0) {
152 #ifdef MULTIBYTE_SUPPORT
153             char_len = mbtowc(&wide_char, sp, end);
154             if (char_len <= 0 || (cp + char_len > ep))
155                 break;
156
157             end -= char_len;
158
159             if (iswcntrl(wide_char) || iswspace(wide_char)) {
160                 sp += char_len;
161 #else
162             end--;
163             if (iscntrl(*sp) || isspace(*sp)) {
164                 sp++;
165 #endif
166                 if (!prevCtrl) {
167                     *cp++ = ' ';
168                     remaining--;
169                 }
170
171                 prevCtrl = 1;
172                 continue;
173             }
174             prevCtrl = 0;
175
176 #ifdef MULTIBYTE_SUPPORT
177             w = wcwidth(wide_char);
178             if (w >= 0 && remaining >= w) {
179                 strncpy(cp, sp, char_len);
180                 cp += char_len;
181                 remaining -= w;
182             }
183             sp += char_len;
184 #else
185             *cp++ = *sp++;
186             remaining--;
187 #endif
188         }
189     }
190
191     if (ljust) {
192         if (cp + remaining > ep)
193             remaining = ep - cp;
194         ep = cp + remaining;
195         if (remaining > 0) {
196             /* copy string to the right */
197             while (--cp >= *dest)
198                 *(cp + remaining) = *cp;
199             /* add padding at the beginning */
200             cp += remaining;
201             for (c=remaining; c>0; c--)
202                 *cp-- = fill;
203         }
204         *dest = ep;
205     } else {
206         /* pad remaining space */
207         while (remaining-- > 0 && cp < ep)
208                 *cp++ = fill;
209         *dest = cp;
210     }
211 }
212
213 static void
214 cpstripped (char **start, char *end, char *str)
215 {
216     int c;
217     char *s = str;
218
219     if (!s)
220         return;
221
222     /* skip any initial control characters or spaces */
223     while ((c = (unsigned char) *s) &&
224 #ifdef LOCALE
225             (iscntrl(c) || isspace(c)))
226 #else
227             (c <= 32))
228 #endif
229         s++;
230
231     /* compact repeated control characters and spaces into a single space */
232     while((c = (unsigned char) *s++) && *start < end)
233         if (!iscntrl(c) && !isspace(c))
234             *(*start)++ = c;
235         else {
236             while ((c = (unsigned char) *s) &&
237 #ifdef LOCALE
238                     (iscntrl(c) || isspace(c)))
239 #else
240                     (c <= 32))
241 #endif
242                 s++;
243             *(*start)++ = ' ';
244         }
245 }
246
247 static char *lmonth[] = { "January",  "February","March",   "April",
248                           "May",      "June",    "July",    "August",
249                           "September","October", "November","December" };
250
251 static char *
252 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
253 {
254     char given[BUFSIZ], surname[BUFSIZ];
255
256     if (mbox == NULL)
257         return NULL;
258     if (*mbox == '"')
259         mbox++;
260     if (*mbox != '/')
261         return NULL;
262
263     if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
264         for (mbox = buffer; (mbox = strchr(mbox, '.')); )
265             *mbox++ = ' ';
266
267         return buffer;
268     }
269
270     if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
271         return NULL;
272
273     if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
274         snprintf (buffer, buffer_len, "%s %s", given, surname);
275     else
276         snprintf (buffer, buffer_len, "%s", surname);
277
278     return buffer;
279 }
280
281 static int
282 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
283 {
284     int idx;
285     char *cp;
286
287     if ((idx = stringdex (key, mbox)) < 0
288             || !(cp = strchr(mbox += idx + strlen (key), '/')))
289         return 0;
290
291     snprintf (buffer, buffer_len, "%*.*s", cp - mbox, cp - mbox, mbox);
292     return 1;
293 }
294
295 struct format *
296 fmt_scan (struct format *format, char *scanl, int width, int *dat)
297 {
298     char *cp, *ep, *sp;
299     char *savestr, *str = NULL;
300     char buffer[BUFSIZ], buffer2[BUFSIZ];
301     int i, c, ljust, n;
302     int value = 0;
303     time_t t;
304     struct format *fmt;
305     struct comp *comp;
306     struct tws *tws;
307     struct mailname *mn;
308
309     cp = scanl;
310     ep = scanl + width - 1;
311
312     for (fmt = format; fmt->f_type != FT_DONE; fmt++)
313         switch (fmt->f_type) {
314         case FT_PARSEADDR:
315         case FT_PARSEDATE:
316             fmt->f_comp->c_flags &= ~CF_PARSED;
317             break;
318         }
319
320     fmt = format;
321
322     while (cp < ep) {
323         switch (fmt->f_type) {
324
325         case FT_COMP:
326             cpstripped (&cp, ep, fmt->f_comp->c_text);
327             break;
328         case FT_COMPF:
329             cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
330             break;
331
332         case FT_LIT:
333             sp = fmt->f_text;
334             while( (c = *sp++) && cp < ep)
335                 *cp++ = c;
336             break;
337         case FT_LITF:
338             sp = fmt->f_text;
339             ljust = 0;
340             i = fmt->f_width;
341             if (i < 0) {
342                 i = -i;
343                 ljust++;                /* XXX should do something with this */
344             }
345             while( (c = *sp++) && --i >= 0 && cp < ep)
346                 *cp++ = c;
347             while( --i >= 0 && cp < ep)
348                 *cp++ = fmt->f_fill;
349             break;
350
351         case FT_STR:
352             cpstripped (&cp, ep, str);
353             break;
354         case FT_STRF:
355             cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
356             break;
357         case FT_STRFW:
358             adios (NULL, "internal error (FT_STRFW)");
359
360         case FT_NUM:
361             n = snprintf(cp, ep - cp + 1, "%d", value);
362             if (n >= 0)
363                 if (n >= ep - cp) {
364                     cp = ep;
365                 } else
366                     cp += n;
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                     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_PUTADDR:
796             /* output the str register as an address component,
797              * splitting it into multiple lines if necessary.  The
798              * value reg. contains the max line length.  The lit.
799              * field may contain a string to prepend to the result
800              * (e.g., "To: ")
801              */
802             {
803             char *lp, *lastb;
804             int indent, wid, len;
805
806             lp = str;
807             wid = value;
808             len = strlen (str);
809             sp = fmt->f_text;
810             indent = strlen (sp);
811             wid -= indent;
812             while( (c = *sp++) && cp < ep)
813                 *cp++ = c;
814             while (len > wid) {
815                 /* try to break at a comma; failing that, break at a
816                  * space.
817                  */
818                 lastb = 0; sp = lp + wid;
819                 while (sp > lp && (c = *--sp) != ',') {
820                     if (! lastb && isspace(c))
821                         lastb = sp - 1;
822                 }
823                 if (sp == lp) {
824                     if (! (sp = lastb)) {
825                         sp = lp + wid - 1;
826                         while (*sp && *sp != ',' && !isspace(*sp))
827                             sp++;
828                         if (*sp != ',')
829                             sp--;
830                     }
831                 }
832                 len -= sp - lp + 1;
833                 while (cp < ep && lp <= sp)
834                     *cp++ = *lp++;
835                 while (isspace(*lp))
836                     lp++, len--;
837                 if (*lp) {
838                     if (cp < ep)
839                         *cp++ = '\n';
840                     for (i=indent; cp < ep && i > 0; i--)
841                         *cp++ = ' ';
842                 }
843             }
844             cpstripped (&cp, ep, lp);
845             }
846             break;
847
848         case FT_PARSEADDR:
849             comp = fmt->f_comp;
850             if (comp->c_flags & CF_PARSED)
851                 break;
852             if (comp->c_mn != &fmt_mnull)
853                 mnfree (comp->c_mn);
854             if ((sp = comp->c_text) && (sp = getname(sp)) &&
855                 (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
856                 comp->c_mn = mn;
857                 while (getname(""))
858                     ;
859                 comp->c_flags |= CF_PARSED;
860             } else {
861                 while (getname(""))             /* XXX */
862                     ;
863                 comp->c_mn = &fmt_mnull;
864             }
865             break;
866
867         case FT_MYMBOX:
868             /*
869              * if there's no component, we say true.  Otherwise we
870              * say "true" only if we can parse the address and it
871              * matches one of our addresses.
872              */
873             comp = fmt->f_comp;
874             if (comp->c_mn != &fmt_mnull)
875                 mnfree (comp->c_mn);
876             if ((sp = comp->c_text) && (sp = getname(sp)) &&
877                 (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
878                 comp->c_mn = mn;
879                 if (ismymbox(mn))
880                     comp->c_flags |= CF_TRUE;
881                 else
882                     comp->c_flags &= ~CF_TRUE;
883                 while ((sp = getname(sp)))
884                     if ((comp->c_flags & CF_TRUE) == 0 &&
885                         (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
886                         if (ismymbox(mn))
887                             comp->c_flags |= CF_TRUE;
888             } else {
889                 while (getname(""))             /* XXX */
890                     ;
891                 if (comp->c_text == 0)
892                     comp->c_flags |= CF_TRUE;
893                 else
894                     comp->c_flags &= ~CF_TRUE;
895                 comp->c_mn = &fmt_mnull;
896             }
897             break;
898
899         case FT_ADDTOSEQ:
900 #ifdef LBL
901             /* If we're working on a folder (as opposed to a file), add the
902              * current msg to sequence given in literal field.  Don't
903              * disturb string or value registers.
904              */
905             if (fmt_current_folder)
906                     seq_addmsg(fmt_current_folder, fmt->f_text, dat[0], -1);
907 #endif
908             break;
909         }
910         fmt++;
911     }
912 #ifndef JLR
913     finished:;
914     if (cp[-1] != '\n')
915         *cp++ = '\n';
916     *cp   = 0;
917     return ((struct format *)0);
918 #else /* JLR */
919     if (cp[-1] != '\n')
920         *cp++ = '\n';
921     while (fmt->f_type != FT_DONE)
922         fmt++;
923
924     finished:;
925     *cp = '\0';
926     return (fmt->f_value ? ++fmt : (struct format *) 0);
927
928 #endif /* JLR */
929 }