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