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