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