2 * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * By using this file, you agree to the terms and conditions set
11 * forth in the LICENSE file which can be found at the top level of
12 * the sendmail distribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
18 SM_IDSTR(id
, "@(#)$Id: vfprintf.c,v 1.54 2005/05/16 03:52:00 ca Exp $")
22 ** Actual printing innards.
23 ** This code is large and complicated...
26 #include <sys/types.h>
30 #include <sm/config.h>
31 #include <sm/varargs.h>
38 static int sm_bprintf
__P((SM_FILE_T
*, const char *, va_list));
39 static void sm_find_arguments
__P((const char *, va_list , va_list **));
40 static void sm_grow_type_table_x
__P((unsigned char **, int *));
41 static int sm_print
__P((SM_FILE_T
*, int, struct sm_uio
*));
44 ** SM_PRINT -- print/flush to the file
46 ** Flush out all the vectors defined by the given uio,
47 ** then reset it so that it can be reused.
51 ** timeout -- time to complete operation (milliseconds)
52 ** uio -- vector list of memory locations of data for printing
60 sm_print(fp
, timeout
, uio
)
63 register struct sm_uio
*uio
;
67 if (uio
->uio_resid
== 0)
72 err
= sm_fvwrite(fp
, timeout
, uio
);
79 ** SM_BPRINTF -- allow formating to an unbuffered file.
81 ** Helper function for `fprintf to unbuffered unix file': creates a
82 ** temporary buffer (via a "fake" file pointer).
83 ** We only work on write-only files; this avoids
84 ** worries about ungetc buffers and so forth.
87 ** fp -- the file to send the o/p to
88 ** fmt -- format instructions for the o/p
89 ** ap -- vectors of data units used for formating
92 ** Failure: SM_IO_EOF and errno set
93 ** Success: number of data units used in the formating
96 ** formatted o/p can be SM_IO_BUFSIZ length maximum
100 sm_bprintf(fp
, fmt
, ap
)
107 unsigned char buf
[SM_IO_BUFSIZ
];
108 extern const char SmFileMagic
[];
110 /* copy the important variables */
111 fake
.sm_magic
= SmFileMagic
;
112 fake
.f_timeout
= SM_TIME_FOREVER
;
113 fake
.f_timeoutstate
= SM_TIME_BLOCK
;
114 fake
.f_flags
= fp
->f_flags
& ~SMNBF
;
115 fake
.f_file
= fp
->f_file
;
116 fake
.f_cookie
= fp
->f_cookie
;
117 fake
.f_write
= fp
->f_write
;
122 fake
.f_setinfo
= fake
.f_getinfo
= NULL
;
123 fake
.f_type
= "sm_bprintf:fake";
125 /* set up the buffer */
126 fake
.f_bf
.smb_base
= fake
.f_p
= buf
;
127 fake
.f_bf
.smb_size
= fake
.f_w
= sizeof(buf
);
128 fake
.f_lbfsize
= 0; /* not actually used, but Just In Case */
130 /* do the work, then copy any error status */
131 ret
= sm_io_vfprintf(&fake
, SM_TIME_FOREVER
, fmt
, ap
);
132 if (ret
>= 0 && sm_io_flush(&fake
, SM_TIME_FOREVER
))
133 ret
= SM_IO_EOF
; /* errno set by sm_io_flush */
134 if (fake
.f_flags
& SMERR
)
135 fp
->f_flags
|= SMERR
;
142 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
145 /* Macros for converting digits to letters and vice versa */
146 #define to_digit(c) ((c) - '0')
147 #define is_digit(c) ((unsigned) to_digit(c) <= 9)
148 #define to_char(n) ((char) (n) + '0')
150 /* Flags used during conversion. */
151 #define ALT 0x001 /* alternate form */
152 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
153 #define LADJUST 0x004 /* left adjustment */
154 #define LONGINT 0x010 /* long integer */
155 #define QUADINT 0x020 /* quad integer */
156 #define SHORTINT 0x040 /* short integer */
157 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
158 #define FPT 0x100 /* Floating point number */
161 ** SM_IO_VPRINTF -- performs actual formating for o/p
164 ** fp -- file pointer for o/p
165 ** timeout -- time to complete the print
166 ** fmt0 -- formating directives
167 ** ap -- vectors with data units for formating
170 ** Success: number of data units used for formatting
171 ** Failure: SM_IO_EOF and sets errno
175 sm_io_vfprintf(fp
, timeout
, fmt0
, ap
)
181 register char *fmt
; /* format string */
182 register int ch
; /* character from fmt */
183 register int n
, m
, n2
; /* handy integers (short term usage) */
184 register char *cp
; /* handy char pointer (short term usage) */
185 register struct sm_iov
*iovp
;/* for PRINT macro */
186 register int flags
; /* flags as above */
187 int ret
; /* return value accumulator */
188 int width
; /* width from format (%8d), or 0 */
189 int prec
; /* precision from format (%.3d), or -1 */
190 char sign
; /* sign prefix (' ', '+', '-', or \0) */
192 ULONGLONG_T _uquad
; /* integer arguments %[diouxX] */
193 enum { OCT
, DEC
, HEX
} base
;/* base for [diouxX] conversion */
194 int dprec
; /* a copy of prec if [diouxX], 0 otherwise */
195 int realsz
; /* field size expanded by dprec */
196 int size
; /* size of converted field or string */
197 char *xdigs
="0123456789abcdef"; /* digits for [xX] conversion */
199 struct sm_uio uio
; /* output information: summary */
200 struct sm_iov iov
[NIOV
];/* ... and individual io vectors */
201 char buf
[BUF
]; /* space for %c, %[diouxX], %[eEfgG] */
202 char ox
[2]; /* space for 0x hex-prefix */
203 va_list *argtable
; /* args, built due to positional arg */
204 va_list statargtable
[STATIC_ARG_TBL_SIZE
];
205 int nextarg
; /* 1-based argument index */
206 va_list orgap
; /* original argument pointer */
209 ** Choose PADSIZE to trade efficiency vs. size. If larger printf
210 ** fields occur frequently, increase PADSIZE and make the initialisers
213 #define PADSIZE 16 /* pad chunk size */
214 static char blanks
[PADSIZE
] =
215 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
216 static char zeroes
[PADSIZE
] =
217 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
220 ** BEWARE, these `goto error' on error, and PAD uses `n'.
222 #define PRINT(ptr, len) do { \
223 iovp->iov_base = (ptr); \
224 iovp->iov_len = (len); \
225 uio.uio_resid += (len); \
227 if (++uio.uio_iovcnt >= NIOV) \
229 if (sm_print(fp, timeout, &uio)) \
234 #define PAD(howmany, with) do \
236 if ((n = (howmany)) > 0) \
238 while (n > PADSIZE) { \
239 PRINT(with, PADSIZE); \
247 if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
249 uio.uio_iovcnt = 0; \
254 ** To extend shorts properly, we need both signed and unsigned
255 ** argument extraction methods.
258 (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
259 flags&LONGINT ? GETARG(long) : \
260 flags&SHORTINT ? (long) (short) GETARG(int) : \
263 (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
264 flags&LONGINT ? GETARG(unsigned long) : \
265 flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
266 (unsigned long) GETARG(unsigned int))
269 ** Get * arguments, including the form *nn$. Preserve the nextarg
270 ** that the argument can be gotten once the type is determined.
272 #define GETASTER(val) \
275 while (is_digit(*cp)) \
277 n2 = 10 * n2 + to_digit(*cp); \
282 int hold = nextarg; \
283 if (argtable == NULL) \
285 argtable = statargtable; \
286 sm_find_arguments(fmt0, orgap, &argtable); \
299 ** Get the argument indexed by nextarg. If the argument table is
300 ** built, use it to get the argument. If its not, get the next
301 ** argument (and arguments must be gotten sequentially).
305 # define GETARG(type) \
306 (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
307 nextarg++, SM_VA_ARG(ap, type))
308 #else /* SM_VA_STD */
309 # define GETARG(type) \
310 ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
311 (nextarg++, SM_VA_ARG(ap, type)))
312 #endif /* SM_VA_STD */
314 /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
321 /* optimise fprintf(stderr) (and other unbuffered Unix files) */
322 if ((fp
->f_flags
& (SMNBF
|SMWR
|SMRW
)) == (SMNBF
|SMWR
) &&
324 return sm_bprintf(fp
, fmt0
, ap
);
329 SM_VA_COPY(orgap
, ap
);
330 uio
.uio_iov
= iovp
= iov
;
335 /* Scan the format for conversions (`%' character). */
340 while ((wc
= *fmt
) != '\0')
349 if ((m
= fmt
- cp
) != 0)
356 fmt
++; /* skip over '%' */
365 reswitch
: switch (ch
)
370 ** ``If the space and + flags both appear, the space
371 ** flag will be ignored.''
384 ** ``A negative field width argument is taken as a
385 ** - flag followed by a positive field width.''
387 ** They don't exclude field widths read from args.
402 if ((ch
= *fmt
++) == '*')
405 prec
= n
< 0 ? -1 : n
;
411 n
= 10 * n
+ to_digit(ch
);
417 if (argtable
== NULL
)
419 argtable
= statargtable
;
420 sm_find_arguments(fmt0
, orgap
,
425 prec
= n
< 0 ? -1 : n
;
430 ** ``Note that 0 is taken as a flag, not as the
431 ** beginning of a field width.''
437 case '1': case '2': case '3': case '4':
438 case '5': case '6': case '7': case '8': case '9':
442 n
= 10 * n
+ to_digit(ch
);
444 } while (is_digit(ch
));
448 if (argtable
== NULL
)
450 argtable
= statargtable
;
451 sm_find_arguments(fmt0
, orgap
,
476 *(cp
= buf
) = GETARG(int);
486 if ((LONGLONG_T
) _uquad
< 0)
488 _uquad
= -(LONGLONG_T
) _uquad
;
506 ** This code implements floating point output
507 ** in the most portable manner possible,
508 ** relying only on 'sprintf' as defined by
509 ** the 1989 ANSI C standard.
510 ** We silently cap width and precision
511 ** at 120, to avoid buffer overflow.
514 val
= GETARG(double);
541 snprintf(out
, sizeof(out
), fmt
, width
,
543 #else /* HASSNPRINTF */
544 sprintf(out
, fmt
, width
, prec
, val
);
545 #endif /* HASSNPRINTF */
548 snprintf(out
, sizeof(out
), fmt
, width
,
550 #else /* HASSNPRINTF */
551 sprintf(out
, fmt
, width
, val
);
552 #endif /* HASSNPRINTF */
560 *GETARG(LONGLONG_T
*) = ret
;
561 else if (flags
& LONGINT
)
562 *GETARG(long *) = ret
;
563 else if (flags
& SHORTINT
)
564 *GETARG(short *) = ret
;
566 *GETARG(int *) = ret
;
567 continue; /* no output */
578 ** ``The argument shall be a pointer to void. The
579 ** value of the pointer is converted to a sequence
580 ** of printable characters, in an implementation-
594 u
.p
= GETARG(void *);
595 if (sizeof(void *) == sizeof(ULONGLONG_T
))
597 else if (sizeof(void *) == sizeof(long))
603 xdigs
= "0123456789abcdef";
608 if ((cp
= GETARG(char *)) == NULL
)
613 ** can't use strlen; can only look for the
614 ** NUL in the first `prec' characters, and
615 ** strlen() will go further.
618 char *p
= memchr(cp
, 0, prec
);
641 xdigs
= "0123456789ABCDEF";
644 xdigs
= "0123456789abcdef";
645 hex
: _uquad
= UARG();
647 /* leading 0x/X only if non-zero */
648 if (flags
& ALT
&& _uquad
!= 0)
651 /* unsigned conversions */
655 ** ``... diouXx conversions ... if a precision is
656 ** specified, the 0 flag will be ignored.''
660 number
: if ((dprec
= prec
) >= 0)
664 ** ``The result of converting a zero value with an
665 ** explicit precision of zero is no characters.''
670 if (_uquad
!= 0 || prec
!= 0)
673 ** Unsigned mod is hard, and unsigned mod
674 ** by a constant is easier than that by
675 ** a variable; hence this switch.
683 *--cp
= to_char(_uquad
& 7);
686 /* handle octal leading 0 */
687 if (flags
& ALT
&& *cp
!= '0')
692 /* many numbers are 1 digit */
695 *--cp
= to_char(_uquad
% 10);
698 *--cp
= to_char(_uquad
);
704 *--cp
= xdigs
[_uquad
& 15];
710 cp
= "bug in sm_io_vfprintf: bad base";
715 size
= buf
+ BUF
- cp
;
718 default: /* "%?" prints ?, unless ? is NUL */
721 /* pretend it was %c with argument ch */
730 ** All reasonable formats wind up here. At this point, `cp'
731 ** points to a string which (if not flags&LADJUST) should be
732 ** padded out to `width' places. If flags&ZEROPAD, it should
733 ** first be prefixed by any sign or other prefix; otherwise,
734 ** it should be blank padded before the prefix is emitted.
735 ** After any left-hand padding and prefixing, emit zeroes
736 ** required by a decimal [diouxX] precision, then print the
737 ** string proper, then emit zeroes required by any leftover
738 ** floating precision; finally, if LADJUST, pad with blanks.
740 ** Compute actual size, so we know how much to pad.
741 ** size excludes decimal prec; realsz includes it.
744 realsz
= dprec
> size
? dprec
: size
;
747 else if (flags
& HEXPREFIX
)
750 /* right-adjusting blank padding */
751 if ((flags
& (LADJUST
|ZEROPAD
)) == 0)
752 PAD(width
- realsz
, blanks
);
759 else if (flags
& HEXPREFIX
)
766 /* right-adjusting zero padding */
767 if ((flags
& (LADJUST
|ZEROPAD
)) == ZEROPAD
)
768 PAD(width
- realsz
, zeroes
);
770 /* leading zeroes from decimal precision */
771 PAD(dprec
- size
, zeroes
);
773 /* the string or number proper */
775 /* left-adjusting padding (always blank) */
777 PAD(width
- realsz
, blanks
);
779 /* finally, adjust ret */
780 ret
+= width
> realsz
? width
: realsz
;
782 FLUSH(); /* copy out the I/O vectors */
787 if ((argtable
!= NULL
) && (argtable
!= statargtable
))
789 return sm_error(fp
) ? SM_IO_EOF
: ret
;
793 /* Type ids for argument type table. */
812 ** SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
814 ** Find all arguments when a positional parameter is encountered. Returns a
815 ** table, indexed by argument number, of pointers to each arguments. The
816 ** initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
817 ** It will be replaced with a malloc-ed one if it overflows.
820 ** fmt0 -- formating directives
821 ** ap -- vector list of data unit for formating consumption
822 ** argtable -- an indexable table (returned) of 'ap'
829 sm_find_arguments(fmt0
, ap
, argtable
)
834 register char *fmt
; /* format string */
835 register int ch
; /* character from fmt */
836 register int n
, n2
; /* handy integer (short term usage) */
837 register char *cp
; /* handy char pointer (short term usage) */
838 register int flags
; /* flags as above */
839 unsigned char *typetable
; /* table of types */
840 unsigned char stattypetable
[STATIC_ARG_TBL_SIZE
];
841 int tablesize
; /* current size of type table */
842 int tablemax
; /* largest used index in table */
843 int nextarg
; /* 1-based argument index */
845 /* Add an argument type to the table, expanding if necessary. */
846 #define ADDTYPE(type) \
847 ((nextarg >= tablesize) ? \
848 (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
849 typetable[nextarg++] = type, \
850 (nextarg > tablemax) ? tablemax = nextarg : 0)
853 ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
854 ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
857 ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
858 ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
860 /* Add * arguments to the type array. */
864 while (is_digit(*cp)) \
866 n2 = 10 * n2 + to_digit(*cp); \
871 int hold = nextarg; \
882 typetable
= stattypetable
;
883 tablesize
= STATIC_ARG_TBL_SIZE
;
886 (void) memset(typetable
, T_UNUSED
, STATIC_ARG_TBL_SIZE
);
888 /* Scan the format for conversions (`%' character). */
891 for (cp
= fmt
; (ch
= *fmt
) != '\0' && ch
!= '%'; fmt
++)
895 fmt
++; /* skip over '%' */
900 reswitch
: switch (ch
)
912 if ((ch
= *fmt
++) == '*')
924 case '1': case '2': case '3': case '4':
925 case '5': case '6': case '7': case '8': case '9':
929 n
= 10 * n
+ to_digit(ch
);
931 } while (is_digit(ch
));
974 else if (flags
& LONGINT
)
976 else if (flags
& SHORTINT
)
980 continue; /* no output */
1000 if (flags
& QUADINT
)
1007 if (flags
& QUADINT
)
1012 default: /* "%?" prints ?, unless ? is NUL */
1019 /* Build the argument table. */
1020 if (tablemax
>= STATIC_ARG_TBL_SIZE
)
1022 *argtable
= (va_list *)
1023 sm_malloc(sizeof(va_list) * (tablemax
+ 1));
1026 for (n
= 1; n
<= tablemax
; n
++)
1028 SM_VA_COPY((*argtable
)[n
], ap
);
1029 switch (typetable
[n
])
1032 (void) SM_VA_ARG(ap
, int);
1035 (void) SM_VA_ARG(ap
, int);
1038 (void) SM_VA_ARG(ap
, int);
1041 (void) SM_VA_ARG(ap
, short *);
1044 (void) SM_VA_ARG(ap
, int);
1047 (void) SM_VA_ARG(ap
, unsigned int);
1050 (void) SM_VA_ARG(ap
, int *);
1053 (void) SM_VA_ARG(ap
, long);
1056 (void) SM_VA_ARG(ap
, unsigned long);
1059 (void) SM_VA_ARG(ap
, long *);
1062 (void) SM_VA_ARG(ap
, LONGLONG_T
);
1065 (void) SM_VA_ARG(ap
, ULONGLONG_T
);
1068 (void) SM_VA_ARG(ap
, LONGLONG_T
*);
1071 (void) SM_VA_ARG(ap
, double);
1074 (void) SM_VA_ARG(ap
, char *);
1077 (void) SM_VA_ARG(ap
, void *);
1082 if ((typetable
!= NULL
) && (typetable
!= stattypetable
))
1087 ** SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1090 ** tabletype -- type of table to grow
1091 ** tablesize -- requested new table size
1094 ** Raises an exception if can't allocate memory.
1098 sm_grow_type_table_x(typetable
, tablesize
)
1099 unsigned char **typetable
;
1102 unsigned char *oldtable
= *typetable
;
1103 int newsize
= *tablesize
* 2;
1105 if (*tablesize
== STATIC_ARG_TBL_SIZE
)
1107 *typetable
= (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1109 (void) memmove(*typetable
, oldtable
, *tablesize
);
1113 *typetable
= (unsigned char *) sm_realloc_x(typetable
,
1114 sizeof(unsigned char) * newsize
);
1116 (void) memset(&typetable
[*tablesize
], T_UNUSED
,
1117 (newsize
- *tablesize
));
1119 *tablesize
= newsize
;