]>
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 **ep
, char *str
, unsigned int wid
, char fill
,
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 */
133 if ((remaining
= (wid
)) < 0) {
134 remaining
= -remaining
;
138 #ifdef MULTIBYTE_SUPPORT
139 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
);
149 w
= wcwidth(wide_char
);
152 * Multibyte characters can have a variable number of column
153 * widths, so use the column width to bump the end pointer when
156 if (char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
165 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
170 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
171 intentionally warns if they are passed a char. */
173 if (iscntrl(c
) || isspace(c
)) {
186 #ifdef MULTIBYTE_SUPPORT
187 if (w
>= 0 && remaining
>= w
) {
188 strncpy(cp
, sp
, char_len
);
202 if (cp
+ remaining
> *ep
)
203 remaining
= *ep
- cp
;
204 endfield
= cp
+ remaining
;
206 /* copy string to the right */
207 while (--cp
>= *dest
)
208 *(cp
+ remaining
) = *cp
;
209 /* add padding at the beginning */
211 for (c
=remaining
; c
>0; c
--)
216 /* pad remaining space */
217 while (remaining
-- > 0 && cp
< *ep
)
224 cpstripped (char **dest
, char **end
, char *max
, char *str
)
226 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
228 #ifdef MULTIBYTE_SUPPORT
231 #endif /* MULTIBYTE_SUPPORT */
238 #ifdef MULTIBYTE_SUPPORT
239 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
240 #endif /* MULTIBYTE_SUPPORT */
243 * Process each character at a time; if we have multibyte support
244 * then deal with that here.
247 while (*str
!= '\0' && len
> 0 && *dest
< *end
) {
248 #ifdef MULTIBYTE_SUPPORT
249 char_len
= mbtowc(&wide_char
, str
, len
);
250 w
= wcwidth(wide_char
);
253 * Account for multibyte characters, and increment the end pointer
254 * by the number of "extra" bytes in this character. That's the
255 * character length (char_len) minus the column width (w).
257 if (char_len
> 1 && max
- *end
>= char_len
- w
) {
258 *end
+= char_len
- w
;
261 if (char_len
<= 0 || *dest
+ char_len
> *end
)
266 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
268 #else /* MULTIBYTE_SUPPORT */
271 if (iscntrl(c
) || isspace(c
)) {
273 #endif /* MULTIBYTE_SUPPORT */
284 #ifdef MULTIBYTE_SUPPORT
285 memcpy(*dest
, str
, char_len
);
288 #else /* MULTIBYE_SUPPORT */
290 #endif /* MULTIBYTE_SUPPORT */
294 static char *lmonth
[] = { "January", "February","March", "April",
295 "May", "June", "July", "August",
296 "September","October", "November","December" };
299 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
301 char given
[BUFSIZ
], surname
[BUFSIZ
];
310 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
311 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
317 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
320 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
321 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
323 snprintf (buffer
, buffer_len
, "%s", surname
);
329 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
334 if ((idx
= stringdex (key
, mbox
)) < 0
335 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
338 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
343 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
)
347 char *savestr
= NULL
;
348 unsigned char *str
= NULL
;
349 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
358 /* ep keeps track of displayed characters. They're limited by width.
359 The total number of characters, cp - scanl + 1 (for trailing NULL),
360 includes invisible control characters and is limited by max. */
362 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
364 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
365 switch (fmt
->f_type
) {
368 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
373 case FT_LS_DECODECOMP
:
375 * Trim these components of any newlines.
377 * But don't trim the "body" and "text" components.
382 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
&&
383 (i
= strlen(comp
->c_text
)) > 0) {
384 if (comp
->c_text
[i
- 1] == '\n' &&
385 strcmp(comp
->c_name
, "body") != 0 &&
386 strcmp(comp
->c_name
, "text") != 0)
387 comp
->c_text
[i
- 1] = '\0';
388 comp
->c_flags
|= CF_TRIMMED
;
396 switch (fmt
->f_type
) {
399 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
402 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
408 while( (c
= *sp
++) && cp
< ep
)
417 ljust
++; /* XXX should do something with this */
419 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
421 while( --i
>= 0 && cp
< ep
)
426 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
429 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
434 while ((c
= *sp
++) && cp
< ep
)
438 size_t len
= strlen (str
);
440 /* Don't want to emit part of an escape sequence. So if
441 there isn't enough room in the buffer for the entire
442 string, skip it completely. */
443 if (cp
- scanl
+ len
+ 1 < max
) {
444 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
446 /* This string doesn't count against the width. So
447 increase ep the same amount as cp, only if the
448 scan buffer will always be large enough. */
449 if (ep
- scanl
+ len
+ 1 < max
) {
457 adios (NULL
, "internal error (FT_STRFW)");
460 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
469 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
480 if (!(value
= (str
&& *str
))) {
487 if (!(value
= (str
== NULL
|| *str
== 0))) {
494 if (value
!= fmt
->f_value
) {
501 if (value
== fmt
->f_value
) {
508 if (value
<= fmt
->f_value
) {
515 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
523 value
= match (str
, fmt
->f_text
);
529 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
536 value
= uprf (str
, fmt
->f_text
);
540 value
= (str
!= NULL
&& *str
!= 0);
544 value
= (str
== NULL
|| *str
== 0);
548 value
= (fmt
->f_value
== value
);
552 value
= (fmt
->f_value
!= value
);
556 value
= (fmt
->f_value
> value
);
567 str
= fmt
->f_comp
->c_text
;
573 if (!(str
= getenv (fmt
->f_text
)))
577 if (!(str
= context_find (fmt
->f_text
)))
581 case FT_LS_DECODECOMP
:
582 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
585 str
= fmt
->f_comp
->c_text
;
589 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
597 strncpy(buffer
, str
, sizeof(buffer
));
598 buffer
[sizeof(buffer
)-1] = '\0';
600 while (isspace(*str
))
603 if ((i
= fmt
->f_width
) < 0) {
608 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
611 xp
+= strlen(str
) - 1;
612 while (xp
> str
&& isspace(*xp
))
614 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
615 str
+= strlen(str
) - i
;
620 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
623 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
626 value
= fmt
->f_value
;
629 value
= dat
[fmt
->f_value
];
637 case FT_LV_CHAR_LEFT
:
638 value
= width
- (cp
- scanl
);
641 value
+= fmt
->f_value
;
644 value
= fmt
->f_value
- value
;
648 value
= value
/ fmt
->f_value
;
654 value
= value
% fmt
->f_value
;
663 value
= fmt
->f_comp
->c_tws
->tw_sec
;
666 value
= fmt
->f_comp
->c_tws
->tw_min
;
669 value
= fmt
->f_comp
->c_tws
->tw_hour
;
672 value
= fmt
->f_comp
->c_tws
->tw_mday
;
675 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
678 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
681 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
684 str
= dtwszone (fmt
->f_comp
->c_tws
);
687 value
= fmt
->f_comp
->c_tws
->tw_year
;
690 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
692 value
= tws
->tw_wday
;
695 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
697 str
= tw_dotw
[tws
->tw_wday
];
700 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
702 str
= tw_ldotw
[tws
->tw_wday
];
705 value
= fmt
->f_comp
->c_tws
->tw_yday
;
708 value
= fmt
->f_comp
->c_tws
->tw_zone
;
711 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
712 value
= dmktime(fmt
->f_comp
->c_tws
);
715 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
716 value
= dmktime(fmt
->f_comp
->c_tws
);
717 value
= time((time_t *) 0) - value
;
720 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
722 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
731 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
737 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
740 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
743 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
747 str
= fmt
->f_comp
->c_mn
->m_pers
;
750 str
= fmt
->f_comp
->c_mn
->m_mbox
;
753 str
= fmt
->f_comp
->c_mn
->m_host
;
756 str
= fmt
->f_comp
->c_mn
->m_path
;
759 str
= fmt
->f_comp
->c_mn
->m_gname
;
762 str
= fmt
->f_comp
->c_mn
->m_note
;
765 str
= adrformat( fmt
->f_comp
->c_mn
);
768 value
= fmt
->f_comp
->c_mn
->m_type
;
771 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
774 value
= fmt
->f_comp
->c_mn
->m_nohost
;
778 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
779 str
= fmt
->f_comp
->c_text
;
782 if (fmt
->f_type
== FT_LS_ADDR
)
784 if ((str
= mn
->m_pers
) == NULL
) {
785 if ((str
= mn
->m_note
)) {
786 strncpy (buffer
, str
, sizeof(buffer
));
787 buffer
[sizeof(buffer
)-1] = '\0';
791 sp
= str
+ strlen(str
) - 1;
800 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
801 buffer
, sizeof(buffer
)))) {
803 switch (mn
->m_type
) {
808 snprintf (buffer
, sizeof(buffer
), "%s!%s",
809 mn
->m_host
, mn
->m_mbox
);
814 snprintf (buffer
, sizeof(buffer
), "%s@%s",
815 mn
->m_mbox
, mn
->m_host
);
827 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
831 strncpy(buffer
, str
, sizeof(buffer
));
832 /* strncpy doesn't NUL-terminate if it fills the buffer */
833 buffer
[sizeof(buffer
)-1] = '\0';
836 /* we will parse from buffer to buffer2 */
837 n
= 0; /* n is the input position in str */
838 m
= 0; /* m is the ouput position in buffer2 */
840 while ( str
[n
] != '\0') {
845 buffer2
[m
++] = str
[n
++];
851 buffer2
[m
++] = str
[n
++];
862 if ((t
= comp
->c_tws
->tw_clock
) == 0)
863 t
= dmktime(comp
->c_tws
);
864 tws
= dlocaltime(&t
);
870 if ((t
= comp
->c_tws
->tw_clock
) == 0)
871 t
= dmktime(comp
->c_tws
);
878 if (comp
->c_flags
& CF_PARSED
)
880 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
882 comp
->c_flags
&= ~CF_TRUE
;
883 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
884 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
885 comp
->c_flags
= CF_TRUE
;
887 comp
->c_flags
|= CF_PARSED
;
891 /* hook for custom address list formatting (see replsbr.c) */
892 str
= formataddr (savestr
, str
);
896 /* The same as formataddr, but doesn't do duplicate suppression */
897 str
= concataddr (savestr
, str
);
901 /* output the str register as an address component,
902 * splitting it into multiple lines if necessary. The
903 * value reg. contains the max line length. The lit.
904 * field may contain a string to prepend to the result
910 int indent
, wid
, len
;
916 indent
= strlen (sp
);
919 adios(NULL
, "putaddr -- num register (%d) must be greater "
920 "than label width (%d)", value
, indent
);
922 while( (c
= *sp
++) && cp
< ep
)
925 /* try to break at a comma; failing that, break at a
928 lastb
= 0; sp
= lp
+ wid
;
929 while (sp
> lp
&& (c
= *--sp
) != ',') {
930 if (! lastb
&& isspace(c
))
934 if (! (sp
= lastb
)) {
936 while (*sp
&& *sp
!= ',' && !isspace(*sp
))
943 while (cp
< ep
&& lp
<= sp
)
950 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
954 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
960 if (comp
->c_flags
& CF_PARSED
)
962 if (comp
->c_mn
!= &fmt_mnull
)
964 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
965 (mn
= getm (sp
, NULL
, 0, fmt_norm
, NULL
))) {
969 comp
->c_flags
|= CF_PARSED
;
971 while (getname("")) /* XXX */
973 comp
->c_mn
= &fmt_mnull
;
979 * if there's no component, we say true. Otherwise we
980 * say "true" only if we can parse the address and it
981 * matches one of our addresses.
984 if (comp
->c_mn
!= &fmt_mnull
)
986 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
987 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
))) {
990 comp
->c_flags
|= CF_TRUE
;
992 comp
->c_flags
&= ~CF_TRUE
;
993 while ((sp
= getname(sp
)))
994 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
995 (mn
= getm (sp
, NULL
, 0, AD_NAME
, NULL
)))
997 comp
->c_flags
|= CF_TRUE
;
999 while (getname("")) /* XXX */
1001 if (comp
->c_text
== 0)
1002 comp
->c_flags
|= CF_TRUE
;
1004 comp
->c_flags
&= ~CF_TRUE
;
1005 comp
->c_mn
= &fmt_mnull
;
1012 /* Emit any trailing sequences of zero display length. */
1013 while (fmt
->f_type
!= FT_DONE
) {
1014 if (fmt
->f_type
== FT_LS_LIT
) {
1016 } else if (fmt
->f_type
== FT_STRLITZ
) {
1017 /* Don't want to emit part of an escape sequence. So if
1018 there isn't enough room in the buffer for the entire
1019 string, skip it completely. Need room for null
1020 terminator, and maybe trailing newline (added below). */
1021 if (cp
- scanl
+ strlen (str
) + 1 < max
) {
1022 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1029 if (cp
> scanl
&& cp
[-1] != '\n') {
1030 if (cp
- scanl
< (int) max
- 1) {
1037 return ((struct format
*)0);