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