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