]> diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
It looks like the first test fails on Solaris because it
[nmh] / sbr / fmt_scan.c
1
2 /*
3 * fmt_scan.c -- format string interpretation
4 *
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 *
9 * This is the engine that processes the format instructions created by
10 * fmt_compile (found in fmt_compile.c).
11 */
12
13 #include <h/mh.h>
14 #include <h/addrsbr.h>
15 #include <h/fmt_scan.h>
16 #include <h/tws.h>
17 #include <h/fmt_compile.h>
18
19 #ifdef HAVE_SYS_TIME_H
20 # include <sys/time.h>
21 #endif
22 #include <time.h>
23 #ifdef MULTIBYTE_SUPPORT
24 # include <wctype.h>
25 # include <wchar.h>
26 #endif
27
28 extern int fmt_norm; /* defined in sbr/fmt_def.c = AD_NAME */
29 struct mailname fmt_mnull = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
30 NULL, NULL };
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 #ifdef LOCALE
52 while ((c1 = *sub)) {
53 c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
54 while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
55 ;
56 if (! c2)
57 return 0;
58 s1 = sub + 1; s2 = str;
59 while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
60 ;
61 if (! c1)
62 return 1;
63 }
64 #else
65 while ((c1 = *sub)) {
66 while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
67 ;
68 if (! c2)
69 return 0;
70 s1 = sub + 1; s2 = str;
71 while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
72 ;
73 if (! c1)
74 return 1;
75 }
76 #endif
77 return 1;
78 }
79
80 /*
81 * copy a number to the destination subject to a maximum width
82 */
83 static void
84 cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
85 int i, c;
86 char *sp;
87 char *cp = *dest;
88 char *ep = cp + n;
89
90 if (cp + wid < ep) {
91 if ((i = (num)) < 0)
92 i = -(num);
93 if ((c = (wid)) < 0)
94 c = -c;
95 sp = cp + c;
96 do {
97 *--sp = (i % 10) + '0';
98 i /= 10;
99 } while (i > 0 && sp > cp);
100 if (i > 0)
101 *sp = '?';
102 else if ((num) < 0 && sp > cp)
103 *--sp = '-';
104 while (sp > cp)
105 *--sp = fill;
106 cp += c;
107 }
108 *dest = cp;
109 }
110
111 /*
112 * copy string from str to dest padding with the fill character to a size
113 * of wid characters. if wid is negative, the string is right aligned
114 * no more than n bytes are copied
115 */
116 static void
117 cptrimmed(char **dest, char **ep, char *str, unsigned int wid, char fill,
118 char *epmax) {
119 int remaining; /* remaining output width available */
120 int c, ljust;
121 int end; /* number of input bytes remaining in str */
122 #ifdef MULTIBYTE_SUPPORT
123 int char_len; /* bytes in current character */
124 int w;
125 wchar_t wide_char;
126 #endif
127 char *sp; /* current position in source string */
128 char *cp = *dest; /* current position in destination string */
129 int prevCtrl = 1;
130
131 /* get alignment */
132 ljust = 0;
133 if ((remaining = (wid)) < 0) {
134 remaining = -remaining;
135 ljust++;
136 }
137 if ((sp = (str))) {
138 #ifdef MULTIBYTE_SUPPORT
139 mbtowc(NULL, NULL, 0); /* reset shift state */
140 #endif
141 end = strlen(str);
142 while (*sp && remaining > 0 && end > 0) {
143 #ifdef MULTIBYTE_SUPPORT
144 char_len = mbtowc(&wide_char, sp, end);
145
146 if (char_len <= 0)
147 break;
148
149 w = wcwidth(wide_char);
150
151 /*
152 * Multibyte characters can have a variable number of column
153 * widths, so use the column width to bump the end pointer when
154 * appropriate.
155 */
156 if (char_len > 1 && epmax - *ep >= char_len - w) {
157 *ep += char_len - w;
158 }
159
160 if (cp + w > *ep)
161 break;
162
163 end -= char_len;
164
165 if (iswcntrl(wide_char) || iswspace(wide_char)) {
166 sp += char_len;
167 #else
168 int c;
169 end--;
170 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
171 intentionally warns if they are passed a char. */
172 c = *sp;
173 if (iscntrl(c) || isspace(c)) {
174 sp++;
175 #endif
176 if (!prevCtrl) {
177 *cp++ = ' ';
178 remaining--;
179 }
180
181 prevCtrl = 1;
182 continue;
183 }
184 prevCtrl = 0;
185
186 #ifdef MULTIBYTE_SUPPORT
187 if (w >= 0 && remaining >= w) {
188 strncpy(cp, sp, char_len);
189 cp += char_len;
190 remaining -= w;
191 }
192 sp += char_len;
193 #else
194 *cp++ = *sp++;
195 remaining--;
196 #endif
197 }
198 }
199
200 if (ljust) {
201 char *endfield;
202 if (cp + remaining > *ep)
203 remaining = *ep - cp;
204 endfield = cp + remaining;
205 if (remaining > 0) {
206 /* copy string to the right */
207 while (--cp >= *dest)
208 *(cp + remaining) = *cp;
209 /* add padding at the beginning */
210 cp += remaining;
211 for (c=remaining; c>0; c--)
212 *cp-- = fill;
213 }
214 *dest = endfield;
215 } else {
216 /* pad remaining space */
217 while (remaining-- > 0 && cp < *ep)
218 *cp++ = fill;
219 *dest = cp;
220 }
221 }
222
223 static void
224 cpstripped (char **dest, char **end, char *max, char *str)
225 {
226 int prevCtrl = 1; /* This is 1 so we strip out leading spaces */
227 int len;
228 #ifdef MULTIBYTE_SUPPORT
229 int char_len, w;
230 wchar_t wide_char;
231 #endif /* MULTIBYTE_SUPPORT */
232
233 if (!str)
234 return;
235
236 len = strlen(str);
237
238 #ifdef MULTIBYTE_SUPPORT
239 mbtowc(NULL, NULL, 0); /* Reset shift state */
240 #endif /* MULTIBYTE_SUPPORT */
241
242 /*
243 * Process each character at a time; if we have multibyte support
244 * then deal with that here.
245 */
246
247 while (*str != '\0' && len > 0 && *dest < *end) {
248 #ifdef MULTIBYTE_SUPPORT
249 char_len = mbtowc(&wide_char, str, len);
250 w = wcwidth(wide_char);
251
252 /*
253 * Account for multibyte characters, and increment the end pointer
254 * by the number of "extra" bytes in this character. That's the
255 * character length (char_len) minus the column width (w).
256 */
257 if (char_len > 1 && max - *end >= char_len - w) {
258 *end += char_len - w;
259 }
260
261 if (char_len <= 0 || *dest + char_len > *end)
262 break;
263
264 len -= char_len;
265
266 if (iswcntrl(wide_char) || iswspace(wide_char)) {
267 str += char_len;
268 #else /* MULTIBYTE_SUPPORT */
269 int c = *str;
270 len--;
271 if (iscntrl(c) || isspace(c)) {
272 str++;
273 #endif /* MULTIBYTE_SUPPORT */
274 if (! prevCtrl) {
275 *(*dest)++ = ' ';
276 }
277
278 prevCtrl = 1;
279 continue;
280 }
281
282 prevCtrl = 0;
283
284 #ifdef MULTIBYTE_SUPPORT
285 memcpy(*dest, str, char_len);
286 str += char_len;
287 *dest += char_len;
288 #else /* MULTIBYE_SUPPORT */
289 *(*dest)++ = *str++
290 #endif /* MULTIBYTE_SUPPORT */
291 }
292 }
293
294 static char *lmonth[] = { "January", "February","March", "April",
295 "May", "June", "July", "August",
296 "September","October", "November","December" };
297
298 static char *
299 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
300 {
301 char given[BUFSIZ], surname[BUFSIZ];
302
303 if (mbox == NULL)
304 return NULL;
305 if (*mbox == '"')
306 mbox++;
307 if (*mbox != '/')
308 return NULL;
309
310 if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
311 for (mbox = buffer; (mbox = strchr(mbox, '.')); )
312 *mbox++ = ' ';
313
314 return buffer;
315 }
316
317 if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
318 return NULL;
319
320 if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
321 snprintf (buffer, buffer_len, "%s %s", given, surname);
322 else
323 snprintf (buffer, buffer_len, "%s", surname);
324
325 return buffer;
326 }
327
328 static int
329 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
330 {
331 int idx;
332 char *cp;
333
334 if ((idx = stringdex (key, mbox)) < 0
335 || !(cp = strchr(mbox += idx + strlen (key), '/')))
336 return 0;
337
338 snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
339 return 1;
340 }
341
342 struct format *
343 fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat)
344 {
345 char *cp, *ep;
346 unsigned char *sp;
347 char *savestr = NULL;
348 unsigned char *str = NULL;
349 char buffer[BUFSIZ], buffer2[BUFSIZ];
350 int i, c, ljust, n;
351 int value = 0;
352 time_t t;
353 struct format *fmt;
354 struct comp *comp;
355 struct tws *tws;
356 struct mailname *mn;
357
358 /* ep keeps track of displayed characters. They're limited by width.
359 The total number of characters, cp - scanl + 1 (for trailing NULL),
360 includes invisible control characters and is limited by max. */
361 cp = scanl;
362 ep = scanl + (width <= (int) max ? width : (int) max) - 1;
363
364 for (fmt = format; fmt->f_type != FT_DONE; fmt++)
365 switch (fmt->f_type) {
366 case FT_PARSEADDR:
367 case FT_PARSEDATE:
368 fmt->f_comp->c_flags &= ~CF_PARSED;
369 break;
370 case FT_COMP:
371 case FT_COMPF:
372 case FT_LS_COMP:
373 case FT_LS_DECODECOMP:
374 /*
375 * Trim these components of any newlines.
376 *
377 * But don't trim the "body" and "text" components.
378 */
379
380 comp = fmt->f_comp;
381
382 if (! (comp->c_flags & CF_TRIMMED) && comp->c_text &&
383 (i = strlen(comp->c_text)) > 0) {
384 if (comp->c_text[i - 1] == '\n' &&
385 strcmp(comp->c_name, "body") != 0 &&
386 strcmp(comp->c_name, "text") != 0)
387 comp->c_text[i - 1] = '\0';
388 comp->c_flags |= CF_TRIMMED;
389 }
390 break;
391 }
392
393 fmt = format;
394
395 while (cp < ep) {
396 switch (fmt->f_type) {
397
398 case FT_COMP:
399 cpstripped (&cp, &ep, scanl + max - 1, fmt->f_comp->c_text);
400 break;
401 case FT_COMPF:
402 cptrimmed (&cp, &ep, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill,
403 scanl + max - 1);
404 break;
405
406 case FT_LIT:
407 sp = fmt->f_text;
408 while( (c = *sp++) && cp < ep)
409 *cp++ = c;
410 break;
411 case FT_LITF:
412 sp = fmt->f_text;
413 ljust = 0;
414 i = fmt->f_width;
415 if (i < 0) {
416 i = -i;
417 ljust++; /* XXX should do something with this */
418 }
419 while( (c = *sp++) && --i >= 0 && cp < ep)
420 *cp++ = c;
421 while( --i >= 0 && cp < ep)
422 *cp++ = fmt->f_fill;
423 break;
424
425 case FT_STR:
426 cpstripped (&cp, &ep, scanl + max - 1, str);
427 break;
428 case FT_STRF:
429 cptrimmed (&cp, &ep, str, fmt->f_width, fmt->f_fill,
430 scanl + max - 1);
431 break;
432 case FT_STRLIT:
433 sp = str;
434 while ((c = *sp++) && cp < ep)
435 *cp++ = c;
436 break;
437 case FT_STRLITZ: {
438 size_t len = strlen (str);
439
440 /* Don't want to emit part of an escape sequence. So if
441 there isn't enough room in the buffer for the entire
442 string, skip it completely. */
443 if (cp - scanl + len + 1 < max) {
444 for (sp = str; *sp; *cp++ = *sp++) continue;
445
446 /* This string doesn't count against the width. So
447 increase ep the same amount as cp, only if the
448 scan buffer will always be large enough. */
449 if (ep - scanl + len + 1 < max) {
450 ep += len;
451 }
452 }
453
454 break;
455 }
456 case FT_STRFW:
457 adios (NULL, "internal error (FT_STRFW)");
458
459 case FT_NUM:
460 n = snprintf(cp, ep - cp + 1, "%d", value);
461 if (n >= 0) {
462 if (n >= ep - cp) {
463 cp = ep;
464 } else
465 cp += n;
466 }
467 break;
468 case FT_NUMF:
469 cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
470 break;
471
472 case FT_CHAR:
473 *cp++ = fmt->f_char;
474 break;
475
476 case FT_DONE:
477 goto finished;
478
479 case FT_IF_S:
480 if (!(value = (str && *str))) {
481 fmt += fmt->f_skip;
482 continue;
483 }
484 break;
485
486 case FT_IF_S_NULL:
487 if (!(value = (str == NULL || *str == 0))) {
488 fmt += fmt->f_skip;
489 continue;
490 }
491 break;
492
493 case FT_IF_V_EQ:
494 if (value != fmt->f_value) {
495 fmt += fmt->f_skip;
496 continue;
497 }
498 break;
499
500 case FT_IF_V_NE:
501 if (value == fmt->f_value) {
502 fmt += fmt->f_skip;
503 continue;
504 }
505 break;
506
507 case FT_IF_V_GT:
508 if (value <= fmt->f_value) {
509 fmt += fmt->f_skip;
510 continue;
511 }
512 break;
513
514 case FT_IF_MATCH:
515 if (!(value = (str && match (str, fmt->f_text)))) {
516 fmt += fmt->f_skip;
517 continue;
518 }
519 break;
520
521 case FT_V_MATCH:
522 if (str)
523 value = match (str, fmt->f_text);
524 else
525 value = 0;
526 break;
527
528 case FT_IF_AMATCH:
529 if (!(value = (str && uprf (str, fmt->f_text)))) {
530 fmt += fmt->f_skip;
531 continue;
532 }
533 break;
534
535 case FT_V_AMATCH:
536 value = uprf (str, fmt->f_text);
537 break;
538
539 case FT_S_NONNULL:
540 value = (str != NULL && *str != 0);
541 break;
542
543 case FT_S_NULL:
544 value = (str == NULL || *str == 0);
545 break;
546
547 case FT_V_EQ:
548 value = (fmt->f_value == value);
549 break;
550
551 case FT_V_NE:
552 value = (fmt->f_value != value);
553 break;
554
555 case FT_V_GT:
556 value = (fmt->f_value > value);
557 break;
558
559 case FT_GOTO:
560 fmt += fmt->f_skip;
561 continue;
562
563 case FT_NOP:
564 break;
565
566 case FT_LS_COMP:
567 str = fmt->f_comp->c_text;
568 break;
569 case FT_LS_LIT:
570 str = fmt->f_text;
571 break;
572 case FT_LS_GETENV:
573 if (!(str = getenv (fmt->f_text)))
574 str = "";
575 break;
576 case FT_LS_CFIND:
577 if (!(str = context_find (fmt->f_text)))
578 str = "";
579 break;
580
581 case FT_LS_DECODECOMP:
582 if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
583 str = buffer2;
584 else
585 str = fmt->f_comp->c_text;
586 break;
587
588 case FT_LS_DECODE:
589 if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
590 str = buffer2;
591 break;
592
593 case FT_LS_TRIM:
594 if (str) {
595 unsigned char *xp;
596
597 strncpy(buffer, str, sizeof(buffer));
598 buffer[sizeof(buffer)-1] = '\0';
599 str = buffer;
600 while (isspace(*str))
601 str++;
602 ljust = 0;
603 if ((i = fmt->f_width) < 0) {
604 i = -i;
605 ljust++;
606 }
607
608 if (!ljust && i > 0 && (int) strlen(str) > i)
609 str[i] = '\0';
610 xp = str;
611 xp += strlen(str) - 1;
612 while (xp > str && isspace(*xp))
613 *xp-- = '\0';
614 if (ljust && i > 0 && (int) strlen(str) > i)
615 str += strlen(str) - i;
616 }
617 break;
618
619 case FT_LV_COMPFLAG:
620 value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
621 break;
622 case FT_LV_COMP:
623 value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
624 break;
625 case FT_LV_LIT:
626 value = fmt->f_value;
627 break;
628 case FT_LV_DAT:
629 value = dat[fmt->f_value];
630 break;
631 case FT_LV_STRLEN:
632 if (str != NULL)
633 value = strlen(str);
634 else
635 value = 0;
636 break;
637 case FT_LV_CHAR_LEFT:
638 value = width - (cp - scanl);
639 break;
640 case FT_LV_PLUS_L:
641 value += fmt->f_value;
642 break;
643 case FT_LV_MINUS_L:
644 value = fmt->f_value - value;
645 break;
646 case FT_LV_DIVIDE_L:
647 if (fmt->f_value)
648 value = value / fmt->f_value;
649 else
650 value = 0;
651 break;
652 case FT_LV_MODULO_L:
653 if (fmt->f_value)
654 value = value % fmt->f_value;
655 else
656 value = 0;
657 break;
658 case FT_SAVESTR:
659 savestr = str;
660 break;
661
662 case FT_LV_SEC:
663 value = fmt->f_comp->c_tws->tw_sec;
664 break;
665 case FT_LV_MIN:
666 value = fmt->f_comp->c_tws->tw_min;
667 break;
668 case FT_LV_HOUR:
669 value = fmt->f_comp->c_tws->tw_hour;
670 break;
671 case FT_LV_MDAY:
672 value = fmt->f_comp->c_tws->tw_mday;
673 break;
674 case FT_LV_MON:
675 value = fmt->f_comp->c_tws->tw_mon + 1;
676 break;
677 case FT_LS_MONTH:
678 str = tw_moty[fmt->f_comp->c_tws->tw_mon];
679 break;
680 case FT_LS_LMONTH:
681 str = lmonth[fmt->f_comp->c_tws->tw_mon];
682 break;
683 case FT_LS_ZONE:
684 str = dtwszone (fmt->f_comp->c_tws);
685 break;
686 case FT_LV_YEAR:
687 value = fmt->f_comp->c_tws->tw_year;
688 break;
689 case FT_LV_WDAY:
690 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
691 set_dotw (tws);
692 value = tws->tw_wday;
693 break;
694 case FT_LS_DAY:
695 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
696 set_dotw (tws);
697 str = tw_dotw[tws->tw_wday];
698 break;
699 case FT_LS_WEEKDAY:
700 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
701 set_dotw (tws);
702 str = tw_ldotw[tws->tw_wday];
703 break;
704 case FT_LV_YDAY:
705 value = fmt->f_comp->c_tws->tw_yday;
706 break;
707 case FT_LV_ZONE:
708 value = fmt->f_comp->c_tws->tw_zone;
709 break;
710 case FT_LV_CLOCK:
711 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
712 value = dmktime(fmt->f_comp->c_tws);
713 break;
714 case FT_LV_RCLOCK:
715 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
716 value = dmktime(fmt->f_comp->c_tws);
717 value = time((time_t *) 0) - value;
718 break;
719 case FT_LV_DAYF:
720 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
721 set_dotw (tws);
722 switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
723 case TW_SEXP:
724 value = 1; break;
725 case TW_SIMP:
726 value = 0; break;
727 default:
728 value = -1; break;
729 }
730 case FT_LV_ZONEF:
731 if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
732 value = 1;
733 else
734 value = -1;
735 break;
736 case FT_LV_DST:
737 value = fmt->f_comp->c_tws->tw_flags & TW_DST;
738 break;
739 case FT_LS_822DATE:
740 str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
741 break;
742 case FT_LS_PRETTY:
743 str = dasctime (fmt->f_comp->c_tws, TW_NULL);
744 break;
745
746 case FT_LS_PERS:
747 str = fmt->f_comp->c_mn->m_pers;
748 break;
749 case FT_LS_MBOX:
750 str = fmt->f_comp->c_mn->m_mbox;
751 break;
752 case FT_LS_HOST:
753 str = fmt->f_comp->c_mn->m_host;
754 break;
755 case FT_LS_PATH:
756 str = fmt->f_comp->c_mn->m_path;
757 break;
758 case FT_LS_GNAME:
759 str = fmt->f_comp->c_mn->m_gname;
760 break;
761 case FT_LS_NOTE:
762 str = fmt->f_comp->c_mn->m_note;
763 break;
764 case FT_LS_822ADDR:
765 str = adrformat( fmt->f_comp->c_mn );
766 break;
767 case FT_LV_HOSTTYPE:
768 value = fmt->f_comp->c_mn->m_type;
769 break;
770 case FT_LV_INGRPF:
771 value = fmt->f_comp->c_mn->m_ingrp;
772 break;
773 case FT_LV_NOHOSTF:
774 value = fmt->f_comp->c_mn->m_nohost;
775 break;
776 case FT_LS_ADDR:
777 case FT_LS_FRIENDLY:
778 if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
779 str = fmt->f_comp->c_text;
780 break;
781 }
782 if (fmt->f_type == FT_LS_ADDR)
783 goto unfriendly;
784 if ((str = mn->m_pers) == NULL) {
785 if ((str = mn->m_note)) {
786 strncpy (buffer, str, sizeof(buffer));
787 buffer[sizeof(buffer)-1] = '\0';
788 str = buffer;
789 if (*str == '(')
790 str++;
791 sp = str + strlen(str) - 1;
792 if (*sp == ')') {
793 *sp-- = '\0';
794 while (sp >= str)
795 if (*sp == ' ')
796 *sp-- = '\0';
797 else
798 break;
799 }
800 } else if (!(str = get_x400_friendly (mn->m_mbox,
801 buffer, sizeof(buffer)))) {
802 unfriendly: ;
803 switch (mn->m_type) {
804 case LOCALHOST:
805 str = mn->m_mbox;
806 break;
807 case UUCPHOST:
808 snprintf (buffer, sizeof(buffer), "%s!%s",
809 mn->m_host, mn->m_mbox);
810 str = buffer;
811 break;
812 default:
813 if (mn->m_mbox) {
814 snprintf (buffer, sizeof(buffer), "%s@%s",
815 mn->m_mbox, mn->m_host);
816 str= buffer;
817 }
818 else
819 str = mn->m_text;
820 break;
821 }
822 }
823 }
824 break;
825
826
827 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
828 case FT_LS_UNQUOTE:
829 if (str) {
830 int m;
831 strncpy(buffer, str, sizeof(buffer));
832 /* strncpy doesn't NUL-terminate if it fills the buffer */
833 buffer[sizeof(buffer)-1] = '\0';
834 str = buffer;
835
836 /* we will parse from buffer to buffer2 */
837 n = 0; /* n is the input position in str */
838 m = 0; /* m is the ouput position in buffer2 */
839
840 while ( str[n] != '\0') {
841 switch ( str[n] ) {
842 case '\\':
843 n++;
844 if ( str[n] != '\0')
845 buffer2[m++] = str[n++];
846 break;
847 case '"':
848 n++;
849 break;
850 default:
851 buffer2[m++] = str[n++];
852 break;
853 }
854 }
855 buffer2[m] = '\0';
856 str = buffer2;
857 }
858 break;
859
860 case FT_LOCALDATE:
861 comp = fmt->f_comp;
862 if ((t = comp->c_tws->tw_clock) == 0)
863 t = dmktime(comp->c_tws);
864 tws = dlocaltime(&t);
865 *comp->c_tws = *tws;
866 break;
867
868 case FT_GMTDATE:
869 comp = fmt->f_comp;
870 if ((t = comp->c_tws->tw_clock) == 0)
871 t = dmktime(comp->c_tws);
872 tws = dgmtime(&t);
873 *comp->c_tws = *tws;
874 break;
875
876 case FT_PARSEDATE:
877 comp = fmt->f_comp;
878 if (comp->c_flags & CF_PARSED)
879 break;
880 if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
881 *comp->c_tws = *tws;
882 comp->c_flags &= ~CF_TRUE;
883 } else if ((comp->c_flags & CF_DATEFAB) == 0) {
884 memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
885 comp->c_flags = CF_TRUE;
886 }
887 comp->c_flags |= CF_PARSED;
888 break;
889
890 case FT_FORMATADDR:
891 /* hook for custom address list formatting (see replsbr.c) */
892 str = formataddr (savestr, str);
893 break;
894
895 case FT_CONCATADDR:
896 /* The same as formataddr, but doesn't do duplicate suppression */
897 str = concataddr (savestr, str);
898 break;
899
900 case FT_PUTADDR:
901 /* output the str register as an address component,
902 * splitting it into multiple lines if necessary. The
903 * value reg. contains the max line length. The lit.
904 * field may contain a string to prepend to the result
905 * (e.g., "To: ")
906 */
907 {
908 unsigned char *lp;
909 char *lastb;
910 int indent, wid, len;
911
912 lp = str;
913 wid = value;
914 len = strlen (str);
915 sp = fmt->f_text;
916 indent = strlen (sp);
917 wid -= indent;
918 if (wid <= 0) {
919 adios(NULL, "putaddr -- num register (%d) must be greater "
920 "than label width (%d)", value, indent);
921 }
922 while( (c = *sp++) && cp < ep)
923 *cp++ = c;
924 while (len > wid) {
925 /* try to break at a comma; failing that, break at a
926 * space.
927 */
928 lastb = 0; sp = lp + wid;
929 while (sp > lp && (c = *--sp) != ',') {
930 if (! lastb && isspace(c))
931 lastb = sp - 1;
932 }
933 if (sp == lp) {
934 if (! (sp = lastb)) {
935 sp = lp + wid - 1;
936 while (*sp && *sp != ',' && !isspace(*sp))
937 sp++;
938 if (*sp != ',')
939 sp--;
940 }
941 }
942 len -= sp - lp + 1;
943 while (cp < ep && lp <= sp)
944 *cp++ = *lp++;
945 while (isspace(*lp))
946 lp++, len--;
947 if (*lp) {
948 if (cp < ep)
949 *cp++ = '\n';
950 for (i=indent; cp < ep && i > 0; i--)
951 *cp++ = ' ';
952 }
953 }
954 cpstripped (&cp, &ep, scanl + max - 1, lp);
955 }
956 break;
957
958 case FT_PARSEADDR:
959 comp = fmt->f_comp;
960 if (comp->c_flags & CF_PARSED)
961 break;
962 if (comp->c_mn != &fmt_mnull)
963 mnfree (comp->c_mn);
964 if ((sp = comp->c_text) && (sp = getname(sp)) &&
965 (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
966 comp->c_mn = mn;
967 while (getname(""))
968 ;
969 comp->c_flags |= CF_PARSED;
970 } else {
971 while (getname("")) /* XXX */
972 ;
973 comp->c_mn = &fmt_mnull;
974 }
975 break;
976
977 case FT_MYMBOX:
978 /*
979 * if there's no component, we say true. Otherwise we
980 * say "true" only if we can parse the address and it
981 * matches one of our addresses.
982 */
983 comp = fmt->f_comp;
984 if (comp->c_mn != &fmt_mnull)
985 mnfree (comp->c_mn);
986 if ((sp = comp->c_text) && (sp = getname(sp)) &&
987 (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
988 comp->c_mn = mn;
989 if (ismymbox(mn))
990 comp->c_flags |= CF_TRUE;
991 else
992 comp->c_flags &= ~CF_TRUE;
993 while ((sp = getname(sp)))
994 if ((comp->c_flags & CF_TRUE) == 0 &&
995 (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
996 if (ismymbox(mn))
997 comp->c_flags |= CF_TRUE;
998 } else {
999 while (getname("")) /* XXX */
1000 ;
1001 if (comp->c_text == 0)
1002 comp->c_flags |= CF_TRUE;
1003 else
1004 comp->c_flags &= ~CF_TRUE;
1005 comp->c_mn = &fmt_mnull;
1006 }
1007 break;
1008 }
1009 fmt++;
1010 }
1011
1012 /* Emit any trailing sequences of zero display length. */
1013 while (fmt->f_type != FT_DONE) {
1014 if (fmt->f_type == FT_LS_LIT) {
1015 str = fmt->f_text;
1016 } else if (fmt->f_type == FT_STRLITZ) {
1017 /* Don't want to emit part of an escape sequence. So if
1018 there isn't enough room in the buffer for the entire
1019 string, skip it completely. Need room for null
1020 terminator, and maybe trailing newline (added below). */
1021 if (cp - scanl + strlen (str) + 1 < max) {
1022 for (sp = str; *sp; *cp++ = *sp++) continue;
1023 }
1024 }
1025 fmt++;
1026 }
1027
1028 finished:;
1029 if (cp > scanl && cp[-1] != '\n') {
1030 if (cp - scanl < (int) max - 1) {
1031 *cp++ = '\n';
1032 } else {
1033 cp[-1] = '\n';
1034 }
1035 }
1036 *cp = '\0';
1037 return ((struct format *)0);
1038 }