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