gcc -Wmissing-field-initializers noticed several struct initializations
[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  * This is the engine that processes the format instructions created by
10  * fmt_compile (found in fmt_compile.c).
11  */
12
13 #include <h/mh.h>
14 #include <h/addrsbr.h>
15 #include <h/fmt_scan.h>
16 #include <h/tws.h>
17 #include <h/fmt_compile.h>
18
19 #ifdef HAVE_SYS_TIME_H
20 # include <sys/time.h>
21 #endif
22 #include <time.h>
23 #ifdef MULTIBYTE_SUPPORT
24 #  include <wctype.h>
25 #  include <wchar.h>
26 #endif
27
28 extern char *formataddr ();     /* hook for custom address formatting */
29 extern char *concataddr ();     /* address formatting but allowing duplicates */
30
31 #ifdef LBL
32 struct msgs *fmt_current_folder; /* current folder (set by main program) */
33 #endif
34
35 extern int fmt_norm;            /* defined in sbr/fmt_def.c = AD_NAME */
36 struct mailname fmt_mnull;
37
38 /*
39  * static prototypes
40  */
41 static int match (char *, char *);
42 static char *get_x400_friendly (char *, char *, int);
43 static int get_x400_comp (char *, char *, char *, int);
44
45
46 /*
47  * test if string "sub" appears anywhere in
48  * string "str" (case insensitive).
49  */
50
51 static int
52 match (char *str, char *sub)
53 {
54     int c1, c2;
55     char *s1, *s2;
56
57 #ifdef LOCALE
58     while ((c1 = *sub)) {
59         c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
60         while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
61             ;
62         if (! c2)
63             return 0;
64         s1 = sub + 1; s2 = str;
65         while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
66             ;
67         if (! c1)
68             return 1;
69     }
70 #else
71     while ((c1 = *sub)) {
72         while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
73             ;
74         if (! c2)
75             return 0;
76         s1 = sub + 1; s2 = str;
77         while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
78             ;
79         if (! c1)
80             return 1;
81     }
82 #endif
83     return 1;
84 }
85
86 /*
87  * copy a number to the destination subject to a maximum width
88  */
89 static void
90 cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
91     int i, c;
92     char *sp;
93     char *cp = *dest;
94     char *ep = cp + n;
95
96     if (cp + wid < ep) {
97         if ((i = (num)) < 0)
98             i = -(num);
99         if ((c = (wid)) < 0)
100             c = -c;
101         sp = cp + c;
102         do {
103             *--sp = (i % 10) + '0';
104             i /= 10;
105         } while (i > 0 && sp > cp);
106         if (i > 0)
107             *sp = '?';
108         else if ((num) < 0 && sp > cp)
109             *--sp = '-';
110         while (sp > cp)
111             *--sp = fill;
112         cp += c;
113     }
114     *dest = cp;
115 }
116
117 /*
118  * copy string from str to dest padding with the fill character to a size
119  * of wid characters. if wid is negative, the string is right aligned
120  * no more than n bytes are copied
121  */
122 static void
123 cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
124     int remaining;     /* remaining output width available */
125     int c, ljust;
126     int end;           /* number of input bytes remaining in str */
127 #ifdef MULTIBYTE_SUPPORT
128     int char_len;      /* bytes in current character */
129     int w;
130     wchar_t wide_char;
131 #endif
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 #ifdef MULTIBYTE_SUPPORT
148             char_len = mbtowc(&wide_char, sp, end);
149             if (char_len <= 0 || (cp + char_len > ep))
150                 break;
151
152             end -= char_len;
153
154             if (iswcntrl(wide_char) || iswspace(wide_char)) {
155                 sp += char_len;
156 #else
157             end--;
158             if (iscntrl(*sp) || isspace(*sp)) {
159                 sp++;
160 #endif
161                 if (!prevCtrl) {
162                     *cp++ = ' ';
163                     remaining--;
164                 }
165
166                 prevCtrl = 1;
167                 continue;
168             }
169             prevCtrl = 0;
170
171 #ifdef MULTIBYTE_SUPPORT
172             w = wcwidth(wide_char);
173             if (w >= 0 && remaining >= w) {
174                 strncpy(cp, sp, char_len);
175                 cp += char_len;
176                 remaining -= w;
177             }
178             sp += char_len;
179 #else
180             *cp++ = *sp++;
181             remaining--;
182 #endif
183         }
184     }
185
186     if (ljust) {
187         if (cp + remaining > ep)
188             remaining = ep - cp;
189         ep = cp + remaining;
190         if (remaining > 0) {
191             /* copy string to the right */
192             while (--cp >= *dest)
193                 *(cp + remaining) = *cp;
194             /* add padding at the beginning */
195             cp += remaining;
196             for (c=remaining; c>0; c--)
197                 *cp-- = fill;
198         }
199         *dest = ep;
200     } else {
201         /* pad remaining space */
202         while (remaining-- > 0 && cp < ep)
203                 *cp++ = fill;
204         *dest = cp;
205     }
206 }
207
208 static void
209 cpstripped (char **start, char *end, char *str)
210 {
211     int c;
212     char *s = str;
213
214     if (!s)
215         return;
216
217     /* skip any initial control characters or spaces */
218     while ((c = (unsigned char) *s) &&
219 #ifdef LOCALE
220             (iscntrl(c) || isspace(c)))
221 #else
222             (c <= 32))
223 #endif
224         s++;
225
226     /* compact repeated control characters and spaces into a single space */
227     while((c = (unsigned char) *s++) && *start < end)
228         if (!iscntrl(c) && !isspace(c))
229             *(*start)++ = c;
230         else {
231             while ((c = (unsigned char) *s) &&
232 #ifdef LOCALE
233                     (iscntrl(c) || isspace(c)))
234 #else
235                     (c <= 32))
236 #endif
237                 s++;
238             *(*start)++ = ' ';
239         }
240 }
241
242 static char *lmonth[] = { "January",  "February","March",   "April",
243                           "May",      "June",    "July",    "August",
244                           "September","October", "November","December" };
245
246 static char *
247 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
248 {
249     char given[BUFSIZ], surname[BUFSIZ];
250
251     if (mbox == NULL)
252         return NULL;
253     if (*mbox == '"')
254         mbox++;
255     if (*mbox != '/')
256         return NULL;
257
258     if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
259         for (mbox = buffer; (mbox = strchr(mbox, '.')); )
260             *mbox++ = ' ';
261
262         return buffer;
263     }
264
265     if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
266         return NULL;
267
268     if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
269         snprintf (buffer, buffer_len, "%s %s", given, surname);
270     else
271         snprintf (buffer, buffer_len, "%s", surname);
272
273     return buffer;
274 }
275
276 static int
277 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
278 {
279     int idx;
280     char *cp;
281
282     if ((idx = stringdex (key, mbox)) < 0
283             || !(cp = strchr(mbox += idx + strlen (key), '/')))
284         return 0;
285
286     snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
287     return 1;
288 }
289
290 struct format *
291 fmt_scan (struct format *format, char *scanl, int width, int *dat)
292 {
293     char *cp, *ep;
294     unsigned char *sp;
295     char *savestr = NULL;
296     unsigned char *str = NULL;
297     char buffer[BUFSIZ], buffer2[BUFSIZ];
298     int i, c, ljust, n;
299     int value = 0;
300     time_t t;
301     struct format *fmt;
302     struct comp *comp;
303     struct tws *tws;
304     struct mailname *mn;
305
306     cp = scanl;
307     ep = scanl + width - 1;
308
309     for (fmt = format; fmt->f_type != FT_DONE; fmt++)
310         switch (fmt->f_type) {
311         case FT_PARSEADDR:
312         case FT_PARSEDATE:
313             fmt->f_comp->c_flags &= ~CF_PARSED;
314             break;
315         }
316
317     fmt = format;
318
319     while (cp < ep) {
320         switch (fmt->f_type) {
321
322         case FT_COMP:
323             cpstripped (&cp, ep, fmt->f_comp->c_text);
324             break;
325         case FT_COMPF:
326             cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
327             break;
328
329         case FT_LIT:
330             sp = fmt->f_text;
331             while( (c = *sp++) && cp < ep)
332                 *cp++ = c;
333             break;
334         case FT_LITF:
335             sp = fmt->f_text;
336             ljust = 0;
337             i = fmt->f_width;
338             if (i < 0) {
339                 i = -i;
340                 ljust++;                /* XXX should do something with this */
341             }
342             while( (c = *sp++) && --i >= 0 && cp < ep)
343                 *cp++ = c;
344             while( --i >= 0 && cp < ep)
345                 *cp++ = fmt->f_fill;
346             break;
347
348         case FT_STR:
349             cpstripped (&cp, ep, str);
350             break;
351         case FT_STRF:
352             cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
353             break;
354         case FT_STRLIT:
355             sp = str;
356             while ((c = *sp++) && cp < ep)
357                 *cp++ = c;
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_CONCATADDR:
799             /* The same as formataddr, but doesn't do duplicate suppression */
800             str = concataddr (savestr, str);
801             break;
802
803         case FT_PUTADDR:
804             /* output the str register as an address component,
805              * splitting it into multiple lines if necessary.  The
806              * value reg. contains the max line length.  The lit.
807              * field may contain a string to prepend to the result
808              * (e.g., "To: ")
809              */
810             {
811             unsigned char *lp;
812             char *lastb;
813             int indent, wid, len;
814
815             lp = str;
816             wid = value;
817             len = strlen (str);
818             sp = fmt->f_text;
819             indent = strlen (sp);
820             wid -= indent;
821             while( (c = *sp++) && cp < ep)
822                 *cp++ = c;
823             while (len > wid) {
824                 /* try to break at a comma; failing that, break at a
825                  * space.
826                  */
827                 lastb = 0; sp = lp + wid;
828                 while (sp > lp && (c = *--sp) != ',') {
829                     if (! lastb && isspace(c))
830                         lastb = sp - 1;
831                 }
832                 if (sp == lp) {
833                     if (! (sp = lastb)) {
834                         sp = lp + wid - 1;
835                         while (*sp && *sp != ',' && !isspace(*sp))
836                             sp++;
837                         if (*sp != ',')
838                             sp--;
839                     }
840                 }
841                 len -= sp - lp + 1;
842                 while (cp < ep && lp <= sp)
843                     *cp++ = *lp++;
844                 while (isspace(*lp))
845                     lp++, len--;
846                 if (*lp) {
847                     if (cp < ep)
848                         *cp++ = '\n';
849                     for (i=indent; cp < ep && i > 0; i--)
850                         *cp++ = ' ';
851                 }
852             }
853             cpstripped (&cp, ep, lp);
854             }
855             break;
856
857         case FT_PARSEADDR:
858             comp = fmt->f_comp;
859             if (comp->c_flags & CF_PARSED)
860                 break;
861             if (comp->c_mn != &fmt_mnull)
862                 mnfree (comp->c_mn);
863             if ((sp = comp->c_text) && (sp = getname(sp)) &&
864                 (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
865                 comp->c_mn = mn;
866                 while (getname(""))
867                     ;
868                 comp->c_flags |= CF_PARSED;
869             } else {
870                 while (getname(""))             /* XXX */
871                     ;
872                 comp->c_mn = &fmt_mnull;
873             }
874             break;
875
876         case FT_MYMBOX:
877             /*
878              * if there's no component, we say true.  Otherwise we
879              * say "true" only if we can parse the address and it
880              * matches one of our addresses.
881              */
882             comp = fmt->f_comp;
883             if (comp->c_mn != &fmt_mnull)
884                 mnfree (comp->c_mn);
885             if ((sp = comp->c_text) && (sp = getname(sp)) &&
886                 (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
887                 comp->c_mn = mn;
888                 if (ismymbox(mn))
889                     comp->c_flags |= CF_TRUE;
890                 else
891                     comp->c_flags &= ~CF_TRUE;
892                 while ((sp = getname(sp)))
893                     if ((comp->c_flags & CF_TRUE) == 0 &&
894                         (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
895                         if (ismymbox(mn))
896                             comp->c_flags |= CF_TRUE;
897             } else {
898                 while (getname(""))             /* XXX */
899                     ;
900                 if (comp->c_text == 0)
901                     comp->c_flags |= CF_TRUE;
902                 else
903                     comp->c_flags &= ~CF_TRUE;
904                 comp->c_mn = &fmt_mnull;
905             }
906             break;
907
908         case FT_ADDTOSEQ:
909 #ifdef LBL
910             /* If we're working on a folder (as opposed to a file), add the
911              * current msg to sequence given in literal field.  Don't
912              * disturb string or value registers.
913              */
914             if (fmt_current_folder)
915                     seq_addmsg(fmt_current_folder, fmt->f_text, dat[0], -1);
916 #endif
917             break;
918         }
919         fmt++;
920     }
921 #ifndef JLR
922     finished:;
923     if (cp[-1] != '\n')
924         *cp++ = '\n';
925     *cp   = 0;
926     return ((struct format *)0);
927 #else /* JLR */
928     if (cp[-1] != '\n')
929         *cp++ = '\n';
930     while (fmt->f_type != FT_DONE)
931         fmt++;
932
933     finished:;
934     *cp = '\0';
935     return (fmt->f_value ? ++fmt : (struct format *) 0);
936
937 #endif /* JLR */
938 }