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