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