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