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