]>
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 (char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
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 (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
,
471 while ((c
= *sp
++) && cp
< ep
)
475 size_t len
= strlen (str
);
477 /* Don't want to emit part of an escape sequence. So if
478 there isn't enough room in the buffer for the entire
479 string, skip it completely. */
480 if (cp
- scanl
+ len
+ 1 < max
) {
481 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
483 /* This string doesn't count against the width. So
484 increase ep the same amount as cp, only if the
485 scan buffer will always be large enough. */
486 if (ep
- scanl
+ len
+ 1 < max
) {
494 adios (NULL
, "internal error (FT_STRFW)");
497 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
506 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
514 if (callbacks
&& callbacks
->trace_func
)
515 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
520 if (!(value
= (str
&& *str
))) {
527 if (!(value
= (str
== NULL
|| *str
== 0))) {
534 if (value
!= fmt
->f_value
) {
541 if (value
== fmt
->f_value
) {
548 if (value
<= fmt
->f_value
) {
555 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
563 value
= match (str
, fmt
->f_text
);
569 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
576 value
= uprf (str
, fmt
->f_text
);
580 value
= (str
!= NULL
&& *str
!= 0);
584 value
= (str
== NULL
|| *str
== 0);
588 value
= (fmt
->f_value
== value
);
592 value
= (fmt
->f_value
!= value
);
596 value
= (fmt
->f_value
> value
);
607 str
= fmt
->f_comp
->c_text
;
613 if (!(str
= getenv (fmt
->f_text
)))
617 if (!(str
= context_find (fmt
->f_text
)))
621 case FT_LS_DECODECOMP
:
622 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
625 str
= fmt
->f_comp
->c_text
;
629 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
637 strncpy(buffer
, str
, sizeof(buffer
));
638 buffer
[sizeof(buffer
)-1] = '\0';
640 while (isspace((unsigned char) *str
))
643 if ((i
= fmt
->f_width
) < 0) {
648 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
651 xp
+= strlen(str
) - 1;
652 while (xp
> str
&& isspace((unsigned char) *xp
))
654 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
655 str
+= strlen(str
) - i
;
660 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
663 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
666 value
= fmt
->f_value
;
669 value
= dat
[fmt
->f_value
];
677 case FT_LV_CHAR_LEFT
:
678 value
= width
- (cp
- scanl
);
681 value
+= fmt
->f_value
;
684 value
= fmt
->f_value
- value
;
688 value
= value
/ fmt
->f_value
;
694 value
= value
% fmt
->f_value
;
703 value
= fmt
->f_comp
->c_tws
->tw_sec
;
706 value
= fmt
->f_comp
->c_tws
->tw_min
;
709 value
= fmt
->f_comp
->c_tws
->tw_hour
;
712 value
= fmt
->f_comp
->c_tws
->tw_mday
;
715 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
718 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
721 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
724 str
= dtwszone (fmt
->f_comp
->c_tws
);
727 value
= fmt
->f_comp
->c_tws
->tw_year
;
730 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
732 value
= tws
->tw_wday
;
735 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
737 str
= tw_dotw
[tws
->tw_wday
];
740 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
742 str
= tw_ldotw
[tws
->tw_wday
];
745 value
= fmt
->f_comp
->c_tws
->tw_yday
;
748 value
= fmt
->f_comp
->c_tws
->tw_zone
;
751 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
752 value
= dmktime(fmt
->f_comp
->c_tws
);
755 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
756 value
= dmktime(fmt
->f_comp
->c_tws
);
757 value
= time((time_t *) 0) - value
;
760 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
762 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
771 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
777 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
780 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
783 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
787 str
= fmt
->f_comp
->c_mn
->m_pers
;
790 str
= fmt
->f_comp
->c_mn
->m_mbox
;
793 str
= fmt
->f_comp
->c_mn
->m_host
;
796 str
= fmt
->f_comp
->c_mn
->m_path
;
799 str
= fmt
->f_comp
->c_mn
->m_gname
;
802 str
= fmt
->f_comp
->c_mn
->m_note
;
805 str
= adrformat( fmt
->f_comp
->c_mn
);
808 value
= fmt
->f_comp
->c_mn
->m_type
;
811 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
814 value
= fmt
->f_comp
->c_mn
->m_nohost
;
818 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
819 str
= fmt
->f_comp
->c_text
;
822 if (fmt
->f_type
== FT_LS_ADDR
)
824 if ((str
= mn
->m_pers
) == NULL
) {
825 if ((str
= mn
->m_note
)) {
826 strncpy (buffer
, str
, sizeof(buffer
));
827 buffer
[sizeof(buffer
)-1] = '\0';
831 sp
= str
+ strlen(str
) - 1;
840 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
841 buffer
, sizeof(buffer
)))) {
843 switch (mn
->m_type
) {
848 snprintf (buffer
, sizeof(buffer
), "%s!%s",
849 mn
->m_host
, mn
->m_mbox
);
854 snprintf (buffer
, sizeof(buffer
), "%s@%s",
855 mn
->m_mbox
, mn
->m_host
);
867 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
870 strncpy(buffer
, str
, sizeof(buffer
));
871 /* strncpy doesn't NUL-terminate if it fills the buffer */
872 buffer
[sizeof(buffer
)-1] = '\0';
873 unquote_string(buffer
, buffer2
);
880 if ((t
= comp
->c_tws
->tw_clock
) == 0)
881 t
= dmktime(comp
->c_tws
);
882 tws
= dlocaltime(&t
);
888 if ((t
= comp
->c_tws
->tw_clock
) == 0)
889 t
= dmktime(comp
->c_tws
);
896 if (comp
->c_flags
& CF_PARSED
)
898 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
900 comp
->c_flags
&= ~CF_TRUE
;
901 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
902 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
903 comp
->c_flags
= CF_TRUE
;
905 comp
->c_flags
|= CF_PARSED
;
909 /* hook for custom address list formatting (see replsbr.c) */
910 if (callbacks
&& callbacks
->formataddr
)
911 str
= callbacks
->formataddr (savestr
, str
);
913 str
= formataddr (savestr
, str
);
917 /* The same as formataddr, but doesn't do duplicate suppression */
918 if (callbacks
&& callbacks
->concataddr
)
919 str
= callbacks
->concataddr (savestr
, str
);
921 str
= concataddr (savestr
, str
);
925 /* output the str register as an address component,
926 * splitting it into multiple lines if necessary. The
927 * value reg. contains the max line length. The lit.
928 * field may contain a string to prepend to the result
933 int indent
, wid
, len
;
939 indent
= strlen (sp
);
942 adios(NULL
, "putaddr -- num register (%d) must be greater "
943 "than label width (%d)", value
, indent
);
945 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
948 /* try to break at a comma; failing that, break at a
951 lastb
= 0; sp
= lp
+ wid
;
952 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
953 if (! lastb
&& isspace(c
))
957 if (! (sp
= lastb
)) {
959 while (*sp
&& *sp
!= ',' &&
960 !isspace((unsigned char) *sp
))
967 while (cp
< ep
&& lp
<= sp
)
969 while (isspace((unsigned char) *lp
))
974 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
978 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
984 if (comp
->c_flags
& CF_PARSED
)
986 if (comp
->c_mn
!= &fmt_mnull
)
988 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
989 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
993 comp
->c_flags
|= CF_PARSED
;
995 while (getname("")) /* XXX */
997 comp
->c_mn
= &fmt_mnull
;
1003 * if there's no component, we say true. Otherwise we
1004 * say "true" only if we can parse the address and it
1005 * matches one of our addresses.
1008 if (comp
->c_mn
!= &fmt_mnull
)
1009 mnfree (comp
->c_mn
);
1010 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1011 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
1014 comp
->c_flags
|= CF_TRUE
;
1016 comp
->c_flags
&= ~CF_TRUE
;
1017 while ((sp
= getname(sp
)))
1018 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
1019 (mn
= getm (sp
, NULL
, 0, NULL
, 0)))
1021 comp
->c_flags
|= CF_TRUE
;
1023 while (getname("")) /* XXX */
1025 if (comp
->c_text
== 0)
1026 comp
->c_flags
|= CF_TRUE
;
1028 comp
->c_flags
&= ~CF_TRUE
;
1029 comp
->c_mn
= &fmt_mnull
;
1035 * Call our tracing callback function, if one was supplied
1038 if (callbacks
&& callbacks
->trace_func
)
1039 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1044 /* Emit any trailing sequences of zero display length. */
1045 while (fmt
->f_type
!= FT_DONE
) {
1046 if (fmt
->f_type
== FT_LS_LIT
) {
1048 if (callbacks
&& callbacks
->trace_func
)
1049 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1051 } else if (fmt
->f_type
== FT_STRLITZ
) {
1052 /* Don't want to emit part of an escape sequence. So if
1053 there isn't enough room in the buffer for the entire
1054 string, skip it completely. Need room for null
1055 terminator, and maybe trailing newline (added below). */
1056 if (cp
- scanl
+ strlen (str
) + 1 < max
) {
1057 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1059 if (callbacks
&& callbacks
->trace_func
)
1060 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1067 if (cp
> scanl
&& cp
[-1] != '\n') {
1068 if (cp
- scanl
< (int) max
- 1) {
1075 return ((struct format
*)0);