]> diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
Cleaned up leaks from calls to content_charset() in mhfixmsg.
[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 = (isascii((unsigned char) c1) && isalpha((unsigned char) c1) &&
53 isupper((unsigned char) c1)) ? tolower((unsigned char) c1) : c1;
54 while ((c2 = *str++) && c1 != ((isascii((unsigned char) c2) &&
55 isalpha((unsigned char) c2) &&
56 isupper((unsigned char) c2)) ?
57 tolower((unsigned char) c2) : c2))
58 ;
59 if (! c2)
60 return 0;
61 s1 = sub + 1; s2 = str;
62 while ((c1 = *s1++) && ((isascii((unsigned char) c1) &&
63 isalpha((unsigned char) c1) &&
64 isupper((unsigned char) c1)) ?
65 tolower(c1) : c1) ==
66 ((isascii((unsigned char) (c2 =*s2++)) &&
67 isalpha((unsigned char) c2) &&
68 isupper((unsigned char) c2)) ?
69 tolower((unsigned char) c2) : c2))
70 ;
71 if (! c1)
72 return 1;
73 }
74 return 1;
75 }
76
77 /*
78 * copy a number to the destination subject to a maximum width
79 */
80 void
81 cpnumber(charstring_t dest, int num, unsigned int wid, char fill, size_t max) {
82 if (wid < (num >= 0 ? max : max-1)) {
83 /* Build up the string representation of num in reverse. */
84 charstring_t rev = charstring_create (0);
85 int i = num >= 0 ? num : -num;
86
87 do {
88 charstring_push_back (rev, i % 10 + '0');
89 i /= 10;
90 } while (--wid > 0 && i > 0);
91 if (i > 0) {
92 /* Overflowed the field (wid). */
93 charstring_push_back (rev, '?');
94 } else if (num < 0 && wid > 0) {
95 /* Shouldn't need the wid > 0 check, that's why the condition
96 at the top checks wid < max-1 when num < 0. */
97 charstring_push_back (rev, '-');
98 --wid;
99 }
100 while (wid-- > 0 && fill != 0) {
101 charstring_push_back (rev, fill);
102 }
103
104 {
105 /* Output the string in reverse. */
106 size_t b = charstring_bytes (rev);
107 const char *cp = b ? &charstring_buffer (rev)[b] : NULL;
108
109 for (; b > 0; --b) {
110 charstring_push_back (dest, *--cp);
111 }
112 }
113
114 charstring_free (rev);
115 }
116 }
117
118 /*
119 * copy string from str to dest padding with the fill character to a
120 * size of wid characters. if wid is negative, the string is right
121 * aligned no more than max characters are copied
122 */
123 void
124 cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) {
125 int remaining; /* remaining output width available */
126 int rjust;
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 if ((sp = str)) {
146 #ifdef MULTIBYTE_SUPPORT
147 if (mbtowc(NULL, NULL, 0)) {} /* reset shift state */
148 #endif
149 end = strlen(str);
150 while (*sp && remaining > 0 && end > 0) {
151 #ifdef MULTIBYTE_SUPPORT
152 char_len = mbtowc(&wide_char, sp, end);
153
154 /*
155 * See the relevant comments in cpstripped() to explain what's
156 * going on here; we want to handle the case where we get
157 * characters that mbtowc() cannot handle
158 */
159
160 if (char_len < 0) {
161 altstr = "?";
162 char_len = mbtowc(&wide_char, altstr, 1);
163 }
164
165 if (char_len <= 0) {
166 break;
167 }
168
169 w = wcwidth(wide_char);
170
171 /* If w > remaining, w must be positive. */
172 if (w > remaining) {
173 break;
174 }
175
176 end -= char_len;
177
178 if (iswcntrl(wide_char) || iswspace(wide_char)) {
179 sp += char_len;
180 #else
181 int c;
182 end--;
183 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
184 intentionally warns if they are passed a char. */
185 c = (unsigned char) *sp;
186 if (iscntrl(c) || isspace(c)) {
187 sp++;
188 #endif
189 if (!prevCtrl) {
190 charstring_push_back (dest, ' ');
191 remaining--;
192 }
193
194 prevCtrl = 1;
195 continue;
196 }
197 prevCtrl = 0;
198
199 #ifdef MULTIBYTE_SUPPORT
200 if (w >= 0 && remaining >= w) {
201 charstring_push_back_chars (dest, altstr ? altstr : sp,
202 char_len, w);
203 remaining -= w;
204 altstr = NULL;
205 }
206 sp += char_len;
207 #else
208 charstring_push_back (dest, *sp++);
209 remaining--;
210 #endif
211 }
212 }
213
214 if (rjust) {
215 if (remaining > 0) {
216 /* copy string to the right */
217 charstring_t copy = charstring_copy (dest);
218
219 /* add padding at the beginning */
220 charstring_clear (dest);
221 for (; remaining > 0; --remaining) {
222 charstring_push_back (dest, fill);
223 }
224
225 charstring_append (dest, copy);
226
227 charstring_free (copy);
228 }
229 } else {
230 /* pad remaining space */
231 while (remaining-- > 0) {
232 charstring_push_back (dest, fill);
233 }
234 }
235 }
236
237 static void
238 cpstripped (charstring_t dest, size_t max, char *str)
239 {
240 int prevCtrl = 1; /* This is 1 so we strip out leading spaces */
241 int len;
242 #ifdef MULTIBYTE_SUPPORT
243 int char_len, w;
244 wchar_t wide_char;
245 char *altstr = NULL;
246 #endif /* MULTIBYTE_SUPPORT */
247
248 if (!str) {
249 return;
250 }
251
252 len = strlen(str);
253
254 #ifdef MULTIBYTE_SUPPORT
255 if (mbtowc(NULL, NULL, 0)) {} /* Reset shift state */
256 #endif /* MULTIBYTE_SUPPORT */
257
258 /*
259 * Process each character at a time; if we have multibyte support
260 * then deal with that here.
261 */
262
263 while (*str != '\0' && len > 0 && max > 0) {
264 #ifdef MULTIBYTE_SUPPORT
265 char_len = mbtowc(&wide_char, str, len);
266 w = wcwidth(wide_char);
267
268 /*
269 * If mbrtowc() failed, then we have a character that isn't valid
270 * in the current encoding. Replace it with a '?'. We do that by
271 * setting the alstr variable to the value of the replacement string;
272 * altstr is used below when the bytes are copied into the output
273 * buffer.
274 */
275
276 if (char_len < 0) {
277 altstr = "?";
278 char_len = mbtowc(&wide_char, altstr, 1);
279 }
280
281 if (char_len <= 0) {
282 break;
283 }
284
285 len -= char_len;
286
287 if (iswcntrl(wide_char) || iswspace(wide_char)) {
288 str += char_len;
289 #else /* MULTIBYTE_SUPPORT */
290 int c = (unsigned char) *str;
291 len--;
292 if (iscntrl(c) || isspace(c)) {
293 str++;
294 #endif /* MULTIBYTE_SUPPORT */
295 if (! prevCtrl) {
296 charstring_push_back (dest, ' ');
297 --max;
298 }
299
300 prevCtrl = 1;
301 continue;
302 }
303
304 prevCtrl = 0;
305
306 #ifdef MULTIBYTE_SUPPORT
307 charstring_push_back_chars (dest, altstr ? altstr : str, char_len, w);
308 max -= w;
309 str += char_len;
310 altstr = NULL;
311 #else /* MULTIBYE_SUPPORT */
312 charstring_push_back (dest, *str++);
313 --max;
314 #endif /* MULTIBYTE_SUPPORT */
315 }
316 }
317
318 static char *lmonth[] = { "January", "February","March", "April",
319 "May", "June", "July", "August",
320 "September","October", "November","December" };
321
322 static char *
323 get_x400_friendly (char *mbox, char *buffer, int buffer_len)
324 {
325 char given[BUFSIZ], surname[BUFSIZ];
326
327 if (mbox == NULL)
328 return NULL;
329 if (*mbox == '"')
330 mbox++;
331 if (*mbox != '/')
332 return NULL;
333
334 if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
335 for (mbox = buffer; (mbox = strchr(mbox, '.')); )
336 *mbox++ = ' ';
337
338 return buffer;
339 }
340
341 if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
342 return NULL;
343
344 if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
345 snprintf (buffer, buffer_len, "%s %s", given, surname);
346 else
347 snprintf (buffer, buffer_len, "%s", surname);
348
349 return buffer;
350 }
351
352 static int
353 get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
354 {
355 int idx;
356 char *cp;
357
358 if ((idx = stringdex (key, mbox)) < 0
359 || !(cp = strchr(mbox += idx + strlen (key), '/')))
360 return 0;
361
362 snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
363 return 1;
364 }
365
366 struct format *
367 fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
368 struct fmt_callbacks *callbacks)
369 {
370 char *sp;
371 char *savestr, *str;
372 char buffer[BUFSIZ], buffer2[BUFSIZ];
373 int i, c, rjust;
374 int value;
375 time_t t;
376 size_t max;
377 struct format *fmt;
378 struct comp *comp;
379 struct tws *tws;
380 struct mailname *mn;
381
382 /*
383 * max is the same as width, but unsigned so comparisons
384 * with charstring_chars() won't raise compile warnings.
385 */
386 max = width;
387 savestr = str = NULL;
388 value = 0;
389
390 for (fmt = format; fmt->f_type != FT_DONE; fmt++)
391 switch (fmt->f_type) {
392 case FT_PARSEADDR:
393 case FT_PARSEDATE:
394 fmt->f_comp->c_flags &= ~CF_PARSED;
395 break;
396 case FT_COMP:
397 case FT_COMPF:
398 case FT_LS_COMP:
399 case FT_LS_DECODECOMP:
400 /*
401 * Trim these components of any newlines.
402 *
403 * But don't trim the "body" and "text" components.
404 */
405
406 comp = fmt->f_comp;
407
408 if (! (comp->c_flags & CF_TRIMMED) && comp->c_text &&
409 (i = strlen(comp->c_text)) > 0) {
410 if (comp->c_text[i - 1] == '\n' &&
411 strcmp(comp->c_name, "body") != 0 &&
412 strcmp(comp->c_name, "text") != 0)
413 comp->c_text[i - 1] = '\0';
414 comp->c_flags |= CF_TRIMMED;
415 }
416 break;
417 }
418
419 fmt = format;
420
421 for ( ; charstring_chars (scanlp) < max; ) {
422 switch (fmt->f_type) {
423
424 case FT_COMP:
425 cpstripped (scanlp, max - charstring_chars (scanlp),
426 fmt->f_comp->c_text);
427 break;
428 case FT_COMPF:
429 cptrimmed (scanlp, fmt->f_comp->c_text, fmt->f_width,
430 fmt->f_fill, max - charstring_chars (scanlp));
431 break;
432
433 case FT_LIT:
434 sp = fmt->f_text;
435 while ((c = *sp++) && charstring_chars (scanlp) < max) {
436 charstring_push_back (scanlp, c);
437 }
438 break;
439 case FT_LITF:
440 sp = fmt->f_text;
441 rjust = 0;
442 i = fmt->f_width;
443 if (i < 0) {
444 i = -i;
445 rjust++; /* XXX should do something with this */
446 }
447 while ((c = *sp++) && --i >= 0 && charstring_chars (scanlp) < max) {
448 charstring_push_back (scanlp, c);
449 }
450 while (--i >= 0 && charstring_chars (scanlp) < max) {
451 charstring_push_back (scanlp, fmt->f_fill);
452 }
453 break;
454
455 case FT_STR:
456 cpstripped (scanlp, max - charstring_chars (scanlp), str);
457 break;
458 case FT_STRF:
459 cptrimmed (scanlp, str, fmt->f_width, fmt->f_fill,
460 max - charstring_chars (scanlp));
461 break;
462 case FT_STRLIT:
463 if (str) {
464 sp = str;
465 while ((c = *sp++) && charstring_chars (scanlp) < max) {
466 charstring_push_back (scanlp, c);
467 }
468 }
469 break;
470 case FT_STRLITZ:
471 if (str) charstring_push_back_chars (scanlp, str, strlen (str), 0);
472 break;
473 case FT_STRFW:
474 adios (NULL, "internal error (FT_STRFW)");
475
476 case FT_NUM: {
477 int num = value;
478 unsigned int wid;
479
480 for (wid = num <= 0 ? 1 : 0; num; ++wid, num /= 10) {}
481 cpnumber (scanlp, value, wid, ' ',
482 max - charstring_chars (scanlp));
483 break;
484 }
485 case FT_LS_KILO:
486 case FT_LS_KIBI:
487 {
488 char *unitcp;
489 unsigned int whole, tenths;
490 unsigned int scale = 0;
491 unsigned int val = (unsigned int)value;
492 char *kibisuff = NULL;
493
494 switch (fmt->f_type) {
495 case FT_LS_KILO: scale = 1000; kibisuff = ""; break;
496 case FT_LS_KIBI: scale = 1024; kibisuff = "i"; break;
497 }
498
499 if (val < scale) {
500 snprintf(buffer, sizeof(buffer), "%u", val);
501 } else {
502 /* To prevent divide by 0, found by clang static
503 analyzer. */
504 if (scale == 0) { scale = 1; }
505
506 /* find correct scale for size (Kilo/Mega/Giga/Tera) */
507 for (unitcp = "KMGT"; val > (scale * scale); val /= scale) {
508 if (!*++unitcp)
509 break;
510 }
511
512 if (!*unitcp) {
513 strcpy(buffer, "huge");
514 } else {
515 /* val is scale times too big. we want tenths */
516 val *= 10;
517
518 /* round up */
519 val += (scale - 1);
520 val /= scale;
521
522 whole = val / 10;
523 tenths = val - (whole * 10);
524
525 if (tenths) {
526 snprintf(buffer, sizeof(buffer), "%u.%u%c%s",
527 whole, tenths, *unitcp, kibisuff);
528 } else {
529 snprintf(buffer, sizeof(buffer), "%u%c%s",
530 whole, *unitcp, kibisuff);
531 }
532 }
533 }
534 str = buffer;
535 }
536 break;
537 case FT_NUMF:
538 cpnumber (scanlp, value, fmt->f_width, fmt->f_fill,
539 max - charstring_chars (scanlp));
540 break;
541
542 case FT_CHAR:
543 charstring_push_back (scanlp, fmt->f_char);
544 break;
545
546 case FT_DONE:
547 if (callbacks && callbacks->trace_func)
548 callbacks->trace_func(callbacks->trace_context, fmt, value,
549 str, charstring_buffer (scanlp));
550 goto finished;
551
552 case FT_IF_S:
553 if (!(value = (str && *str))) {
554 if (callbacks && callbacks->trace_func)
555 callbacks->trace_func(callbacks->trace_context, fmt, value,
556 str, charstring_buffer (scanlp));
557 fmt += fmt->f_skip;
558 continue;
559 }
560 break;
561
562 case FT_IF_S_NULL:
563 if (!(value = (str == NULL || *str == 0))) {
564 if (callbacks && callbacks->trace_func)
565 callbacks->trace_func(callbacks->trace_context, fmt, value,
566 str, charstring_buffer (scanlp));
567 fmt += fmt->f_skip;
568 continue;
569 }
570 break;
571
572 case FT_IF_V_EQ:
573 if (value != fmt->f_value) {
574 if (callbacks && callbacks->trace_func)
575 callbacks->trace_func(callbacks->trace_context, fmt, value,
576 str, charstring_buffer (scanlp));
577 fmt += fmt->f_skip;
578 continue;
579 }
580 break;
581
582 case FT_IF_V_NE:
583 if (value == fmt->f_value) {
584 if (callbacks && callbacks->trace_func)
585 callbacks->trace_func(callbacks->trace_context, fmt, value,
586 str, charstring_buffer (scanlp));
587 fmt += fmt->f_skip;
588 continue;
589 }
590 break;
591
592 case FT_IF_V_GT:
593 if (value <= fmt->f_value) {
594 if (callbacks && callbacks->trace_func)
595 callbacks->trace_func(callbacks->trace_context, fmt, value,
596 str, charstring_buffer (scanlp));
597 fmt += fmt->f_skip;
598 continue;
599 }
600 break;
601
602 case FT_IF_MATCH:
603 if (!(value = (str && match (str, fmt->f_text)))) {
604 if (callbacks && callbacks->trace_func)
605 callbacks->trace_func(callbacks->trace_context, fmt, value,
606 str, charstring_buffer (scanlp));
607 fmt += fmt->f_skip;
608 continue;
609 }
610 break;
611
612 case FT_V_MATCH:
613 if (str)
614 value = match (str, fmt->f_text);
615 else
616 value = 0;
617 break;
618
619 case FT_IF_AMATCH:
620 if (!(value = (str && uprf (str, fmt->f_text)))) {
621 if (callbacks && callbacks->trace_func)
622 callbacks->trace_func(callbacks->trace_context, fmt, value,
623 str, charstring_buffer (scanlp));
624 fmt += fmt->f_skip;
625 continue;
626 }
627 break;
628
629 case FT_V_AMATCH:
630 value = uprf (str, fmt->f_text);
631 break;
632
633 case FT_S_NONNULL:
634 value = (str != NULL && *str != 0);
635 break;
636
637 case FT_S_NULL:
638 value = (str == NULL || *str == 0);
639 break;
640
641 case FT_V_EQ:
642 value = (fmt->f_value == value);
643 break;
644
645 case FT_V_NE:
646 value = (fmt->f_value != value);
647 break;
648
649 case FT_V_GT:
650 value = (fmt->f_value > value);
651 break;
652
653 case FT_GOTO:
654 if (callbacks && callbacks->trace_func)
655 callbacks->trace_func(callbacks->trace_context, fmt, value,
656 str, charstring_buffer (scanlp));
657 fmt += fmt->f_skip;
658 continue;
659
660 case FT_NOP:
661 break;
662
663 case FT_LS_COMP:
664 str = fmt->f_comp->c_text;
665 break;
666 case FT_LS_LIT:
667 str = fmt->f_text;
668 break;
669 case FT_LS_GETENV:
670 if (!(str = getenv (fmt->f_text)))
671 str = "";
672 break;
673 case FT_LS_CFIND:
674 if (!(str = context_find (fmt->f_text)))
675 str = "";
676 break;
677
678 case FT_LS_DECODECOMP:
679 if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
680 str = buffer2;
681 else
682 str = fmt->f_comp->c_text;
683 break;
684
685 case FT_LS_DECODE:
686 if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
687 str = buffer2;
688 break;
689
690 case FT_LS_TRIM:
691 if (str) {
692 char *xp;
693
694 strncpy(buffer, str, sizeof(buffer));
695 buffer[sizeof(buffer)-1] = '\0';
696 str = buffer;
697 while (isspace((unsigned char) *str))
698 str++;
699 rjust = 0;
700 if ((i = fmt->f_width) < 0) {
701 i = -i;
702 rjust++;
703 }
704
705 if (!rjust && i > 0 && (int) strlen(str) > i)
706 str[i] = '\0';
707 xp = str;
708 xp += strlen(str) - 1;
709 while (xp > str && isspace((unsigned char) *xp))
710 *xp-- = '\0';
711 if (rjust && i > 0 && (int) strlen(str) > i)
712 str += strlen(str) - i;
713 }
714 break;
715
716 case FT_LV_COMPFLAG:
717 value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
718 break;
719 case FT_LV_COMP:
720 value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
721 break;
722 case FT_LV_LIT:
723 value = fmt->f_value;
724 break;
725 case FT_LV_DAT:
726 value = dat[fmt->f_value];
727 break;
728 case FT_LV_STRLEN:
729 if (str != NULL)
730 value = strlen(str);
731 else
732 value = 0;
733 break;
734 case FT_LV_CHAR_LEFT:
735 value = max - charstring_bytes (scanlp);
736 break;
737 case FT_LV_PLUS_L:
738 value += fmt->f_value;
739 break;
740 case FT_LV_MINUS_L:
741 value = fmt->f_value - value;
742 break;
743 case FT_LV_DIVIDE_L:
744 if (fmt->f_value)
745 value = value / fmt->f_value;
746 else
747 value = 0;
748 break;
749 case FT_LV_MODULO_L:
750 if (fmt->f_value)
751 value = value % fmt->f_value;
752 else
753 value = 0;
754 break;
755 case FT_SAVESTR:
756 savestr = str;
757 break;
758
759 case FT_LV_SEC:
760 value = fmt->f_comp->c_tws->tw_sec;
761 break;
762 case FT_LV_MIN:
763 value = fmt->f_comp->c_tws->tw_min;
764 break;
765 case FT_LV_HOUR:
766 value = fmt->f_comp->c_tws->tw_hour;
767 break;
768 case FT_LV_MDAY:
769 value = fmt->f_comp->c_tws->tw_mday;
770 break;
771 case FT_LV_MON:
772 value = fmt->f_comp->c_tws->tw_mon + 1;
773 break;
774 case FT_LS_MONTH:
775 str = tw_moty[fmt->f_comp->c_tws->tw_mon];
776 break;
777 case FT_LS_LMONTH:
778 str = lmonth[fmt->f_comp->c_tws->tw_mon];
779 break;
780 case FT_LS_ZONE:
781 str = dtwszone (fmt->f_comp->c_tws);
782 break;
783 case FT_LV_YEAR:
784 value = fmt->f_comp->c_tws->tw_year;
785 break;
786 case FT_LV_WDAY:
787 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
788 set_dotw (tws);
789 value = tws->tw_wday;
790 break;
791 case FT_LS_DAY:
792 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
793 set_dotw (tws);
794 str = tw_dotw[tws->tw_wday];
795 break;
796 case FT_LS_WEEKDAY:
797 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
798 set_dotw (tws);
799 str = tw_ldotw[tws->tw_wday];
800 break;
801 case FT_LV_YDAY:
802 value = fmt->f_comp->c_tws->tw_yday;
803 break;
804 case FT_LV_ZONE:
805 value = fmt->f_comp->c_tws->tw_zone;
806 break;
807 case FT_LV_CLOCK:
808 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
809 value = dmktime(fmt->f_comp->c_tws);
810 break;
811 case FT_LV_RCLOCK:
812 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
813 value = dmktime(fmt->f_comp->c_tws);
814 value = time((time_t *) 0) - value;
815 break;
816 case FT_LV_DAYF:
817 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
818 set_dotw (tws);
819 switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
820 case TW_SEXP:
821 value = 1; break;
822 case TW_SIMP:
823 value = 0; break;
824 default:
825 value = -1; break;
826 }
827 break;
828 case FT_LV_ZONEF:
829 if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
830 value = 1;
831 else
832 value = -1;
833 break;
834 case FT_LV_DST:
835 value = fmt->f_comp->c_tws->tw_flags & TW_DST ? 1 : 0;
836 break;
837 case FT_LS_822DATE:
838 str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
839 break;
840 case FT_LS_PRETTY:
841 str = dasctime (fmt->f_comp->c_tws, TW_NULL);
842 break;
843
844 case FT_LS_PERS:
845 str = fmt->f_comp->c_mn->m_pers;
846 break;
847 case FT_LS_MBOX:
848 str = fmt->f_comp->c_mn->m_mbox;
849 break;
850 case FT_LS_HOST:
851 str = fmt->f_comp->c_mn->m_host;
852 break;
853 case FT_LS_PATH:
854 str = fmt->f_comp->c_mn->m_path;
855 break;
856 case FT_LS_GNAME:
857 str = fmt->f_comp->c_mn->m_gname;
858 break;
859 case FT_LS_NOTE:
860 str = fmt->f_comp->c_mn->m_note;
861 break;
862 case FT_LS_822ADDR:
863 str = adrformat( fmt->f_comp->c_mn );
864 break;
865 case FT_LV_HOSTTYPE:
866 value = fmt->f_comp->c_mn->m_type;
867 break;
868 case FT_LV_INGRPF:
869 value = fmt->f_comp->c_mn->m_ingrp;
870 break;
871 case FT_LV_NOHOSTF:
872 value = fmt->f_comp->c_mn->m_nohost;
873 break;
874 case FT_LS_ADDR:
875 case FT_LS_FRIENDLY:
876 if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
877 str = fmt->f_comp->c_text;
878 break;
879 }
880 if (fmt->f_type == FT_LS_ADDR)
881 goto unfriendly;
882 if ((str = mn->m_pers) == NULL) {
883 if ((str = mn->m_note)) {
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 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 ((struct format *)0);
1178 }