]>
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 struct mailname fmt_mnull
= { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0,
34 static int match (char *, char *);
35 static char *get_x400_friendly (char *, char *, int);
36 static int get_x400_comp (char *, char *, char *, int);
40 * test if string "sub" appears anywhere in
41 * string "str" (case insensitive).
45 match (char *str
, char *sub
)
51 c1
= (isascii((unsigned char) c1
) && isalpha((unsigned char) c1
) &&
52 isupper((unsigned char) c1
)) ? tolower((unsigned char) c1
) : c1
;
53 while ((c2
= *str
++) && c1
!= ((isascii((unsigned char) c2
) &&
54 isalpha((unsigned char) c2
) &&
55 isupper((unsigned char) c2
)) ?
56 tolower((unsigned char) c2
) : c2
))
60 s1
= sub
+ 1; s2
= str
;
61 while ((c1
= *s1
++) && ((isascii((unsigned char) c1
) &&
62 isalpha((unsigned char) c1
) &&
63 isupper((unsigned char) c1
)) ?
65 ((isascii((unsigned char) (c2
=*s2
++)) &&
66 isalpha((unsigned char) c2
) &&
67 isupper((unsigned char) c2
)) ?
68 tolower((unsigned char) c2
) : c2
))
77 * copy a number to the destination subject to a maximum width
80 cpnumber(char **dest
, int num
, unsigned int wid
, char fill
, size_t n
) {
93 *--sp
= (i
% 10) + '0';
95 } while (i
> 0 && sp
> cp
);
98 else if ((num
) < 0 && sp
> cp
)
108 * copy string from str to dest padding with the fill character to a size
109 * of wid characters. if wid is negative, the string is right aligned
110 * no more than n bytes are copied
113 cptrimmed(char **dest
, char **ep
, char *str
, unsigned int wid
, char fill
,
115 int remaining
; /* remaining output width available */
117 int end
; /* number of input bytes remaining in str */
118 #ifdef MULTIBYTE_SUPPORT
119 int char_len
; /* bytes in current character */
124 char *sp
; /* current position in source string */
125 char *cp
= *dest
; /* current position in destination string */
130 if ((remaining
= (wid
)) < 0) {
131 remaining
= -remaining
;
135 #ifdef MULTIBYTE_SUPPORT
136 mbtowc(NULL
, NULL
, 0); /* reset shift state */
139 while (*sp
&& remaining
> 0 && end
> 0) {
140 #ifdef MULTIBYTE_SUPPORT
141 char_len
= mbtowc(&wide_char
, sp
, end
);
144 * See the relevant comments in cpstripped() to explain what's
145 * going on here; we want to handle the case where we get
146 * characters that mbtowc() cannot handle
151 char_len
= mbtowc(&wide_char
, altstr
, 1);
157 w
= wcwidth(wide_char
);
160 * Multibyte characters can have a variable number of column
161 * widths, so use the column width to bump the end pointer when
164 if (w
>= 0 && char_len
> 1 && epmax
- *ep
>= char_len
- w
) {
168 if (w
>= 0 && cp
+ w
> *ep
)
173 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
178 /* isnctrl(), etc., take an int argument. Cygwin's ctype.h
179 intentionally warns if they are passed a char. */
180 c
= (unsigned char) *sp
;
181 if (iscntrl(c
) || isspace(c
)) {
194 #ifdef MULTIBYTE_SUPPORT
195 if (w
>= 0 && remaining
>= w
) {
196 strncpy(cp
, altstr
? altstr
: sp
, char_len
);
211 if (cp
+ remaining
> *ep
)
212 remaining
= *ep
- cp
;
213 endfield
= cp
+ remaining
;
215 /* copy string to the right */
216 while (--cp
>= *dest
)
217 *(cp
+ remaining
) = *cp
;
218 /* add padding at the beginning */
220 for (c
=remaining
; c
>0; c
--)
225 /* pad remaining space */
226 while (remaining
-- > 0 && cp
< *ep
)
233 cpstripped (char **dest
, char **end
, char *max
, char *str
)
235 int prevCtrl
= 1; /* This is 1 so we strip out leading spaces */
237 #ifdef MULTIBYTE_SUPPORT
241 #endif /* MULTIBYTE_SUPPORT */
248 #ifdef MULTIBYTE_SUPPORT
249 mbtowc(NULL
, NULL
, 0); /* Reset shift state */
250 #endif /* MULTIBYTE_SUPPORT */
253 * Process each character at a time; if we have multibyte support
254 * then deal with that here.
257 while (*str
!= '\0' && len
> 0 && *dest
< *end
) {
258 #ifdef MULTIBYTE_SUPPORT
259 char_len
= mbtowc(&wide_char
, str
, len
);
260 w
= wcwidth(wide_char
);
263 * Account for multibyte characters, and increment the end pointer
264 * by the number of "extra" bytes in this character. That's the
265 * character length (char_len) minus the column width (w).
267 if (w
>= 0 && char_len
> 1 && max
- *end
>= char_len
- w
) {
268 *end
+= char_len
- w
;
272 * If mbrtowc() failed, then we have a character that isn't valid
273 * in the current encoding. Replace it with a '?'. We do that by
274 * setting the alstr variable to the value of the replacement string;
275 * altstr is used below when the bytes are copied into the output
281 char_len
= mbtowc(&wide_char
, altstr
, 1);
284 if (char_len
<= 0 || *dest
+ char_len
> *end
)
289 if (iswcntrl(wide_char
) || iswspace(wide_char
)) {
291 #else /* MULTIBYTE_SUPPORT */
292 int c
= (unsigned char) *str
;
294 if (iscntrl(c
) || isspace(c
)) {
296 #endif /* MULTIBYTE_SUPPORT */
307 #ifdef MULTIBYTE_SUPPORT
308 memcpy(*dest
, altstr
? altstr
: str
, char_len
);
312 #else /* MULTIBYE_SUPPORT */
314 #endif /* MULTIBYTE_SUPPORT */
318 static char *lmonth
[] = { "January", "February","March", "April",
319 "May", "June", "July", "August",
320 "September","October", "November","December" };
323 get_x400_friendly (char *mbox
, char *buffer
, int buffer_len
)
325 char given
[BUFSIZ
], surname
[BUFSIZ
];
334 if (get_x400_comp (mbox
, "/PN=", buffer
, buffer_len
)) {
335 for (mbox
= buffer
; (mbox
= strchr(mbox
, '.')); )
341 if (!get_x400_comp (mbox
, "/S=", surname
, sizeof(surname
)))
344 if (get_x400_comp (mbox
, "/G=", given
, sizeof(given
)))
345 snprintf (buffer
, buffer_len
, "%s %s", given
, surname
);
347 snprintf (buffer
, buffer_len
, "%s", surname
);
353 get_x400_comp (char *mbox
, char *key
, char *buffer
, int buffer_len
)
358 if ((idx
= stringdex (key
, mbox
)) < 0
359 || !(cp
= strchr(mbox
+= idx
+ strlen (key
), '/')))
362 snprintf (buffer
, buffer_len
, "%*.*s", (int)(cp
- mbox
), (int)(cp
- mbox
), mbox
);
367 fmt_scan (struct format
*format
, char *scanl
, size_t max
, int width
, int *dat
,
368 struct fmt_callbacks
*callbacks
)
371 char *savestr
= NULL
, *str
= NULL
;
372 char buffer
[BUFSIZ
], buffer2
[BUFSIZ
];
381 /* ep keeps track of displayed characters. They're limited by width.
382 The total number of characters, cp - scanl + 1 (for trailing NULL),
383 includes invisible control characters and is limited by max. */
385 ep
= scanl
+ (width
<= (int) max
? width
: (int) max
) - 1;
387 for (fmt
= format
; fmt
->f_type
!= FT_DONE
; fmt
++)
388 switch (fmt
->f_type
) {
391 fmt
->f_comp
->c_flags
&= ~CF_PARSED
;
396 case FT_LS_DECODECOMP
:
398 * Trim these components of any newlines.
400 * But don't trim the "body" and "text" components.
405 if (! (comp
->c_flags
& CF_TRIMMED
) && comp
->c_text
&&
406 (i
= strlen(comp
->c_text
)) > 0) {
407 if (comp
->c_text
[i
- 1] == '\n' &&
408 strcmp(comp
->c_name
, "body") != 0 &&
409 strcmp(comp
->c_name
, "text") != 0)
410 comp
->c_text
[i
- 1] = '\0';
411 comp
->c_flags
|= CF_TRIMMED
;
419 switch (fmt
->f_type
) {
422 cpstripped (&cp
, &ep
, scanl
+ max
- 1, fmt
->f_comp
->c_text
);
425 cptrimmed (&cp
, &ep
, fmt
->f_comp
->c_text
, fmt
->f_width
, fmt
->f_fill
,
431 while( (c
= *sp
++) && cp
< ep
)
440 ljust
++; /* XXX should do something with this */
442 while( (c
= *sp
++) && --i
>= 0 && cp
< ep
)
444 while( --i
>= 0 && cp
< ep
)
449 cpstripped (&cp
, &ep
, scanl
+ max
- 1, str
);
452 cptrimmed (&cp
, &ep
, str
, fmt
->f_width
, fmt
->f_fill
,
458 while ((c
= *sp
++) && cp
< ep
)
464 size_t len
= strlen (str
);
466 /* Don't want to emit part of an escape sequence. So if
467 there isn't enough room in the buffer for the entire
468 string, skip it completely. */
469 if (cp
- scanl
+ len
+ 1 < max
) {
470 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
472 /* This string doesn't count against the width.
473 So increase ep the same amount as cp, only if the
474 scan buffer will always be large enough. */
475 if (ep
- scanl
+ len
+ 1 < max
) {
482 adios (NULL
, "internal error (FT_STRFW)");
485 n
= snprintf(cp
, ep
- cp
+ 1, "%d", value
);
497 unsigned int whole
, tenths
;
499 unsigned int val
= (unsigned int)value
;
502 switch (fmt
->f_type
) {
503 case FT_LS_KILO
: scale
= 1000; kibisuff
= ""; break;
504 case FT_LS_KIBI
: scale
= 1024; kibisuff
= "i"; break;
508 snprintf(buffer
, sizeof(buffer
), "%u", val
);
511 /* find correct scale for size (Kilo/Mega/Giga/Tera) */
512 for (unitcp
= "KMGT"; val
> (scale
* scale
); val
/= scale
) {
518 strcpy(buffer
, "huge");
520 /* val is scale times too big. we want tenths */
528 tenths
= val
- (whole
* 10);
531 snprintf(buffer
, sizeof(buffer
), "%u.%u%c%s",
532 whole
, tenths
, *unitcp
, kibisuff
);
534 snprintf(buffer
, sizeof(buffer
), "%u%c%s",
535 whole
, *unitcp
, kibisuff
);
543 cpnumber (&cp
, value
, fmt
->f_width
, fmt
->f_fill
, ep
- cp
);
551 if (callbacks
&& callbacks
->trace_func
)
552 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
557 if (!(value
= (str
&& *str
))) {
558 if (callbacks
&& callbacks
->trace_func
)
559 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
567 if (!(value
= (str
== NULL
|| *str
== 0))) {
568 if (callbacks
&& callbacks
->trace_func
)
569 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
577 if (value
!= fmt
->f_value
) {
578 if (callbacks
&& callbacks
->trace_func
)
579 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
587 if (value
== fmt
->f_value
) {
588 if (callbacks
&& callbacks
->trace_func
)
589 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
597 if (value
<= fmt
->f_value
) {
598 if (callbacks
&& callbacks
->trace_func
)
599 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
607 if (!(value
= (str
&& match (str
, fmt
->f_text
)))) {
608 if (callbacks
&& callbacks
->trace_func
)
609 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
618 value
= match (str
, fmt
->f_text
);
624 if (!(value
= (str
&& uprf (str
, fmt
->f_text
)))) {
625 if (callbacks
&& callbacks
->trace_func
)
626 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
634 value
= uprf (str
, fmt
->f_text
);
638 value
= (str
!= NULL
&& *str
!= 0);
642 value
= (str
== NULL
|| *str
== 0);
646 value
= (fmt
->f_value
== value
);
650 value
= (fmt
->f_value
!= value
);
654 value
= (fmt
->f_value
> value
);
658 if (callbacks
&& callbacks
->trace_func
)
659 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
668 str
= fmt
->f_comp
->c_text
;
674 if (!(str
= getenv (fmt
->f_text
)))
678 if (!(str
= context_find (fmt
->f_text
)))
682 case FT_LS_DECODECOMP
:
683 if (decode_rfc2047(fmt
->f_comp
->c_text
, buffer2
, sizeof(buffer2
)))
686 str
= fmt
->f_comp
->c_text
;
690 if (str
&& decode_rfc2047(str
, buffer2
, sizeof(buffer2
)))
698 strncpy(buffer
, str
, sizeof(buffer
));
699 buffer
[sizeof(buffer
)-1] = '\0';
701 while (isspace((unsigned char) *str
))
704 if ((i
= fmt
->f_width
) < 0) {
709 if (!ljust
&& i
> 0 && (int) strlen(str
) > i
)
712 xp
+= strlen(str
) - 1;
713 while (xp
> str
&& isspace((unsigned char) *xp
))
715 if (ljust
&& i
> 0 && (int) strlen(str
) > i
)
716 str
+= strlen(str
) - i
;
721 value
= (fmt
->f_comp
->c_flags
& CF_TRUE
) != 0;
724 value
= (comp
= fmt
->f_comp
)->c_text
? atoi(comp
->c_text
) : 0;
727 value
= fmt
->f_value
;
730 value
= dat
[fmt
->f_value
];
738 case FT_LV_CHAR_LEFT
:
739 value
= width
- (cp
- scanl
);
742 value
+= fmt
->f_value
;
745 value
= fmt
->f_value
- value
;
749 value
= value
/ fmt
->f_value
;
755 value
= value
% fmt
->f_value
;
764 value
= fmt
->f_comp
->c_tws
->tw_sec
;
767 value
= fmt
->f_comp
->c_tws
->tw_min
;
770 value
= fmt
->f_comp
->c_tws
->tw_hour
;
773 value
= fmt
->f_comp
->c_tws
->tw_mday
;
776 value
= fmt
->f_comp
->c_tws
->tw_mon
+ 1;
779 str
= tw_moty
[fmt
->f_comp
->c_tws
->tw_mon
];
782 str
= lmonth
[fmt
->f_comp
->c_tws
->tw_mon
];
785 str
= dtwszone (fmt
->f_comp
->c_tws
);
788 value
= fmt
->f_comp
->c_tws
->tw_year
;
791 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
793 value
= tws
->tw_wday
;
796 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
798 str
= tw_dotw
[tws
->tw_wday
];
801 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
803 str
= tw_ldotw
[tws
->tw_wday
];
806 value
= fmt
->f_comp
->c_tws
->tw_yday
;
809 value
= fmt
->f_comp
->c_tws
->tw_zone
;
812 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
813 value
= dmktime(fmt
->f_comp
->c_tws
);
816 if ((value
= fmt
->f_comp
->c_tws
->tw_clock
) == 0)
817 value
= dmktime(fmt
->f_comp
->c_tws
);
818 value
= time((time_t *) 0) - value
;
821 if (!(((tws
= fmt
->f_comp
->c_tws
)->tw_flags
) & (TW_SEXP
|TW_SIMP
)))
823 switch (fmt
->f_comp
->c_tws
->tw_flags
& TW_SDAY
) {
832 if ((fmt
->f_comp
->c_tws
->tw_flags
& TW_SZONE
) == TW_SZEXP
)
838 value
= fmt
->f_comp
->c_tws
->tw_flags
& TW_DST
;
841 str
= dasctime (fmt
->f_comp
->c_tws
, TW_ZONE
);
844 str
= dasctime (fmt
->f_comp
->c_tws
, TW_NULL
);
848 str
= fmt
->f_comp
->c_mn
->m_pers
;
851 str
= fmt
->f_comp
->c_mn
->m_mbox
;
854 str
= fmt
->f_comp
->c_mn
->m_host
;
857 str
= fmt
->f_comp
->c_mn
->m_path
;
860 str
= fmt
->f_comp
->c_mn
->m_gname
;
863 str
= fmt
->f_comp
->c_mn
->m_note
;
866 str
= adrformat( fmt
->f_comp
->c_mn
);
869 value
= fmt
->f_comp
->c_mn
->m_type
;
872 value
= fmt
->f_comp
->c_mn
->m_ingrp
;
875 value
= fmt
->f_comp
->c_mn
->m_nohost
;
879 if ((mn
= fmt
->f_comp
->c_mn
) == &fmt_mnull
) {
880 str
= fmt
->f_comp
->c_text
;
883 if (fmt
->f_type
== FT_LS_ADDR
)
885 if ((str
= mn
->m_pers
) == NULL
) {
886 if ((str
= mn
->m_note
)) {
887 strncpy (buffer
, str
, sizeof(buffer
));
888 buffer
[sizeof(buffer
)-1] = '\0';
892 sp
= str
+ strlen(str
) - 1;
901 } else if (!(str
= get_x400_friendly (mn
->m_mbox
,
902 buffer
, sizeof(buffer
)))) {
904 switch (mn
->m_type
) {
909 snprintf (buffer
, sizeof(buffer
), "%s!%s",
910 mn
->m_host
, mn
->m_mbox
);
915 snprintf (buffer
, sizeof(buffer
), "%s@%s",
916 mn
->m_mbox
, mn
->m_host
);
928 /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
931 strncpy(buffer
, str
, sizeof(buffer
));
932 /* strncpy doesn't NUL-terminate if it fills the buffer */
933 buffer
[sizeof(buffer
)-1] = '\0';
934 unquote_string(buffer
, buffer2
);
941 if ((t
= comp
->c_tws
->tw_clock
) == 0)
942 t
= dmktime(comp
->c_tws
);
943 tws
= dlocaltime(&t
);
949 if ((t
= comp
->c_tws
->tw_clock
) == 0)
950 t
= dmktime(comp
->c_tws
);
957 if (comp
->c_flags
& CF_PARSED
)
959 if ((sp
= comp
->c_text
) && (tws
= dparsetime(sp
))) {
961 comp
->c_flags
&= ~CF_TRUE
;
962 } else if ((comp
->c_flags
& CF_DATEFAB
) == 0) {
963 memset ((char *) comp
->c_tws
, 0, sizeof *comp
->c_tws
);
964 comp
->c_flags
= CF_TRUE
;
966 comp
->c_flags
|= CF_PARSED
;
970 /* hook for custom address list formatting (see replsbr.c) */
971 if (callbacks
&& callbacks
->formataddr
)
972 str
= callbacks
->formataddr (savestr
, str
);
974 str
= formataddr (savestr
, str
);
978 /* The same as formataddr, but doesn't do duplicate suppression */
979 if (callbacks
&& callbacks
->concataddr
)
980 str
= callbacks
->concataddr (savestr
, str
);
982 str
= concataddr (savestr
, str
);
986 /* output the str register as an address component,
987 * splitting it into multiple lines if necessary. The
988 * value reg. contains the max line length. The lit.
989 * field may contain a string to prepend to the result
994 int indent
, wid
, len
;
1000 indent
= strlen (sp
);
1003 adios(NULL
, "putaddr -- num register (%d) must be greater "
1004 "than label width (%d)", value
, indent
);
1006 while( (c
= (unsigned char) *sp
++) && cp
< ep
)
1009 /* try to break at a comma; failing that, break at a
1012 lastb
= 0; sp
= lp
+ wid
;
1013 while (sp
> lp
&& (c
= (unsigned char) *--sp
) != ',') {
1014 if (! lastb
&& isspace(c
))
1018 if (! (sp
= lastb
)) {
1020 while (*sp
&& *sp
!= ',' &&
1021 !isspace((unsigned char) *sp
))
1028 while (cp
< ep
&& lp
<= sp
)
1030 while (isspace((unsigned char) *lp
))
1035 for (i
=indent
; cp
< ep
&& i
> 0; i
--)
1039 cpstripped (&cp
, &ep
, scanl
+ max
- 1, lp
);
1045 if (comp
->c_flags
& CF_PARSED
)
1047 if (comp
->c_mn
!= &fmt_mnull
)
1048 mnfree (comp
->c_mn
);
1049 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1050 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
1054 comp
->c_flags
|= CF_PARSED
;
1056 while (getname("")) /* XXX */
1058 comp
->c_mn
= &fmt_mnull
;
1064 * if there's no component, we say true. Otherwise we
1065 * say "true" only if we can parse the address and it
1066 * matches one of our addresses.
1069 if (comp
->c_mn
!= &fmt_mnull
)
1070 mnfree (comp
->c_mn
);
1071 if ((sp
= comp
->c_text
) && (sp
= getname(sp
)) &&
1072 (mn
= getm (sp
, NULL
, 0, NULL
, 0))) {
1075 comp
->c_flags
|= CF_TRUE
;
1077 comp
->c_flags
&= ~CF_TRUE
;
1078 while ((sp
= getname(sp
)))
1079 if ((comp
->c_flags
& CF_TRUE
) == 0 &&
1080 (mn
= getm (sp
, NULL
, 0, NULL
, 0)))
1082 comp
->c_flags
|= CF_TRUE
;
1084 while (getname("")) /* XXX */
1086 if (comp
->c_text
== 0)
1087 comp
->c_flags
|= CF_TRUE
;
1089 comp
->c_flags
&= ~CF_TRUE
;
1090 comp
->c_mn
= &fmt_mnull
;
1096 * Call our tracing callback function, if one was supplied
1099 if (callbacks
&& callbacks
->trace_func
)
1100 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1105 /* Emit any trailing sequences of zero display length. */
1106 while (fmt
->f_type
!= FT_DONE
) {
1107 if (fmt
->f_type
== FT_LS_LIT
) {
1109 if (callbacks
&& callbacks
->trace_func
)
1110 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1112 } else if (fmt
->f_type
== FT_STRLITZ
) {
1113 /* Don't want to emit part of an escape sequence. So if
1114 there isn't enough room in the buffer for the entire
1115 string, skip it completely. Need room for null
1116 terminator, and maybe trailing newline (added below). */
1117 if (str
&& (cp
- scanl
+ strlen (str
) + 1 < max
)) {
1118 for (sp
= str
; *sp
; *cp
++ = *sp
++) continue;
1120 if (callbacks
&& callbacks
->trace_func
)
1121 callbacks
->trace_func(callbacks
->trace_context
, fmt
, value
,
1128 if (cp
> scanl
&& cp
[-1] != '\n') {
1129 if (cp
- scanl
< (int) max
- 1) {
1136 return ((struct format
*)0);