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