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