]>
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 */
139 char *sp
; /* current position in source string */
140 char *cp
= *dest
; /* current position in destination string */
145 if ((remaining
= (wid
)) < 0) {
146 remaining
= -remaining
;
150 #ifdef MULTIBYTE_SUPPORT
151 mbtowc(NULL
, NULL
, 0); /* reset shift state */
154 while (*sp
&& remaining
> 0 && end
> 0) {
155 #ifdef MULTIBYTE_SUPPORT
156 char_len
= mbtowc(&wide_char
, sp
, end
);
159 * See the relevant comments in cpstripped() to explain what's
160 * going on here; we want to handle the case where we get
161 * characters that mbtowc() cannot handle
166 char_len
= mbtowc(&wide_char
, altstr
, 1);
172 w
= wcwidth(wide_char
);
175 * Multibyte characters can have a variable number of column
176 * widths, so use the column width to bump the end pointer when
179 if (char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
188 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
193 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
194 intentionally warns if they are passed a char. */
195 c
= (unsigned char) *sp
;
196 if (iscntrl(c
) || isspace(c
)) {
209 #ifdef MULTIBYTE_SUPPORT
210 if (w
>= 0 && remaining
>= w
) {
211 strncpy(cp
, altstr
? altstr
: sp
, char_len
);
226 if (cp
+ remaining
> *ep
)
227 remaining
= *ep
- cp
;
228 endfield
= cp
+ remaining
;
230 /* copy string to the right */
231 while (--cp
>= *dest
)
232 *(cp
+ remaining
) = *cp
;
233 /* add padding at the beginning */
235 for (c
=remaining
; c
>0; c
--)
240 /* pad remaining space */
241 while (remaining
-- > 0 && cp
< *ep
)
248 cpstripped (char **dest
, char **end
, char *max
, char *str
)
250 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
252 #ifdef MULTIBYTE_SUPPORT
256 #endif /* MULTIBYTE_SUPPORT */
263 #ifdef MULTIBYTE_SUPPORT
264 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
265 #endif /* MULTIBYTE_SUPPORT */
268 * Process each character at a time; if we have multibyte support
269 * then deal with that here.
272 while (*str
!= '\0' && len
> 0 && *dest
< *end
) {
273 #ifdef MULTIBYTE_SUPPORT
274 char_len
= mbtowc(&wide_char
, str
, len
);
275 w
= wcwidth(wide_char
);
278 * Account for multibyte characters, and increment the end pointer
279 * by the number of "extra" bytes in this character. That's the
280 * character length (char_len) minus the column width (w).
282 if (char_len
> 1 && max
- *end
>= char_len
- w
) {
283 *end
+= char_len
- w
;
287 * If mbrtowc() failed, then we have a character that isn't valid
288 * in the current encoding. Replace it with a '?'. We do that by
289 * setting the alstr variable to the value of the replacement string;
290 * altstr is used below when the bytes are copied into the output
296 char_len
= mbtowc(&wide_char
, altstr
, 1);
299 if (char_len
<= 0 || *dest
+ char_len
> *end
)
304 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
306 #else /* MULTIBYTE_SUPPORT */
307 int c
= (unsigned char) *str
;
309 if (iscntrl(c
) || isspace(c
)) {
311 #endif /* MULTIBYTE_SUPPORT */
322 #ifdef MULTIBYTE_SUPPORT
323 memcpy(*dest
, altstr
? altstr
: str
, char_len
);
327 #else /* MULTIBYE_SUPPORT */
329 #endif /* MULTIBYTE_SUPPORT */
333 static char *lmonth
[] = { "January", "February","March", "April",
334 "May", "June", "July", "August",
335 "September","October", "November","December" };
338 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
340 char given
[BUFSIZ
], surname
[BUFSIZ
];
349 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
350 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
356 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
359 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
360 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
362 snprintf (buffer
, buffer_len
, "%s", surname
);
368 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
373 if ((idx
= stringdex (key
, mbox
)) < 0
374 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
377 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
382 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
,
383 struct fmt_callbacks
*callbacks
)
386 char *savestr
= NULL
, *str
= NULL
;
387 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
396 /* ep keeps track of displayed characters. They're limited by width.
397 The total number of characters, cp - scanl + 1 (for trailing NULL),
398 includes invisible control characters and is limited by max. */
400 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
402 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
403 switch (fmt
->f_type
) {
406 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
411 case FT_LS_DECODECOMP
:
413 * Trim these components of any newlines.
415 * But don't trim the "body" and "text" components.
420 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
&&
421 (i
= strlen(comp
->c_text
)) > 0) {
422 if (comp
->c_text
[i
- 1] == '\n' &&
423 strcmp(comp
->c_name
, "body") != 0 &&
424 strcmp(comp
->c_name
, "text") != 0)
425 comp
->c_text
[i
- 1] = '\0';
426 comp
->c_flags
|= CF_TRIMMED
;
434 switch (fmt
->f_type
) {
437 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
440 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
446 while( (c
= *sp
++) && cp
< ep
)
455 ljust
++; /* XXX should do something with this */
457 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
459 while( --i
>= 0 && cp
< ep
)
464 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
467 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
472 while ((c
= *sp
++) && cp
< ep
)
476 size_t len
= strlen (str
);
478 /* Don't want to emit part of an escape sequence. So if
479 there isn't enough room in the buffer for the entire
480 string, skip it completely. */
481 if (cp
- scanl
+ len
+ 1 < max
) {
482 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
484 /* This string doesn't count against the width. So
485 increase ep the same amount as cp, only if the
486 scan buffer will always be large enough. */
487 if (ep
- scanl
+ len
+ 1 < max
) {
495 adios (NULL
, "internal error (FT_STRFW)");
498 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
507 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
515 if (callbacks
&& callbacks
->trace_func
)
516 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
521 if (!(value
= (str
&& *str
))) {
528 if (!(value
= (str
== NULL
|| *str
== 0))) {
535 if (value
!= fmt
->f_value
) {
542 if (value
== fmt
->f_value
) {
549 if (value
<= fmt
->f_value
) {
556 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
564 value
= match (str
, fmt
->f_text
);
570 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
577 value
= uprf (str
, fmt
->f_text
);
581 value
= (str
!= NULL
&& *str
!= 0);
585 value
= (str
== NULL
|| *str
== 0);
589 value
= (fmt
->f_value
== value
);
593 value
= (fmt
->f_value
!= value
);
597 value
= (fmt
->f_value
> value
);
608 str
= fmt
->f_comp
->c_text
;
614 if (!(str
= getenv (fmt
->f_text
)))
618 if (!(str
= context_find (fmt
->f_text
)))
622 case FT_LS_DECODECOMP
:
623 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
626 str
= fmt
->f_comp
->c_text
;
630 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
638 strncpy(buffer
, str
, sizeof(buffer
));
639 buffer
[sizeof(buffer
)-1] = '\0';
641 while (isspace((unsigned char) *str
))
644 if ((i
= fmt
->f_width
) < 0) {
649 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
652 xp
+= strlen(str
) - 1;
653 while (xp
> str
&& isspace((unsigned char) *xp
))
655 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
656 str
+= strlen(str
) - i
;
661 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
664 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
667 value
= fmt
->f_value
;
670 value
= dat
[fmt
->f_value
];
678 case FT_LV_CHAR_LEFT
:
679 value
= width
- (cp
- scanl
);
682 value
+= fmt
->f_value
;
685 value
= fmt
->f_value
- value
;
689 value
= value
/ fmt
->f_value
;
695 value
= value
% fmt
->f_value
;
704 value
= fmt
->f_comp
->c_tws
->tw_sec
;
707 value
= fmt
->f_comp
->c_tws
->tw_min
;
710 value
= fmt
->f_comp
->c_tws
->tw_hour
;
713 value
= fmt
->f_comp
->c_tws
->tw_mday
;
716 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
719 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
722 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
725 str
= dtwszone (fmt
->f_comp
->c_tws
);
728 value
= fmt
->f_comp
->c_tws
->tw_year
;
731 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
733 value
= tws
->tw_wday
;
736 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
738 str
= tw_dotw
[tws
->tw_wday
];
741 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
743 str
= tw_ldotw
[tws
->tw_wday
];
746 value
= fmt
->f_comp
->c_tws
->tw_yday
;
749 value
= fmt
->f_comp
->c_tws
->tw_zone
;
752 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
753 value
= dmktime(fmt
->f_comp
->c_tws
);
756 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
757 value
= dmktime(fmt
->f_comp
->c_tws
);
758 value
= time((time_t *) 0) - value
;
761 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
763 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
772 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
778 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
781 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
784 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
788 str
= fmt
->f_comp
->c_mn
->m_pers
;
791 str
= fmt
->f_comp
->c_mn
->m_mbox
;
794 str
= fmt
->f_comp
->c_mn
->m_host
;
797 str
= fmt
->f_comp
->c_mn
->m_path
;
800 str
= fmt
->f_comp
->c_mn
->m_gname
;
803 str
= fmt
->f_comp
->c_mn
->m_note
;
806 str
= adrformat( fmt
->f_comp
->c_mn
);
809 value
= fmt
->f_comp
->c_mn
->m_type
;
812 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
815 value
= fmt
->f_comp
->c_mn
->m_nohost
;
819 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
820 str
= fmt
->f_comp
->c_text
;
823 if (fmt
->f_type
== FT_LS_ADDR
)
825 if ((str
= mn
->m_pers
) == NULL
) {
826 if ((str
= mn
->m_note
)) {
827 strncpy (buffer
, str
, sizeof(buffer
));
828 buffer
[sizeof(buffer
)-1] = '\0';
832 sp
= str
+ strlen(str
) - 1;
841 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
842 buffer
, sizeof(buffer
)))) {
844 switch (mn
->m_type
) {
849 snprintf (buffer
, sizeof(buffer
), "%s!%s",
850 mn
->m_host
, mn
->m_mbox
);
855 snprintf (buffer
, sizeof(buffer
), "%s@%s",
856 mn
->m_mbox
, mn
->m_host
);
868 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
871 strncpy(buffer
, str
, sizeof(buffer
));
872 /* strncpy doesn't NUL-terminate if it fills the buffer */
873 buffer
[sizeof(buffer
)-1] = '\0';
874 unquote_string(buffer
, buffer2
);
881 if ((t
= comp
->c_tws
->tw_clock
) == 0)
882 t
= dmktime(comp
->c_tws
);
883 tws
= dlocaltime(&t
);
889 if ((t
= comp
->c_tws
->tw_clock
) == 0)
890 t
= dmktime(comp
->c_tws
);
897 if (comp
->c_flags
& CF_PARSED
)
899 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
901 comp
->c_flags
&= ~CF_TRUE
;
902 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
903 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
904 comp
->c_flags
= CF_TRUE
;
906 comp
->c_flags
|= CF_PARSED
;
910 /* hook for custom address list formatting (see replsbr.c) */
911 if (callbacks
&& callbacks
->formataddr
)
912 str
= callbacks
->formataddr (savestr
, str
);
914 str
= formataddr (savestr
, str
);
918 /* The same as formataddr, but doesn't do duplicate suppression */
919 if (callbacks
&& callbacks
->concataddr
)
920 str
= callbacks
->concataddr (savestr
, str
);
922 str
= concataddr (savestr
, str
);
926 /* output the str register as an address component,
927 * splitting it into multiple lines if necessary. The
928 * value reg. contains the max line length. The lit.
929 * field may contain a string to prepend to the result
934 int indent
, wid
, len
;
940 indent
= strlen (sp
);
943 adios(NULL
, "putaddr -- num register (%d) must be greater "
944 "than label width (%d)", value
, indent
);
946 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
949 /* try to break at a comma; failing that, break at a
952 lastb
= 0; sp
= lp
+ wid
;
953 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
954 if (! lastb
&& isspace(c
))
958 if (! (sp
= lastb
)) {
960 while (*sp
&& *sp
!= ',' &&
961 !isspace((unsigned char) *sp
))
968 while (cp
< ep
&& lp
<= sp
)
970 while (isspace((unsigned char) *lp
))
975 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
979 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
985 if (comp
->c_flags
& CF_PARSED
)
987 if (comp
->c_mn
!= &fmt_mnull
)
989 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
990 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
994 comp
->c_flags
|= CF_PARSED
;
996 while (getname("")) /* XXX */
998 comp
->c_mn
= &fmt_mnull
;
1004 * if there's no component, we say true. Otherwise we
1005 * say "true" only if we can parse the address and it
1006 * matches one of our addresses.
1009 if (comp
->c_mn
!= &fmt_mnull
)
1010 mnfree (comp
->c_mn
);
1011 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1012 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
))) {
1015 comp
->c_flags
|= CF_TRUE
;
1017 comp
->c_flags
&= ~CF_TRUE
;
1018 while ((sp
= getname(sp
)))
1019 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
1020 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
)))
1022 comp
->c_flags
|= CF_TRUE
;
1024 while (getname("")) /* XXX */
1026 if (comp
->c_text
== 0)
1027 comp
->c_flags
|= CF_TRUE
;
1029 comp
->c_flags
&= ~CF_TRUE
;
1030 comp
->c_mn
= &fmt_mnull
;
1036 * Call our tracing callback function, if one was supplied
1039 if (callbacks
&& callbacks
->trace_func
)
1040 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1045 /* Emit any trailing sequences of zero display length. */
1046 while (fmt
->f_type
!= FT_DONE
) {
1047 if (fmt
->f_type
== FT_LS_LIT
) {
1049 if (callbacks
&& callbacks
->trace_func
)
1050 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1052 } else if (fmt
->f_type
== FT_STRLITZ
) {
1053 /* Don't want to emit part of an escape sequence. So if
1054 there isn't enough room in the buffer for the entire
1055 string, skip it completely. Need room for null
1056 terminator, and maybe trailing newline (added below). */
1057 if (cp
- scanl
+ strlen (str
) + 1 < max
) {
1058 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1060 if (callbacks
&& callbacks
->trace_func
)
1061 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1068 if (cp
> scanl
&& cp
[-1] != '\n') {
1069 if (cp
- scanl
< (int) max
- 1) {
1076 return ((struct format
*)0);