]>
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
)
357 char *savestr
= NULL
, *str
= NULL
;
358 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
367 /* ep keeps track of displayed characters. They're limited by width.
368 The total number of characters, cp - scanl + 1 (for trailing NULL),
369 includes invisible control characters and is limited by max. */
371 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
373 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
374 switch (fmt
->f_type
) {
377 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
382 case FT_LS_DECODECOMP
:
384 * Trim these components of any newlines.
386 * But don't trim the "body" and "text" components.
391 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
&&
392 (i
= strlen(comp
->c_text
)) > 0) {
393 if (comp
->c_text
[i
- 1] == '\n' &&
394 strcmp(comp
->c_name
, "body") != 0 &&
395 strcmp(comp
->c_name
, "text") != 0)
396 comp
->c_text
[i
- 1] = '\0';
397 comp
->c_flags
|= CF_TRIMMED
;
405 switch (fmt
->f_type
) {
408 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
411 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
417 while( (c
= *sp
++) && cp
< ep
)
426 ljust
++; /* XXX should do something with this */
428 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
430 while( --i
>= 0 && cp
< ep
)
435 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
438 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
443 while ((c
= *sp
++) && cp
< ep
)
447 size_t len
= strlen (str
);
449 /* Don't want to emit part of an escape sequence. So if
450 there isn't enough room in the buffer for the entire
451 string, skip it completely. */
452 if (cp
- scanl
+ len
+ 1 < max
) {
453 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
455 /* This string doesn't count against the width. So
456 increase ep the same amount as cp, only if the
457 scan buffer will always be large enough. */
458 if (ep
- scanl
+ len
+ 1 < max
) {
466 adios (NULL
, "internal error (FT_STRFW)");
469 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
478 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
489 if (!(value
= (str
&& *str
))) {
496 if (!(value
= (str
== NULL
|| *str
== 0))) {
503 if (value
!= fmt
->f_value
) {
510 if (value
== fmt
->f_value
) {
517 if (value
<= fmt
->f_value
) {
524 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
532 value
= match (str
, fmt
->f_text
);
538 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
545 value
= uprf (str
, fmt
->f_text
);
549 value
= (str
!= NULL
&& *str
!= 0);
553 value
= (str
== NULL
|| *str
== 0);
557 value
= (fmt
->f_value
== value
);
561 value
= (fmt
->f_value
!= value
);
565 value
= (fmt
->f_value
> value
);
576 str
= fmt
->f_comp
->c_text
;
582 if (!(str
= getenv (fmt
->f_text
)))
586 if (!(str
= context_find (fmt
->f_text
)))
590 case FT_LS_DECODECOMP
:
591 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
594 str
= fmt
->f_comp
->c_text
;
598 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
606 strncpy(buffer
, str
, sizeof(buffer
));
607 buffer
[sizeof(buffer
)-1] = '\0';
609 while (isspace((unsigned char) *str
))
612 if ((i
= fmt
->f_width
) < 0) {
617 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
620 xp
+= strlen(str
) - 1;
621 while (xp
> str
&& isspace((unsigned char) *xp
))
623 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
624 str
+= strlen(str
) - i
;
629 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
632 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
635 value
= fmt
->f_value
;
638 value
= dat
[fmt
->f_value
];
646 case FT_LV_CHAR_LEFT
:
647 value
= width
- (cp
- scanl
);
650 value
+= fmt
->f_value
;
653 value
= fmt
->f_value
- value
;
657 value
= value
/ fmt
->f_value
;
663 value
= value
% fmt
->f_value
;
672 value
= fmt
->f_comp
->c_tws
->tw_sec
;
675 value
= fmt
->f_comp
->c_tws
->tw_min
;
678 value
= fmt
->f_comp
->c_tws
->tw_hour
;
681 value
= fmt
->f_comp
->c_tws
->tw_mday
;
684 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
687 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
690 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
693 str
= dtwszone (fmt
->f_comp
->c_tws
);
696 value
= fmt
->f_comp
->c_tws
->tw_year
;
699 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
701 value
= tws
->tw_wday
;
704 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
706 str
= tw_dotw
[tws
->tw_wday
];
709 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
711 str
= tw_ldotw
[tws
->tw_wday
];
714 value
= fmt
->f_comp
->c_tws
->tw_yday
;
717 value
= fmt
->f_comp
->c_tws
->tw_zone
;
720 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
721 value
= dmktime(fmt
->f_comp
->c_tws
);
724 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
725 value
= dmktime(fmt
->f_comp
->c_tws
);
726 value
= time((time_t *) 0) - value
;
729 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
731 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
740 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
746 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
749 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
752 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
756 str
= fmt
->f_comp
->c_mn
->m_pers
;
759 str
= fmt
->f_comp
->c_mn
->m_mbox
;
762 str
= fmt
->f_comp
->c_mn
->m_host
;
765 str
= fmt
->f_comp
->c_mn
->m_path
;
768 str
= fmt
->f_comp
->c_mn
->m_gname
;
771 str
= fmt
->f_comp
->c_mn
->m_note
;
774 str
= adrformat( fmt
->f_comp
->c_mn
);
777 value
= fmt
->f_comp
->c_mn
->m_type
;
780 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
783 value
= fmt
->f_comp
->c_mn
->m_nohost
;
787 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
788 str
= fmt
->f_comp
->c_text
;
791 if (fmt
->f_type
== FT_LS_ADDR
)
793 if ((str
= mn
->m_pers
) == NULL
) {
794 if ((str
= mn
->m_note
)) {
795 strncpy (buffer
, str
, sizeof(buffer
));
796 buffer
[sizeof(buffer
)-1] = '\0';
800 sp
= str
+ strlen(str
) - 1;
809 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
810 buffer
, sizeof(buffer
)))) {
812 switch (mn
->m_type
) {
817 snprintf (buffer
, sizeof(buffer
), "%s!%s",
818 mn
->m_host
, mn
->m_mbox
);
823 snprintf (buffer
, sizeof(buffer
), "%s@%s",
824 mn
->m_mbox
, mn
->m_host
);
836 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
840 strncpy(buffer
, str
, sizeof(buffer
));
841 /* strncpy doesn't NUL-terminate if it fills the buffer */
842 buffer
[sizeof(buffer
)-1] = '\0';
845 /* we will parse from buffer to buffer2 */
846 n
= 0; /* n is the input position in str */
847 m
= 0; /* m is the ouput position in buffer2 */
849 while ( str
[n
] != '\0') {
854 buffer2
[m
++] = str
[n
++];
860 buffer2
[m
++] = str
[n
++];
871 if ((t
= comp
->c_tws
->tw_clock
) == 0)
872 t
= dmktime(comp
->c_tws
);
873 tws
= dlocaltime(&t
);
879 if ((t
= comp
->c_tws
->tw_clock
) == 0)
880 t
= dmktime(comp
->c_tws
);
887 if (comp
->c_flags
& CF_PARSED
)
889 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
891 comp
->c_flags
&= ~CF_TRUE
;
892 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
893 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
894 comp
->c_flags
= CF_TRUE
;
896 comp
->c_flags
|= CF_PARSED
;
900 /* hook for custom address list formatting (see replsbr.c) */
901 str
= formataddr (savestr
, str
);
905 /* The same as formataddr, but doesn't do duplicate suppression */
906 str
= concataddr (savestr
, str
);
910 /* output the str register as an address component,
911 * splitting it into multiple lines if necessary. The
912 * value reg. contains the max line length. The lit.
913 * field may contain a string to prepend to the result
918 int indent
, wid
, len
;
924 indent
= strlen (sp
);
927 adios(NULL
, "putaddr -- num register (%d) must be greater "
928 "than label width (%d)", value
, indent
);
930 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
933 /* try to break at a comma; failing that, break at a
936 lastb
= 0; sp
= lp
+ wid
;
937 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
938 if (! lastb
&& isspace(c
))
942 if (! (sp
= lastb
)) {
944 while (*sp
&& *sp
!= ',' &&
945 !isspace((unsigned char) *sp
))
952 while (cp
< ep
&& lp
<= sp
)
954 while (isspace((unsigned char) *lp
))
959 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
963 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
969 if (comp
->c_flags
& CF_PARSED
)
971 if (comp
->c_mn
!= &fmt_mnull
)
973 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
974 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
978 comp
->c_flags
|= CF_PARSED
;
980 while (getname("")) /* XXX */
982 comp
->c_mn
= &fmt_mnull
;
988 * if there's no component, we say true. Otherwise we
989 * say "true" only if we can parse the address and it
990 * matches one of our addresses.
993 if (comp
->c_mn
!= &fmt_mnull
)
995 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
996 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
))) {
999 comp
->c_flags
|= CF_TRUE
;
1001 comp
->c_flags
&= ~CF_TRUE
;
1002 while ((sp
= getname(sp
)))
1003 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
1004 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
)))
1006 comp
->c_flags
|= CF_TRUE
;
1008 while (getname("")) /* XXX */
1010 if (comp
->c_text
== 0)
1011 comp
->c_flags
|= CF_TRUE
;
1013 comp
->c_flags
&= ~CF_TRUE
;
1014 comp
->c_mn
= &fmt_mnull
;
1021 /* Emit any trailing sequences of zero display length. */
1022 while (fmt
->f_type
!= FT_DONE
) {
1023 if (fmt
->f_type
== FT_LS_LIT
) {
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;
1038 if (cp
> scanl
&& cp
[-1] != '\n') {
1039 if (cp
- scanl
< (int) max
- 1) {
1046 return ((struct format
*)0);