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