]>
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 struct mailname fmt_mnull
= { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0,
34 static int match (char *, char *);
35 static char *get_x400_friendly (char *, char *, int);
36 static int get_x400_comp (char *, char *, char *, int);
40 * test if string "sub" appears anywhere in
41 * string "str" (case insensitive).
45 match (char *str
, char *sub
)
52 c1
= (isascii((unsigned char) c1
) && isalpha((unsigned char) c1
) &&
53 isupper((unsigned char) c1
)) ? tolower((unsigned char) c1
) : c1
;
54 while ((c2
= *str
++) && c1
!= ((isascii((unsigned char) c2
) &&
55 isalpha((unsigned char) c2
) &&
56 isupper((unsigned char) c2
)) ?
57 tolower((unsigned char) c2
) : c2
))
61 s1
= sub
+ 1; s2
= str
;
62 while ((c1
= *s1
++) && ((isascii((unsigned char) c1
) &&
63 isalpha((unsigned char) c1
) &&
64 isupper((unsigned char) c1
)) ?
66 ((isascii((unsigned char) (c2
=*s2
++)) &&
67 isalpha((unsigned char) c2
) &&
68 isupper((unsigned char) c2
)) ?
69 tolower((unsigned char) c2
) : c2
))
76 while ((c2
= *str
++) && (c1
| 040) != (c2
| 040))
80 s1
= sub
+ 1; s2
= str
;
81 while ((c1
= *s1
++) && (c1
| 040) == (*s2
++ | 040))
91 * copy a number to the destination subject to a maximum width
94 cpnumber(char **dest
, int num
, unsigned int wid
, char fill
, size_t n
) {
107 *--sp
= (i
% 10) + '0';
109 } while (i
> 0 && sp
> cp
);
112 else if ((num
) < 0 && sp
> cp
)
122 * copy string from str to dest padding with the fill character to a size
123 * of wid characters. if wid is negative, the string is right aligned
124 * no more than n bytes are copied
127 cptrimmed(char **dest
, char **ep
, char *str
, unsigned int wid
, char fill
,
129 int remaining
; /* remaining output width available */
131 int end
; /* number of input bytes remaining in str */
132 #ifdef MULTIBYTE_SUPPORT
133 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
);
158 * See the relevant comments in cpstripped() to explain what's
159 * going on here; we want to handle the case where we get
160 * characters that mbtowc() cannot handle
165 char_len
= mbtowc(&wide_char
, altstr
, 1);
171 w
= wcwidth(wide_char
);
174 * Multibyte characters can have a variable number of column
175 * widths, so use the column width to bump the end pointer when
178 if (w
>= 0 && char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
182 if (w
>= 0 && cp
+ w
> *ep
)
187 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
192 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
193 intentionally warns if they are passed a char. */
194 c
= (unsigned char) *sp
;
195 if (iscntrl(c
) || isspace(c
)) {
208 #ifdef MULTIBYTE_SUPPORT
209 if (w
>= 0 && remaining
>= w
) {
210 strncpy(cp
, altstr
? altstr
: sp
, char_len
);
225 if (cp
+ remaining
> *ep
)
226 remaining
= *ep
- cp
;
227 endfield
= cp
+ remaining
;
229 /* copy string to the right */
230 while (--cp
>= *dest
)
231 *(cp
+ remaining
) = *cp
;
232 /* add padding at the beginning */
234 for (c
=remaining
; c
>0; c
--)
239 /* pad remaining space */
240 while (remaining
-- > 0 && cp
< *ep
)
247 cpstripped (char **dest
, char **end
, char *max
, char *str
)
249 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
251 #ifdef MULTIBYTE_SUPPORT
255 #endif /* MULTIBYTE_SUPPORT */
262 #ifdef MULTIBYTE_SUPPORT
263 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
264 #endif /* MULTIBYTE_SUPPORT */
267 * Process each character at a time; if we have multibyte support
268 * then deal with that here.
271 while (*str
!= '\0' && len
> 0 && *dest
< *end
) {
272 #ifdef MULTIBYTE_SUPPORT
273 char_len
= mbtowc(&wide_char
, str
, len
);
274 w
= wcwidth(wide_char
);
277 * Account for multibyte characters, and increment the end pointer
278 * by the number of "extra" bytes in this character. That's the
279 * character length (char_len) minus the column width (w).
281 if (w
>= 0 && char_len
> 1 && max
- *end
>= char_len
- w
) {
282 *end
+= char_len
- w
;
286 * If mbrtowc() failed, then we have a character that isn't valid
287 * in the current encoding. Replace it with a '?'. We do that by
288 * setting the alstr variable to the value of the replacement string;
289 * altstr is used below when the bytes are copied into the output
295 char_len
= mbtowc(&wide_char
, altstr
, 1);
298 if (char_len
<= 0 || *dest
+ char_len
> *end
)
303 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
305 #else /* MULTIBYTE_SUPPORT */
306 int c
= (unsigned char) *str
;
308 if (iscntrl(c
) || isspace(c
)) {
310 #endif /* MULTIBYTE_SUPPORT */
321 #ifdef MULTIBYTE_SUPPORT
322 memcpy(*dest
, altstr
? altstr
: str
, char_len
);
326 #else /* MULTIBYE_SUPPORT */
328 #endif /* MULTIBYTE_SUPPORT */
332 static char *lmonth
[] = { "January", "February","March", "April",
333 "May", "June", "July", "August",
334 "September","October", "November","December" };
337 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
339 char given
[BUFSIZ
], surname
[BUFSIZ
];
348 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
349 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
355 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
358 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
359 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
361 snprintf (buffer
, buffer_len
, "%s", surname
);
367 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
372 if ((idx
= stringdex (key
, mbox
)) < 0
373 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
376 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
381 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
,
382 struct fmt_callbacks
*callbacks
)
385 char *savestr
= NULL
, *str
= NULL
;
386 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
395 /* ep keeps track of displayed characters. They're limited by width.
396 The total number of characters, cp - scanl + 1 (for trailing NULL),
397 includes invisible control characters and is limited by max. */
399 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
401 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
402 switch (fmt
->f_type
) {
405 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
410 case FT_LS_DECODECOMP
:
412 * Trim these components of any newlines.
414 * But don't trim the "body" and "text" components.
419 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
&&
420 (i
= strlen(comp
->c_text
)) > 0) {
421 if (comp
->c_text
[i
- 1] == '\n' &&
422 strcmp(comp
->c_name
, "body") != 0 &&
423 strcmp(comp
->c_name
, "text") != 0)
424 comp
->c_text
[i
- 1] = '\0';
425 comp
->c_flags
|= CF_TRIMMED
;
433 switch (fmt
->f_type
) {
436 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
439 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
445 while( (c
= *sp
++) && cp
< ep
)
454 ljust
++; /* XXX should do something with this */
456 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
458 while( --i
>= 0 && cp
< ep
)
463 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
466 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
472 while ((c
= *sp
++) && cp
< ep
)
478 size_t len
= strlen (str
);
480 /* Don't want to emit part of an escape sequence. So if
481 there isn't enough room in the buffer for the entire
482 string, skip it completely. */
483 if (cp
- scanl
+ len
+ 1 < max
) {
484 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
486 /* This string doesn't count against the width.
487 So increase ep the same amount as cp, only if the
488 scan buffer will always be large enough. */
489 if (ep
- scanl
+ len
+ 1 < max
) {
496 adios (NULL
, "internal error (FT_STRFW)");
499 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
508 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
516 if (callbacks
&& callbacks
->trace_func
)
517 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
522 if (!(value
= (str
&& *str
))) {
523 if (callbacks
&& callbacks
->trace_func
)
524 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
532 if (!(value
= (str
== NULL
|| *str
== 0))) {
533 if (callbacks
&& callbacks
->trace_func
)
534 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
542 if (value
!= fmt
->f_value
) {
543 if (callbacks
&& callbacks
->trace_func
)
544 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
552 if (value
== fmt
->f_value
) {
553 if (callbacks
&& callbacks
->trace_func
)
554 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
562 if (value
<= fmt
->f_value
) {
563 if (callbacks
&& callbacks
->trace_func
)
564 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
572 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
573 if (callbacks
&& callbacks
->trace_func
)
574 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
583 value
= match (str
, fmt
->f_text
);
589 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
590 if (callbacks
&& callbacks
->trace_func
)
591 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
599 value
= uprf (str
, fmt
->f_text
);
603 value
= (str
!= NULL
&& *str
!= 0);
607 value
= (str
== NULL
|| *str
== 0);
611 value
= (fmt
->f_value
== value
);
615 value
= (fmt
->f_value
!= value
);
619 value
= (fmt
->f_value
> value
);
623 if (callbacks
&& callbacks
->trace_func
)
624 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
633 str
= fmt
->f_comp
->c_text
;
639 if (!(str
= getenv (fmt
->f_text
)))
643 if (!(str
= context_find (fmt
->f_text
)))
647 case FT_LS_DECODECOMP
:
648 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
651 str
= fmt
->f_comp
->c_text
;
655 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
663 strncpy(buffer
, str
, sizeof(buffer
));
664 buffer
[sizeof(buffer
)-1] = '\0';
666 while (isspace((unsigned char) *str
))
669 if ((i
= fmt
->f_width
) < 0) {
674 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
677 xp
+= strlen(str
) - 1;
678 while (xp
> str
&& isspace((unsigned char) *xp
))
680 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
681 str
+= strlen(str
) - i
;
686 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
689 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
692 value
= fmt
->f_value
;
695 value
= dat
[fmt
->f_value
];
703 case FT_LV_CHAR_LEFT
:
704 value
= width
- (cp
- scanl
);
707 value
+= fmt
->f_value
;
710 value
= fmt
->f_value
- value
;
714 value
= value
/ fmt
->f_value
;
720 value
= value
% fmt
->f_value
;
729 value
= fmt
->f_comp
->c_tws
->tw_sec
;
732 value
= fmt
->f_comp
->c_tws
->tw_min
;
735 value
= fmt
->f_comp
->c_tws
->tw_hour
;
738 value
= fmt
->f_comp
->c_tws
->tw_mday
;
741 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
744 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
747 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
750 str
= dtwszone (fmt
->f_comp
->c_tws
);
753 value
= fmt
->f_comp
->c_tws
->tw_year
;
756 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
758 value
= tws
->tw_wday
;
761 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
763 str
= tw_dotw
[tws
->tw_wday
];
766 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
768 str
= tw_ldotw
[tws
->tw_wday
];
771 value
= fmt
->f_comp
->c_tws
->tw_yday
;
774 value
= fmt
->f_comp
->c_tws
->tw_zone
;
777 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
778 value
= dmktime(fmt
->f_comp
->c_tws
);
781 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
782 value
= dmktime(fmt
->f_comp
->c_tws
);
783 value
= time((time_t *) 0) - value
;
786 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
788 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
797 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
803 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
806 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
809 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
813 str
= fmt
->f_comp
->c_mn
->m_pers
;
816 str
= fmt
->f_comp
->c_mn
->m_mbox
;
819 str
= fmt
->f_comp
->c_mn
->m_host
;
822 str
= fmt
->f_comp
->c_mn
->m_path
;
825 str
= fmt
->f_comp
->c_mn
->m_gname
;
828 str
= fmt
->f_comp
->c_mn
->m_note
;
831 str
= adrformat( fmt
->f_comp
->c_mn
);
834 value
= fmt
->f_comp
->c_mn
->m_type
;
837 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
840 value
= fmt
->f_comp
->c_mn
->m_nohost
;
844 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
845 str
= fmt
->f_comp
->c_text
;
848 if (fmt
->f_type
== FT_LS_ADDR
)
850 if ((str
= mn
->m_pers
) == NULL
) {
851 if ((str
= mn
->m_note
)) {
852 strncpy (buffer
, str
, sizeof(buffer
));
853 buffer
[sizeof(buffer
)-1] = '\0';
857 sp
= str
+ strlen(str
) - 1;
866 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
867 buffer
, sizeof(buffer
)))) {
869 switch (mn
->m_type
) {
874 snprintf (buffer
, sizeof(buffer
), "%s!%s",
875 mn
->m_host
, mn
->m_mbox
);
880 snprintf (buffer
, sizeof(buffer
), "%s@%s",
881 mn
->m_mbox
, mn
->m_host
);
893 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
896 strncpy(buffer
, str
, sizeof(buffer
));
897 /* strncpy doesn't NUL-terminate if it fills the buffer */
898 buffer
[sizeof(buffer
)-1] = '\0';
899 unquote_string(buffer
, buffer2
);
906 if ((t
= comp
->c_tws
->tw_clock
) == 0)
907 t
= dmktime(comp
->c_tws
);
908 tws
= dlocaltime(&t
);
914 if ((t
= comp
->c_tws
->tw_clock
) == 0)
915 t
= dmktime(comp
->c_tws
);
922 if (comp
->c_flags
& CF_PARSED
)
924 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
926 comp
->c_flags
&= ~CF_TRUE
;
927 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
928 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
929 comp
->c_flags
= CF_TRUE
;
931 comp
->c_flags
|= CF_PARSED
;
935 /* hook for custom address list formatting (see replsbr.c) */
936 if (callbacks
&& callbacks
->formataddr
)
937 str
= callbacks
->formataddr (savestr
, str
);
939 str
= formataddr (savestr
, str
);
943 /* The same as formataddr, but doesn't do duplicate suppression */
944 if (callbacks
&& callbacks
->concataddr
)
945 str
= callbacks
->concataddr (savestr
, str
);
947 str
= concataddr (savestr
, str
);
951 /* output the str register as an address component,
952 * splitting it into multiple lines if necessary. The
953 * value reg. contains the max line length. The lit.
954 * field may contain a string to prepend to the result
959 int indent
, wid
, len
;
965 indent
= strlen (sp
);
968 adios(NULL
, "putaddr -- num register (%d) must be greater "
969 "than label width (%d)", value
, indent
);
971 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
974 /* try to break at a comma; failing that, break at a
977 lastb
= 0; sp
= lp
+ wid
;
978 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
979 if (! lastb
&& isspace(c
))
983 if (! (sp
= lastb
)) {
985 while (*sp
&& *sp
!= ',' &&
986 !isspace((unsigned char) *sp
))
993 while (cp
< ep
&& lp
<= sp
)
995 while (isspace((unsigned char) *lp
))
1000 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
1004 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
1010 if (comp
->c_flags
& CF_PARSED
)
1012 if (comp
->c_mn
!= &fmt_mnull
)
1013 mnfree (comp
->c_mn
);
1014 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1015 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
1019 comp
->c_flags
|= CF_PARSED
;
1021 while (getname("")) /* XXX */
1023 comp
->c_mn
= &fmt_mnull
;
1029 * if there's no component, we say true. Otherwise we
1030 * say "true" only if we can parse the address and it
1031 * matches one of our addresses.
1034 if (comp
->c_mn
!= &fmt_mnull
)
1035 mnfree (comp
->c_mn
);
1036 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1037 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
1040 comp
->c_flags
|= CF_TRUE
;
1042 comp
->c_flags
&= ~CF_TRUE
;
1043 while ((sp
= getname(sp
)))
1044 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
1045 (mn
= getm (sp
, NULL
, 0, NULL
, 0)))
1047 comp
->c_flags
|= CF_TRUE
;
1049 while (getname("")) /* XXX */
1051 if (comp
->c_text
== 0)
1052 comp
->c_flags
|= CF_TRUE
;
1054 comp
->c_flags
&= ~CF_TRUE
;
1055 comp
->c_mn
= &fmt_mnull
;
1061 * Call our tracing callback function, if one was supplied
1064 if (callbacks
&& callbacks
->trace_func
)
1065 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1070 /* Emit any trailing sequences of zero display length. */
1071 while (fmt
->f_type
!= FT_DONE
) {
1072 if (fmt
->f_type
== FT_LS_LIT
) {
1074 if (callbacks
&& callbacks
->trace_func
)
1075 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1077 } else if (fmt
->f_type
== FT_STRLITZ
) {
1078 /* Don't want to emit part of an escape sequence. So if
1079 there isn't enough room in the buffer for the entire
1080 string, skip it completely. Need room for null
1081 terminator, and maybe trailing newline (added below). */
1082 if (str
&& (cp
- scanl
+ strlen (str
) + 1 < max
)) {
1083 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1085 if (callbacks
&& callbacks
->trace_func
)
1086 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1093 if (cp
> scanl
&& cp
[-1] != '\n') {
1094 if (cp
- scanl
< (int) max
- 1) {
1101 return ((struct format
*)0);