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