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