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