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