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