]>
diplodocus.org Git - nmh/blob - sbr/fmt_scan.c
3 * fmt_scan.c -- format string interpretation
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.
9 * This is the engine that processes the format instructions created by
10 * fmt_compile (found in fmt_compile.c).
14 #include <h/addrsbr.h>
15 #include <h/fmt_scan.h>
17 #include <h/fmt_compile.h>
19 #ifdef HAVE_SYS_TIME_H
20 # include <sys/time.h>
23 #ifdef MULTIBYTE_SUPPORT
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,
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);
41 * test if string "sub" appears anywhere in
42 * string "str" (case insensitive).
46 match (char *str
, char *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
))
62 s1
= sub
+ 1; s2
= str
;
63 while ((c1
= *s1
++) && ((isascii((unsigned char) c1
) &&
64 isalpha((unsigned char) c1
) &&
65 isupper((unsigned char) c1
)) ?
67 ((isascii((unsigned char) (c2
=*s2
++)) &&
68 isalpha((unsigned char) c2
) &&
69 isupper((unsigned char) c2
)) ?
70 tolower((unsigned char) c2
) : c2
))
77 while ((c2
= *str
++) && (c1
| 040) != (c2
| 040))
81 s1
= sub
+ 1; s2
= str
;
82 while ((c1
= *s1
++) && (c1
| 040) == (*s2
++ | 040))
92 * copy a number to the destination subject to a maximum width
95 cpnumber(char **dest
, int num
, unsigned int wid
, char fill
, size_t n
) {
108 *--sp
= (i
% 10) + '0';
110 } while (i
> 0 && sp
> cp
);
113 else if ((num
) < 0 && sp
> cp
)
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
128 cptrimmed(char **dest
, char **ep
, char *str
, unsigned int wid
, char fill
,
130 int remaining
; /* remaining output width available */
132 int end
; /* number of input bytes remaining in str */
133 #ifdef MULTIBYTE_SUPPORT
134 int char_len
; /* bytes in current character */
138 char *sp
; /* current position in source string */
139 char *cp
= *dest
; /* current position in destination string */
144 if ((remaining
= (wid
)) < 0) {
145 remaining
= -remaining
;
149 #ifdef MULTIBYTE_SUPPORT
150 mbtowc(NULL
, NULL
, 0); /* reset shift state */
153 while (*sp
&& remaining
> 0 && end
> 0) {
154 #ifdef MULTIBYTE_SUPPORT
155 char_len
= mbtowc(&wide_char
, sp
, end
);
160 w
= wcwidth(wide_char
);
163 * Multibyte characters can have a variable number of column
164 * widths, so use the column width to bump the end pointer when
167 if (char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
176 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
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
)) {
197 #ifdef MULTIBYTE_SUPPORT
198 if (w
>= 0 && remaining
>= w
) {
199 strncpy(cp
, sp
, char_len
);
213 if (cp
+ remaining
> *ep
)
214 remaining
= *ep
- cp
;
215 endfield
= cp
+ remaining
;
217 /* copy string to the right */
218 while (--cp
>= *dest
)
219 *(cp
+ remaining
) = *cp
;
220 /* add padding at the beginning */
222 for (c
=remaining
; c
>0; c
--)
227 /* pad remaining space */
228 while (remaining
-- > 0 && cp
< *ep
)
235 cpstripped (char **dest
, char **end
, char *max
, char *str
)
237 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
239 #ifdef MULTIBYTE_SUPPORT
242 #endif /* MULTIBYTE_SUPPORT */
249 #ifdef MULTIBYTE_SUPPORT
250 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
251 #endif /* MULTIBYTE_SUPPORT */
254 * Process each character at a time; if we have multibyte support
255 * then deal with that here.
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
);
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).
268 if (char_len
> 1 && max
- *end
>= char_len
- w
) {
269 *end
+= char_len
- w
;
272 if (char_len
<= 0 || *dest
+ char_len
> *end
)
277 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
279 #else /* MULTIBYTE_SUPPORT */
280 int c
= (unsigned char) *str
;
282 if (iscntrl(c
) || isspace(c
)) {
284 #endif /* MULTIBYTE_SUPPORT */
295 #ifdef MULTIBYTE_SUPPORT
296 memcpy(*dest
, str
, char_len
);
299 #else /* MULTIBYE_SUPPORT */
301 #endif /* MULTIBYTE_SUPPORT */
305 static char *lmonth
[] = { "January", "February","March", "April",
306 "May", "June", "July", "August",
307 "September","October", "November","December" };
310 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
312 char given
[BUFSIZ
], surname
[BUFSIZ
];
321 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
322 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
328 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
331 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
332 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
334 snprintf (buffer
, buffer_len
, "%s", surname
);
340 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
345 if ((idx
= stringdex (key
, mbox
)) < 0
346 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
349 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
354 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
,
355 struct fmt_callbacks
*callbacks
)
358 char *savestr
= NULL
, *str
= NULL
;
359 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
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. */
372 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
374 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
375 switch (fmt
->f_type
) {
378 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
383 case FT_LS_DECODECOMP
:
385 * Trim these components of any newlines.
387 * But don't trim the "body" and "text" components.
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
;
406 switch (fmt
->f_type
) {
409 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
412 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
418 while( (c
= *sp
++) && cp
< ep
)
427 ljust
++; /* XXX should do something with this */
429 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
431 while( --i
>= 0 && cp
< ep
)
436 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
439 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
444 while ((c
= *sp
++) && cp
< ep
)
448 size_t len
= strlen (str
);
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;
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
) {
467 adios (NULL
, "internal error (FT_STRFW)");
470 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
479 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
487 if (callbacks
&& callbacks
->trace_func
)
488 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
493 if (!(value
= (str
&& *str
))) {
500 if (!(value
= (str
== NULL
|| *str
== 0))) {
507 if (value
!= fmt
->f_value
) {
514 if (value
== fmt
->f_value
) {
521 if (value
<= fmt
->f_value
) {
528 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
536 value
= match (str
, fmt
->f_text
);
542 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
549 value
= uprf (str
, fmt
->f_text
);
553 value
= (str
!= NULL
&& *str
!= 0);
557 value
= (str
== NULL
|| *str
== 0);
561 value
= (fmt
->f_value
== value
);
565 value
= (fmt
->f_value
!= value
);
569 value
= (fmt
->f_value
> value
);
580 str
= fmt
->f_comp
->c_text
;
586 if (!(str
= getenv (fmt
->f_text
)))
590 if (!(str
= context_find (fmt
->f_text
)))
594 case FT_LS_DECODECOMP
:
595 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
598 str
= fmt
->f_comp
->c_text
;
602 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
610 strncpy(buffer
, str
, sizeof(buffer
));
611 buffer
[sizeof(buffer
)-1] = '\0';
613 while (isspace((unsigned char) *str
))
616 if ((i
= fmt
->f_width
) < 0) {
621 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
624 xp
+= strlen(str
) - 1;
625 while (xp
> str
&& isspace((unsigned char) *xp
))
627 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
628 str
+= strlen(str
) - i
;
633 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
636 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
639 value
= fmt
->f_value
;
642 value
= dat
[fmt
->f_value
];
650 case FT_LV_CHAR_LEFT
:
651 value
= width
- (cp
- scanl
);
654 value
+= fmt
->f_value
;
657 value
= fmt
->f_value
- value
;
661 value
= value
/ fmt
->f_value
;
667 value
= value
% fmt
->f_value
;
676 value
= fmt
->f_comp
->c_tws
->tw_sec
;
679 value
= fmt
->f_comp
->c_tws
->tw_min
;
682 value
= fmt
->f_comp
->c_tws
->tw_hour
;
685 value
= fmt
->f_comp
->c_tws
->tw_mday
;
688 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
691 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
694 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
697 str
= dtwszone (fmt
->f_comp
->c_tws
);
700 value
= fmt
->f_comp
->c_tws
->tw_year
;
703 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
705 value
= tws
->tw_wday
;
708 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
710 str
= tw_dotw
[tws
->tw_wday
];
713 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
715 str
= tw_ldotw
[tws
->tw_wday
];
718 value
= fmt
->f_comp
->c_tws
->tw_yday
;
721 value
= fmt
->f_comp
->c_tws
->tw_zone
;
724 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
725 value
= dmktime(fmt
->f_comp
->c_tws
);
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
;
733 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
735 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
744 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
750 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
753 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
756 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
760 str
= fmt
->f_comp
->c_mn
->m_pers
;
763 str
= fmt
->f_comp
->c_mn
->m_mbox
;
766 str
= fmt
->f_comp
->c_mn
->m_host
;
769 str
= fmt
->f_comp
->c_mn
->m_path
;
772 str
= fmt
->f_comp
->c_mn
->m_gname
;
775 str
= fmt
->f_comp
->c_mn
->m_note
;
778 str
= adrformat( fmt
->f_comp
->c_mn
);
781 value
= fmt
->f_comp
->c_mn
->m_type
;
784 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
787 value
= fmt
->f_comp
->c_mn
->m_nohost
;
791 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
792 str
= fmt
->f_comp
->c_text
;
795 if (fmt
->f_type
== FT_LS_ADDR
)
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';
804 sp
= str
+ strlen(str
) - 1;
813 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
814 buffer
, sizeof(buffer
)))) {
816 switch (mn
->m_type
) {
821 snprintf (buffer
, sizeof(buffer
), "%s!%s",
822 mn
->m_host
, mn
->m_mbox
);
827 snprintf (buffer
, sizeof(buffer
), "%s@%s",
828 mn
->m_mbox
, mn
->m_host
);
840 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
844 strncpy(buffer
, str
, sizeof(buffer
));
845 /* strncpy doesn't NUL-terminate if it fills the buffer */
846 buffer
[sizeof(buffer
)-1] = '\0';
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 */
853 while ( str
[n
] != '\0') {
858 buffer2
[m
++] = str
[n
++];
864 buffer2
[m
++] = str
[n
++];
875 if ((t
= comp
->c_tws
->tw_clock
) == 0)
876 t
= dmktime(comp
->c_tws
);
877 tws
= dlocaltime(&t
);
883 if ((t
= comp
->c_tws
->tw_clock
) == 0)
884 t
= dmktime(comp
->c_tws
);
891 if (comp
->c_flags
& CF_PARSED
)
893 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
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
;
900 comp
->c_flags
|= CF_PARSED
;
904 /* hook for custom address list formatting (see replsbr.c) */
905 if (callbacks
&& callbacks
->formataddr
)
906 str
= callbacks
->formataddr (savestr
, str
);
908 str
= formataddr (savestr
, str
);
912 /* The same as formataddr, but doesn't do duplicate suppression */
913 if (callbacks
&& callbacks
->concataddr
)
914 str
= callbacks
->concataddr (savestr
, str
);
916 str
= concataddr (savestr
, str
);
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
928 int indent
, wid
, len
;
934 indent
= strlen (sp
);
937 adios(NULL
, "putaddr -- num register (%d) must be greater "
938 "than label width (%d)", value
, indent
);
940 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
943 /* try to break at a comma; failing that, break at a
946 lastb
= 0; sp
= lp
+ wid
;
947 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
948 if (! lastb
&& isspace(c
))
952 if (! (sp
= lastb
)) {
954 while (*sp
&& *sp
!= ',' &&
955 !isspace((unsigned char) *sp
))
962 while (cp
< ep
&& lp
<= sp
)
964 while (isspace((unsigned char) *lp
))
969 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
973 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
979 if (comp
->c_flags
& CF_PARSED
)
981 if (comp
->c_mn
!= &fmt_mnull
)
983 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
984 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
988 comp
->c_flags
|= CF_PARSED
;
990 while (getname("")) /* XXX */
992 comp
->c_mn
= &fmt_mnull
;
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.
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
))) {
1009 comp
->c_flags
|= CF_TRUE
;
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
)))
1016 comp
->c_flags
|= CF_TRUE
;
1018 while (getname("")) /* XXX */
1020 if (comp
->c_text
== 0)
1021 comp
->c_flags
|= CF_TRUE
;
1023 comp
->c_flags
&= ~CF_TRUE
;
1024 comp
->c_mn
= &fmt_mnull
;
1030 * Call our tracing callback function, if one was supplied
1033 if (callbacks
&& callbacks
->trace_func
)
1034 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1039 /* Emit any trailing sequences of zero display length. */
1040 while (fmt
->f_type
!= FT_DONE
) {
1041 if (fmt
->f_type
== FT_LS_LIT
) {
1043 if (callbacks
&& callbacks
->trace_func
)
1044 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
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;
1054 if (callbacks
&& callbacks
->trace_func
)
1055 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1062 if (cp
> scanl
&& cp
[-1] != '\n') {
1063 if (cp
- scanl
< (int) max
- 1) {
1070 return ((struct format
*)0);