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