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