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