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