]> diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
Added mhfixmsg -[no]decodeheaderfieldbodies switches.
[nmh] / sbr / fmt_scan.c
1 /* fmt_scan.c -- format string interpretation
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 *
7 * This is the engine that processes the format instructions created by
8 * fmt_compile (found in fmt_compile.c).
9 */
10
11 #include "h/mh.h"
12 #include "fmt_addr.h"
13 #include "dtime.h"
14 #include "strindex.h"
15 #include "fmt_rfc2047.h"
16 #include "uprf.h"
17 #include "context_find.h"
18 #include "error.h"
19 #include "h/addrsbr.h"
20 #include "h/fmt_scan.h"
21 #include "h/tws.h"
22 #include "h/fmt_compile.h"
23 #include "h/utils.h"
24 #include "unquote.h"
25
26 #ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
28 #endif
29 #include <time.h>
30 #ifdef MULTIBYTE_SUPPORT
31 # include <wctype.h>
32 # include <wchar.h>
33 #endif
34
35 struct mailname fmt_mnull = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
36 NULL, NULL };
37
38 /*
39 * static prototypes
40 */
41 static int match (char *, char *) PURE;
42 static char *get_x400_friendly (char *, char *, int);
43 static int get_x400_comp (char *, char *, char *, int);
44
45
46 /*
47 * test if string "sub" appears anywhere in
48 * string "str" (case insensitive).
49 */
50
51 static int
52 match (char *str, char *sub)
53 {
54 int c1, c2;
55 char *s1, *s2;
56
57 while ((c1 = *sub)) {
58 c1 = tolower((unsigned char)c1);
59 while ((c2 = *str++) && c1 != tolower((unsigned char)c2))
60 ;
61 if (! c2)
62 return 0;
63 s1 = sub + 1; s2 = str;
64 while ((c1 = *s1++) &&
65 tolower((unsigned char)c1) == tolower((unsigned char)(c2 = *s2++)))
66 ;
67 if (! c1)
68 return 1;
69 }
70 return 1;
71 }
72
73 /* cpnumber formats num as a signed decimal,
74 * appending it to dest if the result doesn't exceed max.
75 * The absolute value of width is the minimum width to produce.
76 * A smaller string is padded;
77 * on the left if width is positive, else the right.
78 * Left-padding uses fill. It is either ' ' or '0'.
79 * Right-padding is always with space. */
80 void
81 cpnumber(charstring_t dest, int num, int width, char fill, size_t max)
82 {
83 if (width == 0 || width == INT_MIN) {
84 return;
85 }
86
87 bool padright = width < 0;
88 if (padright) {
89 width = -width; /* Can't overflow after above check. */
90 }
91 size_t w = width;
92 if (w > max) {
93 return; /* The padded result can't fit. */
94 }
95 if (num < 0 && w == 1) {
96 return; /* No room for `-' and a digit or `?'. */
97 }
98
99 char *s = m_str(num);
100 size_t len = strlen(s);
101 if (len == w) {
102 charstring_append_cstring(dest, s);
103 return;
104 }
105
106 bool neg = *s == '-';
107 if (len < w) {
108 if (padright) {
109 charstring_append_cstring(dest, s);
110 while (len++ < w) {
111 charstring_push_back(dest, ' ');
112 }
113 return;
114 }
115
116 if (neg && fill == '0') {
117 charstring_push_back(dest, *s++);
118 }
119 while (len++ < w) {
120 charstring_push_back(dest, fill);
121 }
122 charstring_append_cstring(dest, s);
123 return;
124 }
125
126 /* Transform 1234567 -> 1234?67 and -1234567 -> 1234-?7. */
127 char *news = s + len - w;
128 if (neg) {
129 *news = '-';
130 }
131 news[neg] = '?';
132 charstring_append_cstring(dest, news);
133 }
134
135 /*
136 * copy string from str to dest padding with the fill character to a
137 * size of wid characters. if wid is negative, the string is right
138 * aligned no more than max characters are copied
139 */
140 void
141 cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max)
142 {
143 int remaining; /* remaining output width available */
144 bool rjust;
145 struct charstring *trimmed;
146 size_t end; /* number of input bytes remaining in str */
147 #ifdef MULTIBYTE_SUPPORT
148 int char_len; /* bytes in current character */
149 int w;
150 wchar_t wide_char;
151 char *altstr = NULL;
152 #endif
153 char *sp; /* current position in source string */
154
155 /* get alignment */
156 rjust = false;
157 if ((remaining = wid) < 0) {
158 remaining = -remaining;
159 rjust = true;
160 }
161 if (remaining > (int) max) { remaining = max; }
162
163 trimmed = rjust ? charstring_create(remaining) : dest;
164
165 bool prevCtrl = true;
166 if ((sp = str)) {
167 #ifdef MULTIBYTE_SUPPORT
168 if (mbtowc(NULL, NULL, 0)) {} /* reset shift state */
169 #endif
170 end = strlen(str);
171 while (*sp && remaining > 0 && end > 0) {
172 #ifdef MULTIBYTE_SUPPORT
173 char_len = mbtowc(&wide_char, sp, end);
174
175 /*
176 * See the relevant comments in cpstripped() to explain what's
177 * going on here; we want to handle the case where we get
178 * characters that mbtowc() cannot handle
179 */
180
181 if (char_len < 0) {
182 altstr = "?";
183 char_len = mbtowc(&wide_char, altstr, 1);
184 }
185
186 if (char_len <= 0) {
187 break;
188 }
189
190 w = wcwidth(wide_char);
191
192 /* If w > remaining, w must be positive. */
193 if (w > remaining) {
194 break;
195 }
196
197 end -= char_len;
198
199 if (iswcntrl(wide_char) || iswspace(wide_char)) {
200 sp += char_len;
201 #else
202 int c;
203 end--;
204 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
205 intentionally warns if they are passed a char. */
206 c = (unsigned char) *sp;
207 if (iscntrl(c) || isspace(c)) {
208 sp++;
209 #endif
210 if (!prevCtrl) {
211 charstring_push_back (trimmed, ' ');
212 remaining--;
213 }
214
215 prevCtrl = true;
216 continue;
217 }
218 prevCtrl = false;
219
220 #ifdef MULTIBYTE_SUPPORT
221 if (w >= 0 && remaining >= w) {
222 charstring_push_back_chars (trimmed, altstr ? altstr : sp,
223 char_len, w);
224 remaining -= w;
225 altstr = NULL;
226 }
227 sp += char_len;
228 #else
229 charstring_push_back (trimmed, *sp++);
230 remaining--;
231 #endif
232 }
233 }
234
235 while (remaining-- > 0) {
236 charstring_push_back(dest, fill);
237 }
238
239 if (rjust) {
240 charstring_append(dest, trimmed);
241 charstring_free(trimmed);
242 }
243 }
244
245 #ifdef MULTIBYTE_SUPPORT
246 static void
247 cpstripped (charstring_t dest, size_t max, char *str)
248 {
249 static bool deja_vu;
250 static char *oddchar;
251 static size_t oddlen;
252 static char *spacechar;
253 static size_t spacelen;
254 char *end;
255 bool squash;
256 char *src;
257 int srclen;
258 wchar_t rune;
259 int w;
260
261 if (!deja_vu) {
262 size_t two;
263
264 deja_vu = true;
265
266 two = MB_CUR_MAX * 2; /* Varies at run-time. */
267
268 oddchar = mh_xmalloc(two);
269 oddlen = wcstombs(oddchar, L"?", two);
270 assert(oddlen > 0);
271
272 assert(wcwidth(L' ') == 1); /* Need to pad in ones. */
273 spacechar = mh_xmalloc(two);
274 spacelen = wcstombs(spacechar, L" ", two);
275 assert(spacelen > 0);
276 }
277
278 if (!str)
279 return; /* It's unclear why no padding in this case. */
280 end = str + strlen(str);
281
282 if (mbtowc(NULL, NULL, 0))
283 {} /* Reset shift state. */
284
285 squash = true; /* Trim `space' or `cntrl' from the start. */
286 while (max) {
287 if (!*str)
288 return; /* It's unclear why no padding in this case. */
289
290 srclen = mbtowc(&rune, str, end - str);
291 if (srclen == -1) {
292 /* Invalid rune, or not enough bytes to finish it. */
293 rune = L'?';
294 src = oddchar;
295 srclen = oddlen;
296 str++; /* Skip one byte. */
297 } else {
298 src = str;
299 str += srclen;
300 }
301
302 if (iswspace(rune) || iswcntrl(rune)) {
303 if (squash)
304 continue; /* Amidst a run of these. */
305 rune = L' ';
306 src = spacechar;
307 srclen = spacelen;
308 squash = true;
309 } else
310 squash = false;
311
312 w = wcwidth(rune);
313 if (w == -1) {
314 rune = L'?';
315 w = wcwidth(rune);
316 assert(w != -1);
317 src = oddchar;
318 srclen = oddlen;
319 }
320
321 if ((size_t)w > max) {
322 /* No room for rune; pad. */
323 while (max--)
324 charstring_push_back_chars(dest, spacechar, spacelen, 1);
325 return;
326 }
327
328 charstring_push_back_chars(dest, src, srclen, w);
329 max -= w;
330 }
331 }
332 #endif
333
334 #ifndef MULTIBYTE_SUPPORT
335 static void
336 cpstripped (charstring_t dest, size_t max, char *str)
337 {
338 bool squash;
339 int c;
340
341 if (!str)
342 return;
343
344 squash = true; /* Strip leading cases. */
345 while (max--) {
346 c = (unsigned char)*str++;
347 if (!c)
348 return;
349
350 if (isspace(c) || iscntrl(c)) {
351 if (squash)
352 continue;
353 c = ' ';
354 squash = true;
355 } else
356 squash = false;
357
358 charstring_push_back(dest, (char)c);
359 }
360 }
361 #endif
362
363 static char *lmonth[] = { "January", "February","March", "April",
364 "May", "June", "July", "August",
365 "September","October", "November","December" };
366
367 static char *
368 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
369 {
370 char given[BUFSIZ], surname[BUFSIZ];
371
372 if (mbox == NULL)
373 return NULL;
374 if (*mbox == '"')
375 mbox++;
376 if (*mbox != '/')
377 return NULL;
378
379 if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
380 for (mbox = buffer; (mbox = strchr(mbox, '.')); )
381 *mbox++ = ' ';
382
383 return buffer;
384 }
385
386 if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
387 return NULL;
388
389 if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
390 snprintf (buffer, buffer_len, "%s %s", given, surname);
391 else
392 snprintf (buffer, buffer_len, "%s", surname);
393
394 return buffer;
395 }
396
397 static int
398 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
399 {
400 int idx;
401 char *cp;
402
403 if ((idx = stringdex (key, mbox)) < 0
404 || !(cp = strchr(mbox += idx + strlen (key), '/')))
405 return 0;
406
407 snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
408 return 1;
409 }
410
411 struct format *
412 fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
413 struct fmt_callbacks *callbacks)
414 {
415 char *sp;
416 char *savestr, *str;
417 char buffer[NMH_BUFSIZ], buffer2[NMH_BUFSIZ];
418 int i, c;
419 bool rjust;
420 int value;
421 time_t t;
422 size_t max;
423 struct format *fmt;
424 struct comp *comp;
425 struct tws *tws;
426 struct mailname *mn;
427
428 /*
429 * max is the same as width, but unsigned so comparisons
430 * with charstring_chars() won't raise compile warnings.
431 */
432 max = width;
433 savestr = str = NULL;
434 value = 0;
435
436 for (fmt = format; fmt->f_type != FT_DONE; fmt++)
437 switch (fmt->f_type) {
438 case FT_PARSEADDR:
439 case FT_PARSEDATE:
440 fmt->f_comp->c_flags &= ~CF_PARSED;
441 break;
442 case FT_COMP:
443 case FT_COMPF:
444 case FT_LS_COMP:
445 case FT_LS_DECODECOMP:
446 /*
447 * Trim these components of any newlines.
448 *
449 * But don't trim the "body" and "text" components.
450 */
451
452 comp = fmt->f_comp;
453
454 if (! (comp->c_flags & CF_TRIMMED) && comp->c_text &&
455 (i = strlen(comp->c_text)) > 0) {
456 if (comp->c_text[i - 1] == '\n' &&
457 strcmp(comp->c_name, "body") != 0 &&
458 strcmp(comp->c_name, "text") != 0)
459 comp->c_text[i - 1] = '\0';
460 comp->c_flags |= CF_TRIMMED;
461 }
462 break;
463 }
464
465 fmt = format;
466
467 for ( ; charstring_chars (scanlp) < max; ) {
468 switch (fmt->f_type) {
469
470 case FT_COMP:
471 cpstripped (scanlp, max - charstring_chars (scanlp),
472 fmt->f_comp->c_text);
473 break;
474 case FT_COMPF:
475 cptrimmed (scanlp, fmt->f_comp->c_text, fmt->f_width,
476 fmt->f_fill, max - charstring_chars (scanlp));
477 break;
478
479 case FT_LIT:
480 sp = fmt->f_text;
481 while ((c = *sp++) && charstring_chars (scanlp) < max) {
482 charstring_push_back (scanlp, c);
483 }
484 break;
485 case FT_LITF:
486 sp = fmt->f_text;
487 rjust = false;
488 i = fmt->f_width;
489 if (i < 0) {
490 i = -i;
491 rjust = true; /* XXX should do something with this */
492 }
493 while ((c = *sp++) && --i >= 0 && charstring_chars (scanlp) < max) {
494 charstring_push_back (scanlp, c);
495 }
496 while (--i >= 0 && charstring_chars (scanlp) < max) {
497 charstring_push_back (scanlp, fmt->f_fill);
498 }
499 break;
500
501 case FT_STR:
502 cpstripped (scanlp, max - charstring_chars (scanlp), str);
503 break;
504 case FT_STRF:
505 cptrimmed (scanlp, str, fmt->f_width, fmt->f_fill,
506 max - charstring_chars (scanlp));
507 break;
508 case FT_STRLIT:
509 if (str) {
510 sp = str;
511 while ((c = *sp++) && charstring_chars (scanlp) < max) {
512 charstring_push_back (scanlp, c);
513 }
514 }
515 break;
516 case FT_STRLITZ:
517 if (str) charstring_push_back_chars (scanlp, str, strlen (str), 0);
518 break;
519 case FT_STRFW:
520 die("internal error (FT_STRFW)");
521
522 case FT_NUM: {
523 int num = value;
524 unsigned int wid;
525
526 for (wid = num <= 0; num; ++wid, num /= 10) {}
527 cpnumber (scanlp, value, wid, ' ',
528 max - charstring_chars (scanlp));
529 break;
530 }
531 case FT_LS_KILO:
532 case FT_LS_KIBI:
533 {
534 char *unitcp;
535 unsigned int whole, tenths;
536 unsigned int scale = 0;
537 unsigned int val = (unsigned int)value;
538 char *kibisuff = NULL;
539
540 switch (fmt->f_type) {
541 case FT_LS_KILO: scale = 1000; kibisuff = ""; break;
542 case FT_LS_KIBI: scale = 1024; kibisuff = "i"; break;
543 }
544
545 if (val < scale) {
546 snprintf(buffer, sizeof(buffer), "%u", val);
547 } else {
548 /* To prevent divide by 0, found by clang static
549 analyzer. */
550 if (scale == 0) { scale = 1; }
551
552 /* find correct scale for size (Kilo/Mega/Giga/Tera) */
553 for (unitcp = "KMGT"; val > (scale * scale); val /= scale) {
554 if (!*++unitcp)
555 break;
556 }
557
558 if (!*unitcp) {
559 strcpy(buffer, "huge");
560 } else {
561 /* val is scale times too big. we want tenths */
562 val *= 10;
563
564 /* round up */
565 val += (scale - 1);
566 val /= scale;
567
568 whole = val / 10;
569 tenths = val - (whole * 10);
570
571 if (tenths) {
572 snprintf(buffer, sizeof(buffer), "%u.%u%c%s",
573 whole, tenths, *unitcp, kibisuff);
574 } else {
575 snprintf(buffer, sizeof(buffer), "%u%c%s",
576 whole, *unitcp, kibisuff);
577 }
578 }
579 }
580 str = buffer;
581 }
582 break;
583 case FT_NUMF:
584 cpnumber (scanlp, value, fmt->f_width, fmt->f_fill,
585 max - charstring_chars (scanlp));
586 break;
587
588 case FT_CHAR:
589 charstring_push_back (scanlp, fmt->f_char);
590 break;
591
592 case FT_DONE:
593 if (callbacks && callbacks->trace_func)
594 callbacks->trace_func(callbacks->trace_context, fmt, value,
595 str, charstring_buffer (scanlp));
596 goto finished;
597
598 case FT_IF_S:
599 if (!(value = (str && *str))) {
600 if (callbacks && callbacks->trace_func)
601 callbacks->trace_func(callbacks->trace_context, fmt, value,
602 str, charstring_buffer (scanlp));
603 fmt += fmt->f_skip;
604 continue;
605 }
606 break;
607
608 case FT_IF_S_NULL:
609 if (!(value = (str == NULL || *str == 0))) {
610 if (callbacks && callbacks->trace_func)
611 callbacks->trace_func(callbacks->trace_context, fmt, value,
612 str, charstring_buffer (scanlp));
613 fmt += fmt->f_skip;
614 continue;
615 }
616 break;
617
618 case FT_IF_V_EQ:
619 if (value != fmt->f_value) {
620 if (callbacks && callbacks->trace_func)
621 callbacks->trace_func(callbacks->trace_context, fmt, value,
622 str, charstring_buffer (scanlp));
623 fmt += fmt->f_skip;
624 continue;
625 }
626 break;
627
628 case FT_IF_V_NE:
629 if (value == fmt->f_value) {
630 if (callbacks && callbacks->trace_func)
631 callbacks->trace_func(callbacks->trace_context, fmt, value,
632 str, charstring_buffer (scanlp));
633 fmt += fmt->f_skip;
634 continue;
635 }
636 break;
637
638 case FT_IF_V_GT:
639 if (value <= fmt->f_value) {
640 if (callbacks && callbacks->trace_func)
641 callbacks->trace_func(callbacks->trace_context, fmt, value,
642 str, charstring_buffer (scanlp));
643 fmt += fmt->f_skip;
644 continue;
645 }
646 break;
647
648 case FT_IF_MATCH:
649 if (!(value = (str && match (str, fmt->f_text)))) {
650 if (callbacks && callbacks->trace_func)
651 callbacks->trace_func(callbacks->trace_context, fmt, value,
652 str, charstring_buffer (scanlp));
653 fmt += fmt->f_skip;
654 continue;
655 }
656 break;
657
658 case FT_V_MATCH:
659 if (str)
660 value = match (str, fmt->f_text);
661 else
662 value = 0;
663 break;
664
665 case FT_IF_AMATCH:
666 if (!(value = (str && uprf (str, fmt->f_text)))) {
667 if (callbacks && callbacks->trace_func)
668 callbacks->trace_func(callbacks->trace_context, fmt, value,
669 str, charstring_buffer (scanlp));
670 fmt += fmt->f_skip;
671 continue;
672 }
673 break;
674
675 case FT_V_AMATCH:
676 value = uprf (str, fmt->f_text);
677 break;
678
679 case FT_S_NONNULL:
680 value = (str != NULL && *str != 0);
681 break;
682
683 case FT_S_NULL:
684 value = (str == NULL || *str == 0);
685 break;
686
687 case FT_V_EQ:
688 value = (fmt->f_value == value);
689 break;
690
691 case FT_V_NE:
692 value = (fmt->f_value != value);
693 break;
694
695 case FT_V_GT:
696 value = (fmt->f_value > value);
697 break;
698
699 case FT_GOTO:
700 if (callbacks && callbacks->trace_func)
701 callbacks->trace_func(callbacks->trace_context, fmt, value,
702 str, charstring_buffer (scanlp));
703 fmt += fmt->f_skip;
704 continue;
705
706 case FT_NOP:
707 break;
708
709 case FT_LS_COMP:
710 str = fmt->f_comp->c_text;
711 break;
712 case FT_LS_LIT:
713 str = fmt->f_text;
714 break;
715 case FT_LS_GETENV:
716 if (!(str = getenv (fmt->f_text)))
717 str = "";
718 break;
719 case FT_LS_CFIND:
720 if (!(str = context_find (fmt->f_text)))
721 str = "";
722 break;
723
724 case FT_LS_DECODECOMP:
725 if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
726 str = buffer2;
727 else
728 str = fmt->f_comp->c_text;
729 break;
730
731 case FT_LS_DECODE:
732 if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
733 str = buffer2;
734 break;
735
736 case FT_LS_TRIM:
737 if (str) {
738 char *xp;
739
740 if (str != buffer)
741 strncpy(buffer, str, sizeof(buffer));
742 buffer[sizeof(buffer)-1] = '\0';
743 str = buffer;
744 while (isspace((unsigned char) *str))
745 str++;
746 rjust = false;
747 if ((i = fmt->f_width) < 0) {
748 i = -i;
749 rjust = true;
750 }
751
752 if (!rjust && i > 0 && (int) strlen(str) > i)
753 str[i] = '\0';
754 xp = str;
755 xp += strlen(str) - 1;
756 while (xp > str && isspace((unsigned char) *xp))
757 *xp-- = '\0';
758 if (rjust && i > 0 && (int) strlen(str) > i)
759 str += strlen(str) - i;
760 }
761 break;
762
763 case FT_LV_COMPFLAG:
764 value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
765 break;
766 case FT_LV_COMP:
767 value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
768 break;
769 case FT_LV_LIT:
770 value = fmt->f_value;
771 break;
772 case FT_LV_DAT:
773 value = dat[fmt->f_value];
774 break;
775 case FT_LV_STRLEN:
776 if (str != NULL)
777 value = strlen(str);
778 else
779 value = 0;
780 break;
781 case FT_LV_CHAR_LEFT:
782 value = max - charstring_bytes (scanlp);
783 break;
784 case FT_LV_PLUS_L:
785 value += fmt->f_value;
786 break;
787 case FT_LV_MINUS_L:
788 value = fmt->f_value - value;
789 break;
790 case FT_LV_MULTIPLY_L:
791 value *= fmt->f_value;
792 break;
793 case FT_LV_DIVIDE_L:
794 if (fmt->f_value)
795 value /= fmt->f_value;
796 else
797 value = 0;
798 break;
799 case FT_LV_MODULO_L:
800 if (fmt->f_value)
801 value %= fmt->f_value;
802 else
803 value = 0;
804 break;
805 case FT_SAVESTR:
806 savestr = str;
807 break;
808
809 case FT_LV_SEC:
810 value = fmt->f_comp->c_tws->tw_sec;
811 break;
812 case FT_LV_MIN:
813 value = fmt->f_comp->c_tws->tw_min;
814 break;
815 case FT_LV_HOUR:
816 value = fmt->f_comp->c_tws->tw_hour;
817 break;
818 case FT_LV_MDAY:
819 value = fmt->f_comp->c_tws->tw_mday;
820 break;
821 case FT_LV_MON:
822 value = fmt->f_comp->c_tws->tw_mon + 1;
823 break;
824 case FT_LS_MONTH:
825 str = tw_moty[fmt->f_comp->c_tws->tw_mon];
826 break;
827 case FT_LS_LMONTH:
828 str = lmonth[fmt->f_comp->c_tws->tw_mon];
829 break;
830 case FT_LS_ZONE:
831 str = dtwszone (fmt->f_comp->c_tws);
832 break;
833 case FT_LV_YEAR:
834 value = fmt->f_comp->c_tws->tw_year;
835 break;
836 case FT_LV_WDAY:
837 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
838 set_dotw (tws);
839 value = tws->tw_wday;
840 break;
841 case FT_LS_DAY:
842 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
843 set_dotw (tws);
844 str = tw_dotw[tws->tw_wday];
845 break;
846 case FT_LS_WEEKDAY:
847 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
848 set_dotw (tws);
849 str = tw_ldotw[tws->tw_wday];
850 break;
851 case FT_LV_YDAY:
852 value = fmt->f_comp->c_tws->tw_yday;
853 break;
854 case FT_LV_ZONE:
855 value = fmt->f_comp->c_tws->tw_zone;
856 break;
857 case FT_LV_CLOCK:
858 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
859 value = dmktime(fmt->f_comp->c_tws);
860 break;
861 case FT_LV_RCLOCK:
862 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
863 value = dmktime(fmt->f_comp->c_tws);
864 value = time(NULL) - value;
865 break;
866 case FT_LV_DAYF:
867 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
868 set_dotw (tws);
869 switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
870 case TW_SEXP:
871 value = 1; break;
872 case TW_SIMP:
873 value = 0; break;
874 default:
875 value = -1; break;
876 }
877 break;
878 case FT_LV_ZONEF:
879 if (fmt->f_comp->c_tws->tw_flags & TW_SZEXP)
880 value = 1;
881 else
882 value = -1;
883 break;
884 case FT_LV_DST:
885 value = fmt->f_comp->c_tws->tw_flags & TW_DST ? 1 : 0;
886 break;
887 case FT_LS_822DATE:
888 str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
889 break;
890 case FT_LS_PRETTY:
891 str = dasctime (fmt->f_comp->c_tws, TW_NULL);
892 break;
893
894 case FT_LS_PERS:
895 str = fmt->f_comp->c_mn->m_pers;
896 break;
897 case FT_LS_MBOX:
898 str = fmt->f_comp->c_mn->m_mbox;
899 break;
900 case FT_LS_HOST:
901 str = fmt->f_comp->c_mn->m_host;
902 break;
903 case FT_LS_PATH:
904 str = fmt->f_comp->c_mn->m_path;
905 break;
906 case FT_LS_GNAME:
907 str = fmt->f_comp->c_mn->m_gname;
908 break;
909 case FT_LS_NOTE:
910 str = fmt->f_comp->c_mn->m_note;
911 break;
912 case FT_LS_822ADDR:
913 str = adrformat( fmt->f_comp->c_mn );
914 break;
915 case FT_LV_HOSTTYPE:
916 value = fmt->f_comp->c_mn->m_type;
917 break;
918 case FT_LV_INGRPF:
919 value = fmt->f_comp->c_mn->m_ingrp;
920 break;
921 case FT_LV_NOHOSTF:
922 value = fmt->f_comp->c_mn->m_nohost;
923 break;
924 case FT_LS_ADDR:
925 case FT_LS_FRIENDLY:
926 if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
927 str = fmt->f_comp->c_text;
928 break;
929 }
930 if (fmt->f_type == FT_LS_ADDR)
931 goto unfriendly;
932 if ((str = mn->m_pers) == NULL) {
933 if ((str = mn->m_note)) {
934 if (str != buffer)
935 strncpy (buffer, str, sizeof(buffer));
936 buffer[sizeof(buffer)-1] = '\0';
937 str = buffer;
938 if (*str == '(')
939 str++;
940 sp = str + strlen(str) - 1;
941 if (*sp == ')') {
942 *sp-- = '\0';
943 while (sp >= str)
944 if (*sp == ' ')
945 *sp-- = '\0';
946 else
947 break;
948 }
949 } else if (!(str = get_x400_friendly (mn->m_mbox,
950 buffer, sizeof(buffer)))) {
951 unfriendly:
952 switch (mn->m_type) {
953 case LOCALHOST:
954 str = mn->m_mbox;
955 break;
956 case UUCPHOST:
957 snprintf (buffer, sizeof(buffer), "%s!%s",
958 mn->m_host, mn->m_mbox);
959 str = buffer;
960 break;
961 default:
962 if (mn->m_mbox) {
963 snprintf (buffer, sizeof(buffer), "%s@%s",
964 mn->m_mbox, mn->m_host);
965 str= buffer;
966 }
967 else
968 str = mn->m_text;
969 break;
970 }
971 }
972 }
973 break;
974
975
976 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
977 case FT_LS_UNQUOTE:
978 if (str) {
979 if (str != buffer)
980 strncpy(buffer, str, sizeof(buffer));
981 /* strncpy doesn't NUL-terminate if it fills the buffer */
982 buffer[sizeof(buffer)-1] = '\0';
983 unquote_string(buffer, buffer2);
984 str = buffer2;
985 }
986 break;
987
988 case FT_LOCALDATE:
989 comp = fmt->f_comp;
990 if ((t = comp->c_tws->tw_clock) == 0)
991 t = dmktime(comp->c_tws);
992 tws = dlocaltime(&t);
993 *comp->c_tws = *tws;
994 break;
995
996 case FT_GMTDATE:
997 comp = fmt->f_comp;
998 if ((t = comp->c_tws->tw_clock) == 0)
999 t = dmktime(comp->c_tws);
1000 tws = dgmtime(&t);
1001 *comp->c_tws = *tws;
1002 break;
1003
1004 case FT_PARSEDATE:
1005 comp = fmt->f_comp;
1006 if (comp->c_flags & CF_PARSED)
1007 break;
1008 if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
1009 *comp->c_tws = *tws;
1010 comp->c_flags &= ~CF_TRUE;
1011 } else if ((comp->c_flags & CF_DATEFAB) == 0) {
1012 ZERO(comp->c_tws);
1013 comp->c_flags = CF_TRUE;
1014 }
1015 comp->c_flags |= CF_PARSED;
1016 break;
1017
1018 case FT_FORMATADDR:
1019 /* hook for custom address list formatting (see replsbr.c) */
1020 if (callbacks && callbacks->formataddr)
1021 str = callbacks->formataddr (savestr, str);
1022 else
1023 str = formataddr (savestr, str);
1024 break;
1025
1026 case FT_CONCATADDR:
1027 /* The same as formataddr, but doesn't do duplicate suppression */
1028 if (callbacks && callbacks->concataddr)
1029 str = callbacks->concataddr (savestr, str);
1030 else
1031 str = concataddr (savestr, str);
1032 break;
1033
1034 case FT_PUTADDR:
1035 /* output the str register as an address component,
1036 * splitting it into multiple lines if necessary. The
1037 * value reg. contains the max line length. The lit.
1038 * field may contain a string to prepend to the result
1039 * (e.g., "To: ")
1040 */
1041 {
1042 char *lp, *lastb;
1043 int indent, wid, len;
1044
1045 lp = str;
1046 wid = value;
1047 len = str ? strlen (str) : 0;
1048 sp = fmt->f_text;
1049 indent = strlen (sp);
1050 wid -= indent;
1051 if (wid <= 0) {
1052 die("putaddr -- num register (%d) must be greater "
1053 "than label width (%d)", value, indent);
1054 }
1055 while ((c = *sp++) && charstring_chars (scanlp) < max) {
1056 charstring_push_back (scanlp, c);
1057 }
1058 while (len > wid) {
1059 /* try to break at a comma; failing that, break at a
1060 * space.
1061 */
1062 lastb = 0; sp = lp + wid;
1063 while (sp > lp && (c = (unsigned char) *--sp) != ',') {
1064 if (! lastb && isspace(c))
1065 lastb = sp - 1;
1066 }
1067 if (sp == lp) {
1068 if (! (sp = lastb)) {
1069 sp = lp + wid - 1;
1070 while (*sp && *sp != ',' &&
1071 !isspace((unsigned char) *sp))
1072 sp++;
1073 if (*sp != ',')
1074 sp--;
1075 }
1076 }
1077 len -= sp - lp + 1;
1078 while (lp <= sp && charstring_chars (scanlp) < max) {
1079 charstring_push_back (scanlp, *lp++);
1080 }
1081 while (isspace((unsigned char) *lp))
1082 lp++, len--;
1083 if (*lp) {
1084 if (charstring_chars (scanlp) < max) {
1085 charstring_push_back (scanlp, '\n');
1086 }
1087 for (i=indent;
1088 charstring_chars (scanlp) < max && i > 0;
1089 i--)
1090 charstring_push_back (scanlp, ' ');
1091 }
1092 }
1093 cpstripped (scanlp, max - charstring_chars (scanlp), lp);
1094 }
1095 break;
1096
1097 case FT_PARSEADDR:
1098 comp = fmt->f_comp;
1099 if (comp->c_flags & CF_PARSED)
1100 break;
1101 if (comp->c_mn != &fmt_mnull)
1102 mnfree (comp->c_mn);
1103 if ((sp = comp->c_text) && (sp = getname(sp)) &&
1104 (mn = getm (sp, NULL, 0, NULL, 0))) {
1105 comp->c_mn = mn;
1106 while (getname(""))
1107 ;
1108 comp->c_flags |= CF_PARSED;
1109 } else {
1110 while (getname("")) /* XXX */
1111 ;
1112 comp->c_mn = &fmt_mnull;
1113 }
1114 break;
1115
1116 case FT_MYMBOX:
1117 case FT_GETMYMBOX:
1118 case FT_GETMYADDR:
1119 /*
1120 * if there's no component, we say true. Otherwise we
1121 * say "true" only if we can parse the address and it
1122 * matches one of our addresses.
1123 */
1124 comp = fmt->f_comp;
1125 if (comp->c_mn != &fmt_mnull)
1126 mnfree (comp->c_mn);
1127 if ((sp = comp->c_text) && (sp = getname(sp)) &&
1128 (mn = getm (sp, NULL, 0, NULL, 0))) {
1129 comp->c_mn = mn;
1130 if (ismymbox(mn)) {
1131 comp->c_flags |= CF_TRUE;
1132 /* Set str for use with FT_GETMYMBOX. With
1133 FT_GETMYADDR, comp->c_mn will be run through
1134 FT_LS_ADDR, which will strip off any pers
1135 name. */
1136 str = mn->m_text;
1137 } else {
1138 comp->c_flags &= ~CF_TRUE;
1139 }
1140 while ((sp = getname(sp)))
1141 if ((comp->c_flags & CF_TRUE) == 0 &&
1142 (mn = getm (sp, NULL, 0, NULL, 0)))
1143 if (ismymbox(mn)) {
1144 comp->c_flags |= CF_TRUE;
1145 /* Set str and comp->c_text for use with
1146 FT_GETMYMBOX. With FT_GETMYADDR,
1147 comp->c_mn will be run through
1148 FT_LS_ADDR, which will strip off any
1149 pers name. */
1150 free (comp->c_text);
1151 comp->c_text = str = strdup (mn->m_text);
1152 comp->c_mn = mn;
1153 }
1154 comp->c_flags |= CF_PARSED;
1155 } else {
1156 while (getname("")) /* XXX */
1157 ;
1158 if (comp->c_text == 0)
1159 comp->c_flags |= CF_TRUE;
1160 else {
1161 comp->c_flags &= ~CF_TRUE;
1162 }
1163 comp->c_mn = &fmt_mnull;
1164 }
1165 if ((comp->c_flags & CF_TRUE) == 0 &&
1166 (fmt->f_type == FT_GETMYMBOX || fmt->f_type == FT_GETMYADDR)) {
1167 /* Fool FT_LS_ADDR into not producing an address. */
1168 comp->c_mn = &fmt_mnull; comp->c_text = NULL;
1169 }
1170 break;
1171 }
1172
1173 /*
1174 * Call our tracing callback function, if one was supplied
1175 */
1176
1177 if (callbacks && callbacks->trace_func)
1178 callbacks->trace_func(callbacks->trace_context, fmt, value,
1179 str, charstring_buffer (scanlp));
1180 fmt++;
1181 }
1182
1183 /* Emit any trailing sequences of zero display length. */
1184 while (fmt->f_type != FT_DONE) {
1185 if (fmt->f_type == FT_LS_LIT) {
1186 str = fmt->f_text;
1187 if (callbacks && callbacks->trace_func)
1188 callbacks->trace_func(callbacks->trace_context, fmt, value,
1189 str, charstring_buffer (scanlp));
1190 } else if (fmt->f_type == FT_STRLITZ) {
1191 /* Don't want to emit part of an escape sequence. So if
1192 there isn't enough room in the buffer for the entire
1193 string, skip it completely. Need room for null
1194 terminator, and maybe trailing newline (added below). */
1195 if (str) {
1196 for (sp = str; *sp; ++sp) {
1197 charstring_push_back (scanlp, *sp);
1198 }
1199 }
1200 if (callbacks && callbacks->trace_func)
1201 callbacks->trace_func(callbacks->trace_context, fmt, value,
1202 str, charstring_buffer (scanlp));
1203 }
1204 fmt++;
1205 }
1206
1207 finished:
1208 if (charstring_bytes (scanlp) > 0) {
1209 /*
1210 * Append a newline if the last character wasn't.
1211 */
1212 #ifdef MULTIBYTE_SUPPORT
1213 /*
1214 * It's a little tricky because the last byte might be part of
1215 * a multibyte character, in which case we assume that wasn't
1216 * a newline.
1217 */
1218 size_t last_char_len = charstring_last_char_len (scanlp);
1219 #else /* ! MULTIBYTE_SUPPORT */
1220 size_t last_char_len = 1;
1221 #endif /* ! MULTIBYTE_SUPPORT */
1222
1223 if (last_char_len > 1 ||
1224 charstring_buffer (scanlp)[charstring_bytes (scanlp) - 1] != '\n') {
1225 charstring_push_back (scanlp, '\n');
1226 }
1227 }
1228
1229 return NULL;
1230 }