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