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