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