]> diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
Document argsplit changes in mh-profile 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
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 {
356 char *cp, *ep, *sp;
357 char *savestr = NULL, *str = NULL;
358 char buffer[BUFSIZ], buffer2[BUFSIZ];
359 int i, c, ljust, n;
360 int value = 0;
361 time_t t;
362 struct format *fmt;
363 struct comp *comp;
364 struct tws *tws;
365 struct mailname *mn;
366
367 /* ep keeps track of displayed characters. They're limited by width.
368 The total number of characters, cp - scanl + 1 (for trailing NULL),
369 includes invisible control characters and is limited by max. */
370 cp = scanl;
371 ep = scanl + (width <= (int) max ? width : (int) max) - 1;
372
373 for (fmt = format; fmt->f_type != FT_DONE; fmt++)
374 switch (fmt->f_type) {
375 case FT_PARSEADDR:
376 case FT_PARSEDATE:
377 fmt->f_comp->c_flags &= ~CF_PARSED;
378 break;
379 case FT_COMP:
380 case FT_COMPF:
381 case FT_LS_COMP:
382 case FT_LS_DECODECOMP:
383 /*
384 * Trim these components of any newlines.
385 *
386 * But don't trim the "body" and "text" components.
387 */
388
389 comp = fmt->f_comp;
390
391 if (! (comp->c_flags & CF_TRIMMED) && comp->c_text &&
392 (i = strlen(comp->c_text)) > 0) {
393 if (comp->c_text[i - 1] == '\n' &&
394 strcmp(comp->c_name, "body") != 0 &&
395 strcmp(comp->c_name, "text") != 0)
396 comp->c_text[i - 1] = '\0';
397 comp->c_flags |= CF_TRIMMED;
398 }
399 break;
400 }
401
402 fmt = format;
403
404 while (cp < ep) {
405 switch (fmt->f_type) {
406
407 case FT_COMP:
408 cpstripped (&cp, &ep, scanl + max - 1, fmt->f_comp->c_text);
409 break;
410 case FT_COMPF:
411 cptrimmed (&cp, &ep, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill,
412 scanl + max - 1);
413 break;
414
415 case FT_LIT:
416 sp = fmt->f_text;
417 while( (c = *sp++) && cp < ep)
418 *cp++ = c;
419 break;
420 case FT_LITF:
421 sp = fmt->f_text;
422 ljust = 0;
423 i = fmt->f_width;
424 if (i < 0) {
425 i = -i;
426 ljust++; /* XXX should do something with this */
427 }
428 while( (c = *sp++) && --i >= 0 && cp < ep)
429 *cp++ = c;
430 while( --i >= 0 && cp < ep)
431 *cp++ = fmt->f_fill;
432 break;
433
434 case FT_STR:
435 cpstripped (&cp, &ep, scanl + max - 1, str);
436 break;
437 case FT_STRF:
438 cptrimmed (&cp, &ep, str, fmt->f_width, fmt->f_fill,
439 scanl + max - 1);
440 break;
441 case FT_STRLIT:
442 sp = str;
443 while ((c = *sp++) && cp < ep)
444 *cp++ = c;
445 break;
446 case FT_STRLITZ: {
447 size_t len = strlen (str);
448
449 /* Don't want to emit part of an escape sequence. So if
450 there isn't enough room in the buffer for the entire
451 string, skip it completely. */
452 if (cp - scanl + len + 1 < max) {
453 for (sp = str; *sp; *cp++ = *sp++) continue;
454
455 /* This string doesn't count against the width. So
456 increase ep the same amount as cp, only if the
457 scan buffer will always be large enough. */
458 if (ep - scanl + len + 1 < max) {
459 ep += len;
460 }
461 }
462
463 break;
464 }
465 case FT_STRFW:
466 adios (NULL, "internal error (FT_STRFW)");
467
468 case FT_NUM:
469 n = snprintf(cp, ep - cp + 1, "%d", value);
470 if (n >= 0) {
471 if (n >= ep - cp) {
472 cp = ep;
473 } else
474 cp += n;
475 }
476 break;
477 case FT_NUMF:
478 cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
479 break;
480
481 case FT_CHAR:
482 *cp++ = fmt->f_char;
483 break;
484
485 case FT_DONE:
486 goto finished;
487
488 case FT_IF_S:
489 if (!(value = (str && *str))) {
490 fmt += fmt->f_skip;
491 continue;
492 }
493 break;
494
495 case FT_IF_S_NULL:
496 if (!(value = (str == NULL || *str == 0))) {
497 fmt += fmt->f_skip;
498 continue;
499 }
500 break;
501
502 case FT_IF_V_EQ:
503 if (value != fmt->f_value) {
504 fmt += fmt->f_skip;
505 continue;
506 }
507 break;
508
509 case FT_IF_V_NE:
510 if (value == fmt->f_value) {
511 fmt += fmt->f_skip;
512 continue;
513 }
514 break;
515
516 case FT_IF_V_GT:
517 if (value <= fmt->f_value) {
518 fmt += fmt->f_skip;
519 continue;
520 }
521 break;
522
523 case FT_IF_MATCH:
524 if (!(value = (str && match (str, fmt->f_text)))) {
525 fmt += fmt->f_skip;
526 continue;
527 }
528 break;
529
530 case FT_V_MATCH:
531 if (str)
532 value = match (str, fmt->f_text);
533 else
534 value = 0;
535 break;
536
537 case FT_IF_AMATCH:
538 if (!(value = (str && uprf (str, fmt->f_text)))) {
539 fmt += fmt->f_skip;
540 continue;
541 }
542 break;
543
544 case FT_V_AMATCH:
545 value = uprf (str, fmt->f_text);
546 break;
547
548 case FT_S_NONNULL:
549 value = (str != NULL && *str != 0);
550 break;
551
552 case FT_S_NULL:
553 value = (str == NULL || *str == 0);
554 break;
555
556 case FT_V_EQ:
557 value = (fmt->f_value == value);
558 break;
559
560 case FT_V_NE:
561 value = (fmt->f_value != value);
562 break;
563
564 case FT_V_GT:
565 value = (fmt->f_value > value);
566 break;
567
568 case FT_GOTO:
569 fmt += fmt->f_skip;
570 continue;
571
572 case FT_NOP:
573 break;
574
575 case FT_LS_COMP:
576 str = fmt->f_comp->c_text;
577 break;
578 case FT_LS_LIT:
579 str = fmt->f_text;
580 break;
581 case FT_LS_GETENV:
582 if (!(str = getenv (fmt->f_text)))
583 str = "";
584 break;
585 case FT_LS_CFIND:
586 if (!(str = context_find (fmt->f_text)))
587 str = "";
588 break;
589
590 case FT_LS_DECODECOMP:
591 if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
592 str = buffer2;
593 else
594 str = fmt->f_comp->c_text;
595 break;
596
597 case FT_LS_DECODE:
598 if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
599 str = buffer2;
600 break;
601
602 case FT_LS_TRIM:
603 if (str) {
604 char *xp;
605
606 strncpy(buffer, str, sizeof(buffer));
607 buffer[sizeof(buffer)-1] = '\0';
608 str = buffer;
609 while (isspace((unsigned char) *str))
610 str++;
611 ljust = 0;
612 if ((i = fmt->f_width) < 0) {
613 i = -i;
614 ljust++;
615 }
616
617 if (!ljust && i > 0 && (int) strlen(str) > i)
618 str[i] = '\0';
619 xp = str;
620 xp += strlen(str) - 1;
621 while (xp > str && isspace((unsigned char) *xp))
622 *xp-- = '\0';
623 if (ljust && i > 0 && (int) strlen(str) > i)
624 str += strlen(str) - i;
625 }
626 break;
627
628 case FT_LV_COMPFLAG:
629 value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
630 break;
631 case FT_LV_COMP:
632 value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
633 break;
634 case FT_LV_LIT:
635 value = fmt->f_value;
636 break;
637 case FT_LV_DAT:
638 value = dat[fmt->f_value];
639 break;
640 case FT_LV_STRLEN:
641 if (str != NULL)
642 value = strlen(str);
643 else
644 value = 0;
645 break;
646 case FT_LV_CHAR_LEFT:
647 value = width - (cp - scanl);
648 break;
649 case FT_LV_PLUS_L:
650 value += fmt->f_value;
651 break;
652 case FT_LV_MINUS_L:
653 value = fmt->f_value - value;
654 break;
655 case FT_LV_DIVIDE_L:
656 if (fmt->f_value)
657 value = value / fmt->f_value;
658 else
659 value = 0;
660 break;
661 case FT_LV_MODULO_L:
662 if (fmt->f_value)
663 value = value % fmt->f_value;
664 else
665 value = 0;
666 break;
667 case FT_SAVESTR:
668 savestr = str;
669 break;
670
671 case FT_LV_SEC:
672 value = fmt->f_comp->c_tws->tw_sec;
673 break;
674 case FT_LV_MIN:
675 value = fmt->f_comp->c_tws->tw_min;
676 break;
677 case FT_LV_HOUR:
678 value = fmt->f_comp->c_tws->tw_hour;
679 break;
680 case FT_LV_MDAY:
681 value = fmt->f_comp->c_tws->tw_mday;
682 break;
683 case FT_LV_MON:
684 value = fmt->f_comp->c_tws->tw_mon + 1;
685 break;
686 case FT_LS_MONTH:
687 str = tw_moty[fmt->f_comp->c_tws->tw_mon];
688 break;
689 case FT_LS_LMONTH:
690 str = lmonth[fmt->f_comp->c_tws->tw_mon];
691 break;
692 case FT_LS_ZONE:
693 str = dtwszone (fmt->f_comp->c_tws);
694 break;
695 case FT_LV_YEAR:
696 value = fmt->f_comp->c_tws->tw_year;
697 break;
698 case FT_LV_WDAY:
699 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
700 set_dotw (tws);
701 value = tws->tw_wday;
702 break;
703 case FT_LS_DAY:
704 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
705 set_dotw (tws);
706 str = tw_dotw[tws->tw_wday];
707 break;
708 case FT_LS_WEEKDAY:
709 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
710 set_dotw (tws);
711 str = tw_ldotw[tws->tw_wday];
712 break;
713 case FT_LV_YDAY:
714 value = fmt->f_comp->c_tws->tw_yday;
715 break;
716 case FT_LV_ZONE:
717 value = fmt->f_comp->c_tws->tw_zone;
718 break;
719 case FT_LV_CLOCK:
720 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
721 value = dmktime(fmt->f_comp->c_tws);
722 break;
723 case FT_LV_RCLOCK:
724 if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
725 value = dmktime(fmt->f_comp->c_tws);
726 value = time((time_t *) 0) - value;
727 break;
728 case FT_LV_DAYF:
729 if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
730 set_dotw (tws);
731 switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
732 case TW_SEXP:
733 value = 1; break;
734 case TW_SIMP:
735 value = 0; break;
736 default:
737 value = -1; break;
738 }
739 case FT_LV_ZONEF:
740 if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
741 value = 1;
742 else
743 value = -1;
744 break;
745 case FT_LV_DST:
746 value = fmt->f_comp->c_tws->tw_flags & TW_DST;
747 break;
748 case FT_LS_822DATE:
749 str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
750 break;
751 case FT_LS_PRETTY:
752 str = dasctime (fmt->f_comp->c_tws, TW_NULL);
753 break;
754
755 case FT_LS_PERS:
756 str = fmt->f_comp->c_mn->m_pers;
757 break;
758 case FT_LS_MBOX:
759 str = fmt->f_comp->c_mn->m_mbox;
760 break;
761 case FT_LS_HOST:
762 str = fmt->f_comp->c_mn->m_host;
763 break;
764 case FT_LS_PATH:
765 str = fmt->f_comp->c_mn->m_path;
766 break;
767 case FT_LS_GNAME:
768 str = fmt->f_comp->c_mn->m_gname;
769 break;
770 case FT_LS_NOTE:
771 str = fmt->f_comp->c_mn->m_note;
772 break;
773 case FT_LS_822ADDR:
774 str = adrformat( fmt->f_comp->c_mn );
775 break;
776 case FT_LV_HOSTTYPE:
777 value = fmt->f_comp->c_mn->m_type;
778 break;
779 case FT_LV_INGRPF:
780 value = fmt->f_comp->c_mn->m_ingrp;
781 break;
782 case FT_LV_NOHOSTF:
783 value = fmt->f_comp->c_mn->m_nohost;
784 break;
785 case FT_LS_ADDR:
786 case FT_LS_FRIENDLY:
787 if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
788 str = fmt->f_comp->c_text;
789 break;
790 }
791 if (fmt->f_type == FT_LS_ADDR)
792 goto unfriendly;
793 if ((str = mn->m_pers) == NULL) {
794 if ((str = mn->m_note)) {
795 strncpy (buffer, str, sizeof(buffer));
796 buffer[sizeof(buffer)-1] = '\0';
797 str = buffer;
798 if (*str == '(')
799 str++;
800 sp = str + strlen(str) - 1;
801 if (*sp == ')') {
802 *sp-- = '\0';
803 while (sp >= str)
804 if (*sp == ' ')
805 *sp-- = '\0';
806 else
807 break;
808 }
809 } else if (!(str = get_x400_friendly (mn->m_mbox,
810 buffer, sizeof(buffer)))) {
811 unfriendly: ;
812 switch (mn->m_type) {
813 case LOCALHOST:
814 str = mn->m_mbox;
815 break;
816 case UUCPHOST:
817 snprintf (buffer, sizeof(buffer), "%s!%s",
818 mn->m_host, mn->m_mbox);
819 str = buffer;
820 break;
821 default:
822 if (mn->m_mbox) {
823 snprintf (buffer, sizeof(buffer), "%s@%s",
824 mn->m_mbox, mn->m_host);
825 str= buffer;
826 }
827 else
828 str = mn->m_text;
829 break;
830 }
831 }
832 }
833 break;
834
835
836 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
837 case FT_LS_UNQUOTE:
838 if (str) {
839 int m;
840 strncpy(buffer, str, sizeof(buffer));
841 /* strncpy doesn't NUL-terminate if it fills the buffer */
842 buffer[sizeof(buffer)-1] = '\0';
843 str = buffer;
844
845 /* we will parse from buffer to buffer2 */
846 n = 0; /* n is the input position in str */
847 m = 0; /* m is the ouput position in buffer2 */
848
849 while ( str[n] != '\0') {
850 switch ( str[n] ) {
851 case '\\':
852 n++;
853 if ( str[n] != '\0')
854 buffer2[m++] = str[n++];
855 break;
856 case '"':
857 n++;
858 break;
859 default:
860 buffer2[m++] = str[n++];
861 break;
862 }
863 }
864 buffer2[m] = '\0';
865 str = buffer2;
866 }
867 break;
868
869 case FT_LOCALDATE:
870 comp = fmt->f_comp;
871 if ((t = comp->c_tws->tw_clock) == 0)
872 t = dmktime(comp->c_tws);
873 tws = dlocaltime(&t);
874 *comp->c_tws = *tws;
875 break;
876
877 case FT_GMTDATE:
878 comp = fmt->f_comp;
879 if ((t = comp->c_tws->tw_clock) == 0)
880 t = dmktime(comp->c_tws);
881 tws = dgmtime(&t);
882 *comp->c_tws = *tws;
883 break;
884
885 case FT_PARSEDATE:
886 comp = fmt->f_comp;
887 if (comp->c_flags & CF_PARSED)
888 break;
889 if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
890 *comp->c_tws = *tws;
891 comp->c_flags &= ~CF_TRUE;
892 } else if ((comp->c_flags & CF_DATEFAB) == 0) {
893 memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
894 comp->c_flags = CF_TRUE;
895 }
896 comp->c_flags |= CF_PARSED;
897 break;
898
899 case FT_FORMATADDR:
900 /* hook for custom address list formatting (see replsbr.c) */
901 str = formataddr (savestr, str);
902 break;
903
904 case FT_CONCATADDR:
905 /* The same as formataddr, but doesn't do duplicate suppression */
906 str = concataddr (savestr, str);
907 break;
908
909 case FT_PUTADDR:
910 /* output the str register as an address component,
911 * splitting it into multiple lines if necessary. The
912 * value reg. contains the max line length. The lit.
913 * field may contain a string to prepend to the result
914 * (e.g., "To: ")
915 */
916 {
917 char *lp, *lastb;
918 int indent, wid, len;
919
920 lp = str;
921 wid = value;
922 len = strlen (str);
923 sp = fmt->f_text;
924 indent = strlen (sp);
925 wid -= indent;
926 if (wid <= 0) {
927 adios(NULL, "putaddr -- num register (%d) must be greater "
928 "than label width (%d)", value, indent);
929 }
930 while( (c = (unsigned char) *sp++) && cp < ep)
931 *cp++ = (char) c;
932 while (len > wid) {
933 /* try to break at a comma; failing that, break at a
934 * space.
935 */
936 lastb = 0; sp = lp + wid;
937 while (sp > lp && (c = (unsigned char) *--sp) != ',') {
938 if (! lastb && isspace(c))
939 lastb = sp - 1;
940 }
941 if (sp == lp) {
942 if (! (sp = lastb)) {
943 sp = lp + wid - 1;
944 while (*sp && *sp != ',' &&
945 !isspace((unsigned char) *sp))
946 sp++;
947 if (*sp != ',')
948 sp--;
949 }
950 }
951 len -= sp - lp + 1;
952 while (cp < ep && lp <= sp)
953 *cp++ = *lp++;
954 while (isspace((unsigned char) *lp))
955 lp++, len--;
956 if (*lp) {
957 if (cp < ep)
958 *cp++ = '\n';
959 for (i=indent; cp < ep && i > 0; i--)
960 *cp++ = ' ';
961 }
962 }
963 cpstripped (&cp, &ep, scanl + max - 1, lp);
964 }
965 break;
966
967 case FT_PARSEADDR:
968 comp = fmt->f_comp;
969 if (comp->c_flags & CF_PARSED)
970 break;
971 if (comp->c_mn != &fmt_mnull)
972 mnfree (comp->c_mn);
973 if ((sp = comp->c_text) && (sp = getname(sp)) &&
974 (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
975 comp->c_mn = mn;
976 while (getname(""))
977 ;
978 comp->c_flags |= CF_PARSED;
979 } else {
980 while (getname("")) /* XXX */
981 ;
982 comp->c_mn = &fmt_mnull;
983 }
984 break;
985
986 case FT_MYMBOX:
987 /*
988 * if there's no component, we say true. Otherwise we
989 * say "true" only if we can parse the address and it
990 * matches one of our addresses.
991 */
992 comp = fmt->f_comp;
993 if (comp->c_mn != &fmt_mnull)
994 mnfree (comp->c_mn);
995 if ((sp = comp->c_text) && (sp = getname(sp)) &&
996 (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
997 comp->c_mn = mn;
998 if (ismymbox(mn))
999 comp->c_flags |= CF_TRUE;
1000 else
1001 comp->c_flags &= ~CF_TRUE;
1002 while ((sp = getname(sp)))
1003 if ((comp->c_flags & CF_TRUE) == 0 &&
1004 (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
1005 if (ismymbox(mn))
1006 comp->c_flags |= CF_TRUE;
1007 } else {
1008 while (getname("")) /* XXX */
1009 ;
1010 if (comp->c_text == 0)
1011 comp->c_flags |= CF_TRUE;
1012 else
1013 comp->c_flags &= ~CF_TRUE;
1014 comp->c_mn = &fmt_mnull;
1015 }
1016 break;
1017 }
1018 fmt++;
1019 }
1020
1021 /* Emit any trailing sequences of zero display length. */
1022 while (fmt->f_type != FT_DONE) {
1023 if (fmt->f_type == FT_LS_LIT) {
1024 str = fmt->f_text;
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 }
1034 fmt++;
1035 }
1036
1037 finished:;
1038 if (cp > scanl && cp[-1] != '\n') {
1039 if (cp - scanl < (int) max - 1) {
1040 *cp++ = '\n';
1041 } else {
1042 cp[-1] = '\n';
1043 }
1044 }
1045 *cp = '\0';
1046 return ((struct format *)0);
1047 }