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