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