]>
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
= (isalpha(c1
) && isupper(c1
)) ? tolower(c1
) : c1
;
54 while ((c2
= *str
++) && c1
!= ((isalpha(c2
) && isupper(c2
)) ? tolower(c2
) : c2
))
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
))
66 while ((c2
= *str
++) && (c1
| 040) != (c2
| 040))
70 s1
= sub
+ 1; s2
= str
;
71 while ((c1
= *s1
++) && (c1
| 040) == (*s2
++ | 040))
81 * copy a number to the destination subject to a maximum width
84 cpnumber(char **dest
, int num
, unsigned int wid
, char fill
, size_t n
) {
97 *--sp
= (i
% 10) + '0';
99 } while (i
> 0 && sp
> cp
);
102 else if ((num
) < 0 && sp
> cp
)
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
117 cptrimmed(char **dest
, char *str
, unsigned int wid
, char fill
, size_t n
,
119 int remaining
; /* remaining output width available */
121 int end
; /* number of input bytes remaining in str */
122 #ifdef MULTIBYTE_SUPPORT
123 int char_len
; /* bytes in current character */
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 */
135 if ((remaining
= (wid
)) < 0) {
136 remaining
= -remaining
;
140 mbtowc(NULL
, NULL
, 0); /* reset shift state */
142 while (*sp
&& remaining
> 0 && end
> 0) {
143 #ifdef MULTIBYTE_SUPPORT
144 char_len
= mbtowc(&wide_char
, sp
, end
);
146 /* Account for multibyte characters taking only one character's
148 if (char_len
> 1 && epmax
- ep
>= char_len
- 1) {
152 if (char_len
<= 0 || (cp
+ char_len
> ep
))
157 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
162 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
163 intentionally warns if they are passed a char. */
165 if (iscntrl(c
) || isspace(c
)) {
178 #ifdef MULTIBYTE_SUPPORT
179 w
= wcwidth(wide_char
);
180 if (w
>= 0 && remaining
>= w
) {
181 strncpy(cp
, sp
, char_len
);
194 if (cp
+ remaining
> ep
)
198 /* copy string to the right */
199 while (--cp
>= *dest
)
200 *(cp
+ remaining
) = *cp
;
201 /* add padding at the beginning */
203 for (c
=remaining
; c
>0; c
--)
208 /* pad remaining space */
209 while (remaining
-- > 0 && cp
< ep
)
216 cpstripped (char **dest
, char *end
, char *max
, char *str
)
218 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
220 #ifdef MULTIBYTE_SUPPORT
223 #endif /* MULTIBYTE_SUPPORT */
230 #ifdef MULTIBYTE_SUPPORT
231 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
232 #endif /* MULTIBYTE_SUPPORT */
235 * Process each character at a time; if we have multibyte support
236 * then deal with that here.
239 while (*str
!= '\0' && len
> 0 && *dest
< end
) {
240 #ifdef MULTIBYTE_SUPPORT
241 char_len
= mbtowc(&wide_char
, str
, len
);
243 /* Account for multibyte characters taking only one character's
245 if (char_len
> 1 && max
- end
>= char_len
- 1) {
249 if (char_len
<= 0 || *dest
+ char_len
> end
)
254 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
256 #else /* MULTIBYTE_SUPPORT */
259 if (iscntrl(c
) || isspace(c
)) {
261 #endif /* MULTIBYTE_SUPPORT */
272 #ifdef MULTIBYTE_SUPPORT
273 memcpy(*dest
, str
, char_len
);
276 #else /* MULTIBYE_SUPPORT */
278 #endif /* MULTIBYTE_SUPPORT */
282 static char *lmonth
[] = { "January", "February","March", "April",
283 "May", "June", "July", "August",
284 "September","October", "November","December" };
287 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
289 char given
[BUFSIZ
], surname
[BUFSIZ
];
298 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
299 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
305 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
308 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
309 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
311 snprintf (buffer
, buffer_len
, "%s", surname
);
317 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
322 if ((idx
= stringdex (key
, mbox
)) < 0
323 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
326 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
331 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
,
332 struct fmt_callbacks
*callbacks
)
336 char *savestr
= NULL
;
337 unsigned char *str
= NULL
;
338 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
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. */
351 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
353 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
354 switch (fmt
->f_type
) {
357 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
362 case FT_LS_DECODECOMP
:
364 * Trim these components of any newlines.
366 * But don't trim the "body" and "text" components.
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
;
385 switch (fmt
->f_type
) {
388 cpstripped (&cp
, ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
391 cptrimmed (&cp
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
392 ep
- cp
, scanl
- cp
+ max
- 1);
397 while( (c
= *sp
++) && cp
< ep
)
406 ljust
++; /* XXX should do something with this */
408 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
410 while( --i
>= 0 && cp
< ep
)
415 cpstripped (&cp
, ep
, scanl
+ max
- 1, str
);
418 cptrimmed (&cp
, str
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
,
419 scanl
- cp
+ max
- 1);
423 while ((c
= *sp
++) && cp
< ep
)
427 size_t len
= strlen (str
);
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;
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
) {
446 adios (NULL
, "internal error (FT_STRFW)");
449 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
458 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
466 if (callbacks
&& callbacks
->trace_func
)
467 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
472 if (!(value
= (str
&& *str
))) {
479 if (!(value
= (str
== NULL
|| *str
== 0))) {
486 if (value
!= fmt
->f_value
) {
493 if (value
== fmt
->f_value
) {
500 if (value
<= fmt
->f_value
) {
507 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
515 value
= match (str
, fmt
->f_text
);
521 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
528 value
= uprf (str
, fmt
->f_text
);
532 value
= (str
!= NULL
&& *str
!= 0);
536 value
= (str
== NULL
|| *str
== 0);
540 value
= (fmt
->f_value
== value
);
544 value
= (fmt
->f_value
!= value
);
548 value
= (fmt
->f_value
> value
);
559 str
= fmt
->f_comp
->c_text
;
565 if (!(str
= getenv (fmt
->f_text
)))
569 if (!(str
= context_find (fmt
->f_text
)))
573 case FT_LS_DECODECOMP
:
574 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
577 str
= fmt
->f_comp
->c_text
;
581 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
589 strncpy(buffer
, str
, sizeof(buffer
));
590 buffer
[sizeof(buffer
)-1] = '\0';
592 while (isspace(*str
))
595 if ((i
= fmt
->f_width
) < 0) {
600 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
603 xp
+= strlen(str
) - 1;
604 while (xp
> str
&& isspace(*xp
))
606 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
607 str
+= strlen(str
) - i
;
612 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
615 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
618 value
= fmt
->f_value
;
621 value
= dat
[fmt
->f_value
];
629 case FT_LV_CHAR_LEFT
:
630 value
= width
- (cp
- scanl
);
633 value
+= fmt
->f_value
;
636 value
= fmt
->f_value
- value
;
640 value
= value
/ fmt
->f_value
;
646 value
= value
% fmt
->f_value
;
655 value
= fmt
->f_comp
->c_tws
->tw_sec
;
658 value
= fmt
->f_comp
->c_tws
->tw_min
;
661 value
= fmt
->f_comp
->c_tws
->tw_hour
;
664 value
= fmt
->f_comp
->c_tws
->tw_mday
;
667 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
670 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
673 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
676 str
= dtwszone (fmt
->f_comp
->c_tws
);
679 value
= fmt
->f_comp
->c_tws
->tw_year
;
682 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
684 value
= tws
->tw_wday
;
687 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
689 str
= tw_dotw
[tws
->tw_wday
];
692 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
694 str
= tw_ldotw
[tws
->tw_wday
];
697 value
= fmt
->f_comp
->c_tws
->tw_yday
;
700 value
= fmt
->f_comp
->c_tws
->tw_zone
;
703 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
704 value
= dmktime(fmt
->f_comp
->c_tws
);
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
;
712 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
714 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
723 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
729 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
732 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
735 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
739 str
= fmt
->f_comp
->c_mn
->m_pers
;
742 str
= fmt
->f_comp
->c_mn
->m_mbox
;
745 str
= fmt
->f_comp
->c_mn
->m_host
;
748 str
= fmt
->f_comp
->c_mn
->m_path
;
751 str
= fmt
->f_comp
->c_mn
->m_gname
;
754 str
= fmt
->f_comp
->c_mn
->m_note
;
757 str
= adrformat( fmt
->f_comp
->c_mn
);
760 value
= fmt
->f_comp
->c_mn
->m_type
;
763 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
766 value
= fmt
->f_comp
->c_mn
->m_nohost
;
770 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
771 str
= fmt
->f_comp
->c_text
;
774 if (fmt
->f_type
== FT_LS_ADDR
)
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';
783 sp
= str
+ strlen(str
) - 1;
792 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
793 buffer
, sizeof(buffer
)))) {
795 switch (mn
->m_type
) {
800 snprintf (buffer
, sizeof(buffer
), "%s!%s",
801 mn
->m_host
, mn
->m_mbox
);
806 snprintf (buffer
, sizeof(buffer
), "%s@%s",
807 mn
->m_mbox
, mn
->m_host
);
819 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
823 strncpy(buffer
, str
, sizeof(buffer
));
824 /* strncpy doesn't NUL-terminate if it fills the buffer */
825 buffer
[sizeof(buffer
)-1] = '\0';
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 */
832 while ( str
[n
] != '\0') {
837 buffer2
[m
++] = str
[n
++];
843 buffer2
[m
++] = str
[n
++];
854 if ((t
= comp
->c_tws
->tw_clock
) == 0)
855 t
= dmktime(comp
->c_tws
);
856 tws
= dlocaltime(&t
);
862 if ((t
= comp
->c_tws
->tw_clock
) == 0)
863 t
= dmktime(comp
->c_tws
);
870 if (comp
->c_flags
& CF_PARSED
)
872 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
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
;
879 comp
->c_flags
|= CF_PARSED
;
883 /* hook for custom address list formatting (see replsbr.c) */
884 if (callbacks
&& callbacks
->formataddr
)
885 str
= callbacks
->formataddr (savestr
, str
);
887 str
= formataddr (savestr
, str
);
891 /* The same as formataddr, but doesn't do duplicate suppression */
892 if (callbacks
&& callbacks
->concataddr
)
893 str
= callbacks
->concataddr (savestr
, str
);
895 str
= concataddr (savestr
, str
);
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
908 int indent
, wid
, len
;
914 indent
= strlen (sp
);
917 adios(NULL
, "putaddr -- num register (%d) must be greater "
918 "than label width (%d)", value
, indent
);
920 while( (c
= *sp
++) && cp
< ep
)
923 /* try to break at a comma; failing that, break at a
926 lastb
= 0; sp
= lp
+ wid
;
927 while (sp
> lp
&& (c
= *--sp
) != ',') {
928 if (! lastb
&& isspace(c
))
932 if (! (sp
= lastb
)) {
934 while (*sp
&& *sp
!= ',' && !isspace(*sp
))
941 while (cp
< ep
&& lp
<= sp
)
948 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
952 cpstripped (&cp
, ep
, scanl
+ max
- 1, lp
);
958 if (comp
->c_flags
& CF_PARSED
)
960 if (comp
->c_mn
!= &fmt_mnull
)
962 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
963 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
967 comp
->c_flags
|= CF_PARSED
;
969 while (getname("")) /* XXX */
971 comp
->c_mn
= &fmt_mnull
;
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.
982 if (comp
->c_mn
!= &fmt_mnull
)
984 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
985 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
))) {
988 comp
->c_flags
|= CF_TRUE
;
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
)))
995 comp
->c_flags
|= CF_TRUE
;
997 while (getname("")) /* XXX */
999 if (comp
->c_text
== 0)
1000 comp
->c_flags
|= CF_TRUE
;
1002 comp
->c_flags
&= ~CF_TRUE
;
1003 comp
->c_mn
= &fmt_mnull
;
1009 * Call our tracing callback function, if one was supplied
1012 if (callbacks
&& callbacks
->trace_func
)
1013 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1018 /* Emit any trailing sequences of zero display length. */
1019 while (fmt
->f_type
!= FT_DONE
) {
1020 if (fmt
->f_type
== FT_LS_LIT
) {
1022 if (callbacks
&& callbacks
->trace_func
)
1023 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
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;
1033 if (callbacks
&& callbacks
->trace_func
)
1034 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1041 if (cp
> scanl
&& cp
[-1] != '\n') {
1042 if (cp
- scanl
< (int) max
- 1) {
1049 return ((struct format
*)0);