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