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