]> diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
Increment VERSION on the head to 1.7+dev.
[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_UNITS:
494 {
495 char *unitcp;
496 unsigned int whole, tenths;
497
498 if (value < 1000) {
499 snprintf(buffer, sizeof(buffer), "%u", value);
500 } else {
501
502 /* find correct scale for size (Kilo/Mega/Giga/Tera) */
503 for (unitcp = "KMGT"; value > 999999; value /= 1000) {
504 if (!*++unitcp)
505 break;
506 }
507
508 if (!*unitcp) {
509 strcpy(buffer, "huge");
510 } else {
511 /* round up to next higer tenth of kilo-/mega-/giga- */
512 value += 99; value /= 100;
513
514 /* want one digit after decimal, avoid float */
515 whole = value / 10;
516 tenths = value - (whole * 10);
517
518 if (tenths) {
519 snprintf(buffer, sizeof(buffer), "%u.%u%c", whole, tenths, *unitcp);
520 } else {
521 snprintf(buffer, sizeof(buffer), "%u%c", whole, *unitcp);
522 }
523 }
524 }
525 str = buffer;
526 }
527 break;
528 case FT_NUMF:
529 cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
530 break;
531
532 case FT_CHAR:
533 *cp++ = fmt->f_char;
534 break;
535
536 case FT_DONE:
537 if (callbacks && callbacks->trace_func)
538 callbacks->trace_func(callbacks->trace_context, fmt, value,
539 str, scanl);
540 goto finished;
541
542 case FT_IF_S:
543 if (!(value = (str && *str))) {
544 if (callbacks && callbacks->trace_func)
545 callbacks->trace_func(callbacks->trace_context, fmt, value,
546 str, scanl);
547 fmt += fmt->f_skip;
548 continue;
549 }
550 break;
551
552 case FT_IF_S_NULL:
553 if (!(value = (str == NULL || *str == 0))) {
554 if (callbacks && callbacks->trace_func)
555 callbacks->trace_func(callbacks->trace_context, fmt, value,
556 str, scanl);
557 fmt += fmt->f_skip;
558 continue;
559 }
560 break;
561
562 case FT_IF_V_EQ:
563 if (value != fmt->f_value) {
564 if (callbacks && callbacks->trace_func)
565 callbacks->trace_func(callbacks->trace_context, fmt, value,
566 str, scanl);
567 fmt += fmt->f_skip;
568 continue;
569 }
570 break;
571
572 case FT_IF_V_NE:
573 if (value == fmt->f_value) {
574 if (callbacks && callbacks->trace_func)
575 callbacks->trace_func(callbacks->trace_context, fmt, value,
576 str, scanl);
577 fmt += fmt->f_skip;
578 continue;
579 }
580 break;
581
582 case FT_IF_V_GT:
583 if (value <= fmt->f_value) {
584 if (callbacks && callbacks->trace_func)
585 callbacks->trace_func(callbacks->trace_context, fmt, value,
586 str, scanl);
587 fmt += fmt->f_skip;
588 continue;
589 }
590 break;
591
592 case FT_IF_MATCH:
593 if (!(value = (str && match (str, fmt->f_text)))) {
594 if (callbacks && callbacks->trace_func)
595 callbacks->trace_func(callbacks->trace_context, fmt, value,
596 str, scanl);
597 fmt += fmt->f_skip;
598 continue;
599 }
600 break;
601
602 case FT_V_MATCH:
603 if (str)
604 value = match (str, fmt->f_text);
605 else
606 value = 0;
607 break;
608
609 case FT_IF_AMATCH:
610 if (!(value = (str && uprf (str, fmt->f_text)))) {
611 if (callbacks && callbacks->trace_func)
612 callbacks->trace_func(callbacks->trace_context, fmt, value,
613 str, scanl);
614 fmt += fmt->f_skip;
615 continue;
616 }
617 break;
618
619 case FT_V_AMATCH:
620 value = uprf (str, fmt->f_text);
621 break;
622
623 case FT_S_NONNULL:
624 value = (str != NULL && *str != 0);
625 break;
626
627 case FT_S_NULL:
628 value = (str == NULL || *str == 0);
629 break;
630
631 case FT_V_EQ:
632 value = (fmt->f_value == value);
633 break;
634
635 case FT_V_NE:
636 value = (fmt->f_value != value);
637 break;
638
639 case FT_V_GT:
640 value = (fmt->f_value > value);
641 break;
642
643 case FT_GOTO:
644 if (callbacks && callbacks->trace_func)
645 callbacks->trace_func(callbacks->trace_context, fmt, value,
646 str, scanl);
647 fmt += fmt->f_skip;
648 continue;
649
650 case FT_NOP:
651 break;
652
653 case FT_LS_COMP:
654 str = fmt->f_comp->c_text;
655 break;
656 case FT_LS_LIT:
657 str = fmt->f_text;
658 break;
659 case FT_LS_GETENV:
660 if (!(str = getenv (fmt->f_text)))
661 str = "";
662 break;
663 case FT_LS_CFIND:
664 if (!(str = context_find (fmt->f_text)))
665 str = "";
666 break;
667
668 case FT_LS_DECODECOMP:
669 if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
670 str = buffer2;
671 else
672 str = fmt->f_comp->c_text;
673 break;
674
675 case FT_LS_DECODE:
676 if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
677 str = buffer2;
678 break;
679
680 case FT_LS_TRIM:
681 if (str) {
682 char *xp;
683
684 strncpy(buffer, str, sizeof(buffer));
685 buffer[sizeof(buffer)-1] = '\0';
686 str = buffer;
687 while (isspace((unsigned char) *str))
688 str++;
689 ljust = 0;
690 if ((i = fmt->f_width) < 0) {
691 i = -i;
692 ljust++;
693 }
694
695 if (!ljust && i > 0 && (int) strlen(str) > i)
696 str[i] = '\0';
697 xp = str;
698 xp += strlen(str) - 1;
699 while (xp > str && isspace((unsigned char) *xp))
700 *xp-- = '\0';
701 if (ljust && i > 0 && (int) strlen(str) > i)
702 str += strlen(str) - i;
703 }
704 break;
705
706 case FT_LV_COMPFLAG:
707 value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
708 break;
709 case FT_LV_COMP:
710 value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
711 break;
712 case FT_LV_LIT:
713 value = fmt->f_value;
714 break;
715 case FT_LV_DAT:
716 value = dat[fmt->f_value];
717 break;
718 case FT_LV_STRLEN:
719 if (str != NULL)
720 value = strlen(str);
721 else
722 value = 0;
723 break;
724 case FT_LV_CHAR_LEFT:
725 value = width - (cp - scanl);
726 break;
727 case FT_LV_PLUS_L:
728 value += fmt->f_value;
729 break;
730 case FT_LV_MINUS_L:
731 value = fmt->f_value - value;
732 break;
733 case FT_LV_DIVIDE_L:
734 if (fmt->f_value)
735 value = value / fmt->f_value;
736 else
737 value = 0;
738 break;
739 case FT_LV_MODULO_L:
740 if (fmt->f_value)
741 value = value % fmt->f_value;
742 else
743 value = 0;
744 break;
745 case FT_SAVESTR:
746 savestr = str;
747 break;
748
749 case FT_LV_SEC:
750 value = fmt->f_comp->c_tws->tw_sec;
751 break;
752 case FT_LV_MIN:
753 value = fmt->f_comp->c_tws->tw_min;
754 break;
755 case FT_LV_HOUR:
756 value = fmt->f_comp->c_tws->tw_hour;
757 break;
758 case FT_LV_MDAY:
759 value = fmt->f_comp->c_tws->tw_mday;
760 break;
761 case FT_LV_MON:
762 value = fmt->f_comp->c_tws->tw_mon + 1;
763 break;
764 case FT_LS_MONTH:
765 str = tw_moty[fmt->f_comp->c_tws->tw_mon];
766 break;
767 case FT_LS_LMONTH:
768 str = lmonth[fmt->f_comp->c_tws->tw_mon];
769 break;
770 case FT_LS_ZONE:
771 str = dtwszone (fmt->f_comp->c_tws);
772 break;
773 case FT_LV_YEAR:
774 value = fmt->f_comp->c_tws->tw_year;
775 break;
776 case FT_LV_WDAY:
777 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
778 set_dotw (tws);
779 value = tws->tw_wday;
780 break;
781 case FT_LS_DAY:
782 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
783 set_dotw (tws);
784 str = tw_dotw[tws->tw_wday];
785 break;
786 case FT_LS_WEEKDAY:
787 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
788 set_dotw (tws);
789 str = tw_ldotw[tws->tw_wday];
790 break;
791 case FT_LV_YDAY:
792 value = fmt->f_comp->c_tws->tw_yday;
793 break;
794 case FT_LV_ZONE:
795 value = fmt->f_comp->c_tws->tw_zone;
796 break;
797 case FT_LV_CLOCK:
798 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
799 value = dmktime(fmt->f_comp->c_tws);
800 break;
801 case FT_LV_RCLOCK:
802 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
803 value = dmktime(fmt->f_comp->c_tws);
804 value = time((time_t *) 0) - value;
805 break;
806 case FT_LV_DAYF:
807 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
808 set_dotw (tws);
809 switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
810 case TW_SEXP:
811 value = 1; break;
812 case TW_SIMP:
813 value = 0; break;
814 default:
815 value = -1; break;
816 }
817 case FT_LV_ZONEF:
818 if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
819 value = 1;
820 else
821 value = -1;
822 break;
823 case FT_LV_DST:
824 value = fmt->f_comp->c_tws->tw_flags & TW_DST;
825 break;
826 case FT_LS_822DATE:
827 str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
828 break;
829 case FT_LS_PRETTY:
830 str = dasctime (fmt->f_comp->c_tws, TW_NULL);
831 break;
832
833 case FT_LS_PERS:
834 str = fmt->f_comp->c_mn->m_pers;
835 break;
836 case FT_LS_MBOX:
837 str = fmt->f_comp->c_mn->m_mbox;
838 break;
839 case FT_LS_HOST:
840 str = fmt->f_comp->c_mn->m_host;
841 break;
842 case FT_LS_PATH:
843 str = fmt->f_comp->c_mn->m_path;
844 break;
845 case FT_LS_GNAME:
846 str = fmt->f_comp->c_mn->m_gname;
847 break;
848 case FT_LS_NOTE:
849 str = fmt->f_comp->c_mn->m_note;
850 break;
851 case FT_LS_822ADDR:
852 str = adrformat( fmt->f_comp->c_mn );
853 break;
854 case FT_LV_HOSTTYPE:
855 value = fmt->f_comp->c_mn->m_type;
856 break;
857 case FT_LV_INGRPF:
858 value = fmt->f_comp->c_mn->m_ingrp;
859 break;
860 case FT_LV_NOHOSTF:
861 value = fmt->f_comp->c_mn->m_nohost;
862 break;
863 case FT_LS_ADDR:
864 case FT_LS_FRIENDLY:
865 if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
866 str = fmt->f_comp->c_text;
867 break;
868 }
869 if (fmt->f_type == FT_LS_ADDR)
870 goto unfriendly;
871 if ((str = mn->m_pers) == NULL) {
872 if ((str = mn->m_note)) {
873 strncpy (buffer, str, sizeof(buffer));
874 buffer[sizeof(buffer)-1] = '\0';
875 str = buffer;
876 if (*str == '(')
877 str++;
878 sp = str + strlen(str) - 1;
879 if (*sp == ')') {
880 *sp-- = '\0';
881 while (sp >= str)
882 if (*sp == ' ')
883 *sp-- = '\0';
884 else
885 break;
886 }
887 } else if (!(str = get_x400_friendly (mn->m_mbox,
888 buffer, sizeof(buffer)))) {
889 unfriendly: ;
890 switch (mn->m_type) {
891 case LOCALHOST:
892 str = mn->m_mbox;
893 break;
894 case UUCPHOST:
895 snprintf (buffer, sizeof(buffer), "%s!%s",
896 mn->m_host, mn->m_mbox);
897 str = buffer;
898 break;
899 default:
900 if (mn->m_mbox) {
901 snprintf (buffer, sizeof(buffer), "%s@%s",
902 mn->m_mbox, mn->m_host);
903 str= buffer;
904 }
905 else
906 str = mn->m_text;
907 break;
908 }
909 }
910 }
911 break;
912
913
914 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
915 case FT_LS_UNQUOTE:
916 if (str) {
917 strncpy(buffer, str, sizeof(buffer));
918 /* strncpy doesn't NUL-terminate if it fills the buffer */
919 buffer[sizeof(buffer)-1] = '\0';
920 unquote_string(buffer, buffer2);
921 str = buffer2;
922 }
923 break;
924
925 case FT_LOCALDATE:
926 comp = fmt->f_comp;
927 if ((t = comp->c_tws->tw_clock) == 0)
928 t = dmktime(comp->c_tws);
929 tws = dlocaltime(&t);
930 *comp->c_tws = *tws;
931 break;
932
933 case FT_GMTDATE:
934 comp = fmt->f_comp;
935 if ((t = comp->c_tws->tw_clock) == 0)
936 t = dmktime(comp->c_tws);
937 tws = dgmtime(&t);
938 *comp->c_tws = *tws;
939 break;
940
941 case FT_PARSEDATE:
942 comp = fmt->f_comp;
943 if (comp->c_flags & CF_PARSED)
944 break;
945 if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
946 *comp->c_tws = *tws;
947 comp->c_flags &= ~CF_TRUE;
948 } else if ((comp->c_flags & CF_DATEFAB) == 0) {
949 memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
950 comp->c_flags = CF_TRUE;
951 }
952 comp->c_flags |= CF_PARSED;
953 break;
954
955 case FT_FORMATADDR:
956 /* hook for custom address list formatting (see replsbr.c) */
957 if (callbacks && callbacks->formataddr)
958 str = callbacks->formataddr (savestr, str);
959 else
960 str = formataddr (savestr, str);
961 break;
962
963 case FT_CONCATADDR:
964 /* The same as formataddr, but doesn't do duplicate suppression */
965 if (callbacks && callbacks->concataddr)
966 str = callbacks->concataddr (savestr, str);
967 else
968 str = concataddr (savestr, str);
969 break;
970
971 case FT_PUTADDR:
972 /* output the str register as an address component,
973 * splitting it into multiple lines if necessary. The
974 * value reg. contains the max line length. The lit.
975 * field may contain a string to prepend to the result
976 * (e.g., "To: ")
977 */
978 {
979 char *lp, *lastb;
980 int indent, wid, len;
981
982 lp = str;
983 wid = value;
984 len = strlen (str);
985 sp = fmt->f_text;
986 indent = strlen (sp);
987 wid -= indent;
988 if (wid <= 0) {
989 adios(NULL, "putaddr -- num register (%d) must be greater "
990 "than label width (%d)", value, indent);
991 }
992 while( (c = (unsigned char) *sp++) && cp < ep)
993 *cp++ = (char) c;
994 while (len > wid) {
995 /* try to break at a comma; failing that, break at a
996 * space.
997 */
998 lastb = 0; sp = lp + wid;
999 while (sp > lp && (c = (unsigned char) *--sp) != ',') {
1000 if (! lastb && isspace(c))
1001 lastb = sp - 1;
1002 }
1003 if (sp == lp) {
1004 if (! (sp = lastb)) {
1005 sp = lp + wid - 1;
1006 while (*sp && *sp != ',' &&
1007 !isspace((unsigned char) *sp))
1008 sp++;
1009 if (*sp != ',')
1010 sp--;
1011 }
1012 }
1013 len -= sp - lp + 1;
1014 while (cp < ep && lp <= sp)
1015 *cp++ = *lp++;
1016 while (isspace((unsigned char) *lp))
1017 lp++, len--;
1018 if (*lp) {
1019 if (cp < ep)
1020 *cp++ = '\n';
1021 for (i=indent; cp < ep && i > 0; i--)
1022 *cp++ = ' ';
1023 }
1024 }
1025 cpstripped (&cp, &ep, scanl + max - 1, lp);
1026 }
1027 break;
1028
1029 case FT_PARSEADDR:
1030 comp = fmt->f_comp;
1031 if (comp->c_flags & CF_PARSED)
1032 break;
1033 if (comp->c_mn != &fmt_mnull)
1034 mnfree (comp->c_mn);
1035 if ((sp = comp->c_text) && (sp = getname(sp)) &&
1036 (mn = getm (sp, NULL, 0, NULL, 0))) {
1037 comp->c_mn = mn;
1038 while (getname(""))
1039 ;
1040 comp->c_flags |= CF_PARSED;
1041 } else {
1042 while (getname("")) /* XXX */
1043 ;
1044 comp->c_mn = &fmt_mnull;
1045 }
1046 break;
1047
1048 case FT_MYMBOX:
1049 /*
1050 * if there's no component, we say true. Otherwise we
1051 * say "true" only if we can parse the address and it
1052 * matches one of our addresses.
1053 */
1054 comp = fmt->f_comp;
1055 if (comp->c_mn != &fmt_mnull)
1056 mnfree (comp->c_mn);
1057 if ((sp = comp->c_text) && (sp = getname(sp)) &&
1058 (mn = getm (sp, NULL, 0, NULL, 0))) {
1059 comp->c_mn = mn;
1060 if (ismymbox(mn))
1061 comp->c_flags |= CF_TRUE;
1062 else
1063 comp->c_flags &= ~CF_TRUE;
1064 while ((sp = getname(sp)))
1065 if ((comp->c_flags & CF_TRUE) == 0 &&
1066 (mn = getm (sp, NULL, 0, NULL, 0)))
1067 if (ismymbox(mn))
1068 comp->c_flags |= CF_TRUE;
1069 } else {
1070 while (getname("")) /* XXX */
1071 ;
1072 if (comp->c_text == 0)
1073 comp->c_flags |= CF_TRUE;
1074 else
1075 comp->c_flags &= ~CF_TRUE;
1076 comp->c_mn = &fmt_mnull;
1077 }
1078 break;
1079 }
1080
1081 /*
1082 * Call our tracing callback function, if one was supplied
1083 */
1084
1085 if (callbacks && callbacks->trace_func)
1086 callbacks->trace_func(callbacks->trace_context, fmt, value,
1087 str, scanl);
1088 fmt++;
1089 }
1090
1091 /* Emit any trailing sequences of zero display length. */
1092 while (fmt->f_type != FT_DONE) {
1093 if (fmt->f_type == FT_LS_LIT) {
1094 str = fmt->f_text;
1095 if (callbacks && callbacks->trace_func)
1096 callbacks->trace_func(callbacks->trace_context, fmt, value,
1097 str, scanl);
1098 } else if (fmt->f_type == FT_STRLITZ) {
1099 /* Don't want to emit part of an escape sequence. So if
1100 there isn't enough room in the buffer for the entire
1101 string, skip it completely. Need room for null
1102 terminator, and maybe trailing newline (added below). */
1103 if (str && (cp - scanl + strlen (str) + 1 < max)) {
1104 for (sp = str; *sp; *cp++ = *sp++) continue;
1105 }
1106 if (callbacks && callbacks->trace_func)
1107 callbacks->trace_func(callbacks->trace_context, fmt, value,
1108 str, scanl);
1109 }
1110 fmt++;
1111 }
1112
1113 finished:;
1114 if (cp > scanl && cp[-1] != '\n') {
1115 if (cp - scanl < (int) max - 1) {
1116 *cp++ = '\n';
1117 } else {
1118 cp[-1] = '\n';
1119 }
1120 }
1121 *cp = '\0';
1122 return ((struct format *)0);
1123 }