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