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