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