]>
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
)
335 char *savestr
= NULL
;
336 unsigned char *str
= NULL
;
337 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
346 /* ep keeps track of displayed characters. They're limited by width.
347 The total number of characters, cp - scanl + 1 (for trailing NULL),
348 includes invisible control characters and is limited by max. */
350 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
352 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
353 switch (fmt
->f_type
) {
356 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
361 case FT_LS_DECODECOMP
:
363 * Trim these components of any newlines.
365 * But don't trim the "body" and "text" components.
370 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
) {
371 i
= strlen(comp
->c_text
);
372 if (comp
->c_text
[i
- 1] == '\n' &&
373 strcmp(comp
->c_name
, "body") != 0 &&
374 strcmp(comp
->c_name
, "text") != 0)
375 comp
->c_text
[i
- 1] = '\0';
376 comp
->c_flags
|= CF_TRIMMED
;
384 switch (fmt
->f_type
) {
387 cpstripped (&cp
, ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
390 cptrimmed (&cp
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
391 ep
- cp
, scanl
- cp
+ max
- 1);
396 while( (c
= *sp
++) && cp
< ep
)
405 ljust
++; /* XXX should do something with this */
407 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
409 while( --i
>= 0 && cp
< ep
)
414 cpstripped (&cp
, ep
, scanl
+ max
- 1, str
);
417 cptrimmed (&cp
, str
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
,
418 scanl
- cp
+ max
- 1);
422 while ((c
= *sp
++) && cp
< ep
)
426 size_t len
= strlen (str
);
428 /* Don't want to emit part of an escape sequence. So if
429 there isn't enough room in the buffer for the entire
430 string, skip it completely. */
431 if (cp
- scanl
+ len
+ 1 < max
) {
432 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
434 /* This string doesn't count against the width. So
435 increase ep the same amount as cp, only if the
436 scan buffer will always be large enough. */
437 if (ep
- scanl
+ len
+ 1 < max
) {
445 adios (NULL
, "internal error (FT_STRFW)");
448 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
457 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
468 if (!(value
= (str
&& *str
))) {
475 if (!(value
= (str
== NULL
|| *str
== 0))) {
482 if (value
!= fmt
->f_value
) {
489 if (value
== fmt
->f_value
) {
496 if (value
<= fmt
->f_value
) {
503 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
511 value
= match (str
, fmt
->f_text
);
517 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
524 value
= uprf (str
, fmt
->f_text
);
528 value
= (str
!= NULL
&& *str
!= 0);
532 value
= (str
== NULL
|| *str
== 0);
536 value
= (fmt
->f_value
== value
);
540 value
= (fmt
->f_value
!= value
);
544 value
= (fmt
->f_value
> value
);
555 str
= fmt
->f_comp
->c_text
;
561 if (!(str
= getenv (fmt
->f_text
)))
565 if (!(str
= context_find (fmt
->f_text
)))
569 case FT_LS_DECODECOMP
:
570 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
573 str
= fmt
->f_comp
->c_text
;
577 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
585 strncpy(buffer
, str
, sizeof(buffer
));
586 buffer
[sizeof(buffer
)-1] = '\0';
588 while (isspace(*str
))
591 if ((i
= fmt
->f_width
) < 0) {
596 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
599 xp
+= strlen(str
) - 1;
600 while (xp
> str
&& isspace(*xp
))
602 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
603 str
+= strlen(str
) - i
;
608 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
611 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
614 value
= fmt
->f_value
;
617 value
= dat
[fmt
->f_value
];
625 case FT_LV_CHAR_LEFT
:
626 value
= width
- (cp
- scanl
);
629 value
+= fmt
->f_value
;
632 value
= fmt
->f_value
- value
;
636 value
= value
/ fmt
->f_value
;
642 value
= value
% fmt
->f_value
;
651 value
= fmt
->f_comp
->c_tws
->tw_sec
;
654 value
= fmt
->f_comp
->c_tws
->tw_min
;
657 value
= fmt
->f_comp
->c_tws
->tw_hour
;
660 value
= fmt
->f_comp
->c_tws
->tw_mday
;
663 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
666 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
669 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
672 str
= dtwszone (fmt
->f_comp
->c_tws
);
675 value
= fmt
->f_comp
->c_tws
->tw_year
;
678 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
680 value
= tws
->tw_wday
;
683 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
685 str
= tw_dotw
[tws
->tw_wday
];
688 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
690 str
= tw_ldotw
[tws
->tw_wday
];
693 value
= fmt
->f_comp
->c_tws
->tw_yday
;
696 value
= fmt
->f_comp
->c_tws
->tw_zone
;
699 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
700 value
= dmktime(fmt
->f_comp
->c_tws
);
703 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
704 value
= dmktime(fmt
->f_comp
->c_tws
);
705 value
= time((time_t *) 0) - value
;
708 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
710 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
719 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
725 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
728 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
731 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
735 str
= fmt
->f_comp
->c_mn
->m_pers
;
738 str
= fmt
->f_comp
->c_mn
->m_mbox
;
741 str
= fmt
->f_comp
->c_mn
->m_host
;
744 str
= fmt
->f_comp
->c_mn
->m_path
;
747 str
= fmt
->f_comp
->c_mn
->m_gname
;
750 str
= fmt
->f_comp
->c_mn
->m_note
;
753 str
= adrformat( fmt
->f_comp
->c_mn
);
756 value
= fmt
->f_comp
->c_mn
->m_type
;
759 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
762 value
= fmt
->f_comp
->c_mn
->m_nohost
;
766 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
767 str
= fmt
->f_comp
->c_text
;
770 if (fmt
->f_type
== FT_LS_ADDR
)
772 if ((str
= mn
->m_pers
) == NULL
) {
773 if ((str
= mn
->m_note
)) {
774 strncpy (buffer
, str
, sizeof(buffer
));
775 buffer
[sizeof(buffer
)-1] = '\0';
779 sp
= str
+ strlen(str
) - 1;
788 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
789 buffer
, sizeof(buffer
)))) {
791 switch (mn
->m_type
) {
796 snprintf (buffer
, sizeof(buffer
), "%s!%s",
797 mn
->m_host
, mn
->m_mbox
);
802 snprintf (buffer
, sizeof(buffer
), "%s@%s",
803 mn
->m_mbox
, mn
->m_host
);
815 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
819 strncpy(buffer
, str
, sizeof(buffer
));
820 /* strncpy doesn't NUL-terminate if it fills the buffer */
821 buffer
[sizeof(buffer
)-1] = '\0';
824 /* we will parse from buffer to buffer2 */
825 n
= 0; /* n is the input position in str */
826 m
= 0; /* m is the ouput position in buffer2 */
828 while ( str
[n
] != '\0') {
833 buffer2
[m
++] = str
[n
++];
839 buffer2
[m
++] = str
[n
++];
850 if ((t
= comp
->c_tws
->tw_clock
) == 0)
851 t
= dmktime(comp
->c_tws
);
852 tws
= dlocaltime(&t
);
858 if ((t
= comp
->c_tws
->tw_clock
) == 0)
859 t
= dmktime(comp
->c_tws
);
866 if (comp
->c_flags
& CF_PARSED
)
868 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
870 comp
->c_flags
&= ~CF_TRUE
;
871 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
872 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
873 comp
->c_flags
= CF_TRUE
;
875 comp
->c_flags
|= CF_PARSED
;
879 /* hook for custom address list formatting (see replsbr.c) */
880 str
= formataddr (savestr
, str
);
884 /* The same as formataddr, but doesn't do duplicate suppression */
885 str
= concataddr (savestr
, str
);
889 /* output the str register as an address component,
890 * splitting it into multiple lines if necessary. The
891 * value reg. contains the max line length. The lit.
892 * field may contain a string to prepend to the result
898 int indent
, wid
, len
;
904 indent
= strlen (sp
);
907 adios(NULL
, "putaddr -- num register (%d) must be greater "
908 "than label width (%d)", value
, indent
);
910 while( (c
= *sp
++) && cp
< ep
)
913 /* try to break at a comma; failing that, break at a
916 lastb
= 0; sp
= lp
+ wid
;
917 while (sp
> lp
&& (c
= *--sp
) != ',') {
918 if (! lastb
&& isspace(c
))
922 if (! (sp
= lastb
)) {
924 while (*sp
&& *sp
!= ',' && !isspace(*sp
))
931 while (cp
< ep
&& lp
<= sp
)
938 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
942 cpstripped (&cp
, ep
, scanl
+ max
- 1, lp
);
948 if (comp
->c_flags
& CF_PARSED
)
950 if (comp
->c_mn
!= &fmt_mnull
)
952 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
953 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
957 comp
->c_flags
|= CF_PARSED
;
959 while (getname("")) /* XXX */
961 comp
->c_mn
= &fmt_mnull
;
967 * if there's no component, we say true. Otherwise we
968 * say "true" only if we can parse the address and it
969 * matches one of our addresses.
972 if (comp
->c_mn
!= &fmt_mnull
)
974 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
975 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
))) {
978 comp
->c_flags
|= CF_TRUE
;
980 comp
->c_flags
&= ~CF_TRUE
;
981 while ((sp
= getname(sp
)))
982 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
983 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
)))
985 comp
->c_flags
|= CF_TRUE
;
987 while (getname("")) /* XXX */
989 if (comp
->c_text
== 0)
990 comp
->c_flags
|= CF_TRUE
;
992 comp
->c_flags
&= ~CF_TRUE
;
993 comp
->c_mn
= &fmt_mnull
;
1000 /* Emit any trailing sequences of zero display length. */
1001 while (fmt
->f_type
!= FT_DONE
) {
1002 if (fmt
->f_type
== FT_LS_LIT
) {
1004 } else if (fmt
->f_type
== FT_STRLITZ
) {
1005 /* Don't want to emit part of an escape sequence. So if
1006 there isn't enough room in the buffer for the entire
1007 string, skip it completely. Need room for null
1008 terminator, and maybe trailing newline (added below). */
1009 if (cp
- scanl
+ strlen (str
) + 1 < max
) {
1010 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1017 if (cp
> scanl
&& cp
[-1] != '\n') {
1018 if (cp
- scanl
< (int) max
- 1) {
1025 return ((struct format
*)0);