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