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