2 * Copyright (c) 2000-2001 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.
16 SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.52 2001/09/11 04:04:49 gshapiro Exp $")
20 ** Actual printing innards.
21 ** This code is large and complicated...
24 #include <sys/types.h>
28 #include <sm/config.h>
29 #include <sm/varargs.h>
36 static void sm_find_arguments __P((const char *, va_list , va_list **));
37 static void sm_grow_type_table_x __P((unsigned char **, int *));
38 static int sm_print __P((SM_FILE_T *, int, struct sm_uio *));
41 ** SM_PRINT -- print/flush to the file
43 ** Flush out all the vectors defined by the given uio,
44 ** then reset it so that it can be reused.
48 ** timeout -- time to complete operation (milliseconds)
49 ** uio -- vector list of memory locations of data for printing
57 sm_print(fp, timeout, uio)
60 register struct sm_uio *uio;
64 if (uio->uio_resid == 0)
69 err = sm_fvwrite(fp, timeout, uio);
76 ** SM_BPRINTF -- allow formating to an unbuffered file.
78 ** Helper function for `fprintf to unbuffered unix file': creates a
79 ** temporary buffer (via a "fake" file pointer).
80 ** We only work on write-only files; this avoids
81 ** worries about ungetc buffers and so forth.
84 ** fp -- the file to send the o/p to
85 ** fmt -- format instructions for the o/p
86 ** ap -- vectors of data units used for formating
89 ** Failure: SM_IO_EOF and errno set
90 ** Success: number of data units used in the formating
93 ** formatted o/p can be SM_IO_BUFSIZ length maximum
97 sm_bprintf(fp, fmt, ap)
98 register SM_FILE_T *fp;
104 unsigned char buf[SM_IO_BUFSIZ];
105 extern const char SmFileMagic[];
107 /* copy the important variables */
108 fake.sm_magic = SmFileMagic;
109 fake.f_timeout = SM_TIME_FOREVER;
110 fake.f_timeoutstate = SM_TIME_BLOCK;
111 fake.f_flags = fp->f_flags & ~SMNBF;
112 fake.f_file = fp->f_file;
113 fake.f_cookie = fp->f_cookie;
114 fake.f_write = fp->f_write;
119 fake.f_setinfo = fake.f_getinfo = NULL;
120 fake.f_type = "sm_bprintf:fake";
122 /* set up the buffer */
123 fake.f_bf.smb_base = fake.f_p = buf;
124 fake.f_bf.smb_size = fake.f_w = sizeof(buf);
125 fake.f_lbfsize = 0; /* not actually used, but Just In Case */
127 /* do the work, then copy any error status */
128 ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
129 if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
130 ret = SM_IO_EOF; /* errno set by sm_io_flush */
131 if (fake.f_flags & SMERR)
132 fp->f_flags |= SMERR;
139 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
142 /* Macros for converting digits to letters and vice versa */
143 #define to_digit(c) ((c) - '0')
144 #define is_digit(c) ((unsigned) to_digit(c) <= 9)
145 #define to_char(n) ((char) (n) + '0')
147 /* Flags used during conversion. */
148 #define ALT 0x001 /* alternate form */
149 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
150 #define LADJUST 0x004 /* left adjustment */
151 #define LONGINT 0x010 /* long integer */
152 #define QUADINT 0x020 /* quad integer */
153 #define SHORTINT 0x040 /* short integer */
154 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
155 #define FPT 0x100 /* Floating point number */
158 ** SM_IO_VPRINTF -- performs actual formating for o/p
161 ** fp -- file pointer for o/p
162 ** timeout -- time to complete the print
163 ** fmt0 -- formating directives
164 ** ap -- vectors with data units for formating
167 ** Success: number of data units used for formatting
168 ** Failure: SM_IO_EOF and sets errno
172 sm_io_vfprintf(fp, timeout, fmt0, ap)
178 register char *fmt; /* format string */
179 register int ch; /* character from fmt */
180 register int n, m, n2; /* handy integers (short term usage) */
181 register char *cp; /* handy char pointer (short term usage) */
182 register struct sm_iov *iovp;/* for PRINT macro */
183 register int flags; /* flags as above */
184 int ret; /* return value accumulator */
185 int width; /* width from format (%8d), or 0 */
186 int prec; /* precision from format (%.3d), or -1 */
187 char sign; /* sign prefix (' ', '+', '-', or \0) */
189 ULONGLONG_T _uquad; /* integer arguments %[diouxX] */
190 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
191 int dprec; /* a copy of prec if [diouxX], 0 otherwise */
192 int realsz; /* field size expanded by dprec */
193 int size; /* size of converted field or string */
194 char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
196 struct sm_uio uio; /* output information: summary */
197 struct sm_iov iov[NIOV];/* ... and individual io vectors */
198 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
199 char ox[2]; /* space for 0x hex-prefix */
200 va_list *argtable; /* args, built due to positional arg */
201 va_list statargtable[STATIC_ARG_TBL_SIZE];
202 int nextarg; /* 1-based argument index */
203 va_list orgap; /* original argument pointer */
206 ** Choose PADSIZE to trade efficiency vs. size. If larger printf
207 ** fields occur frequently, increase PADSIZE and make the initialisers
210 #define PADSIZE 16 /* pad chunk size */
211 static char blanks[PADSIZE] =
212 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
213 static char zeroes[PADSIZE] =
214 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
217 ** BEWARE, these `goto error' on error, and PAD uses `n'.
219 #define PRINT(ptr, len) do { \
220 iovp->iov_base = (ptr); \
221 iovp->iov_len = (len); \
222 uio.uio_resid += (len); \
224 if (++uio.uio_iovcnt >= NIOV) \
226 if (sm_print(fp, timeout, &uio)) \
231 #define PAD(howmany, with) do \
233 if ((n = (howmany)) > 0) \
235 while (n > PADSIZE) { \
236 PRINT(with, PADSIZE); \
244 if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
246 uio.uio_iovcnt = 0; \
251 ** To extend shorts properly, we need both signed and unsigned
252 ** argument extraction methods.
255 (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
256 flags&LONGINT ? GETARG(long) : \
257 flags&SHORTINT ? (long) (short) GETARG(int) : \
260 (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
261 flags&LONGINT ? GETARG(unsigned long) : \
262 flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
263 (unsigned long) GETARG(unsigned int))
266 ** Get * arguments, including the form *nn$. Preserve the nextarg
267 ** that the argument can be gotten once the type is determined.
269 #define GETASTER(val) \
272 while (is_digit(*cp)) \
274 n2 = 10 * n2 + to_digit(*cp); \
279 int hold = nextarg; \
280 if (argtable == NULL) \
282 argtable = statargtable; \
283 sm_find_arguments(fmt0, orgap, &argtable); \
296 ** Get the argument indexed by nextarg. If the argument table is
297 ** built, use it to get the argument. If its not, get the next
298 ** argument (and arguments must be gotten sequentially).
302 # define GETARG(type) \
303 (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
304 nextarg++, SM_VA_ARG(ap, type))
305 #else /* SM_VA_STD */
306 # define GETARG(type) \
307 ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
308 (nextarg++, SM_VA_ARG(ap, type)))
309 #endif /* SM_VA_STD */
311 /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
318 /* optimise fprintf(stderr) (and other unbuffered Unix files) */
319 if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
321 return sm_bprintf(fp, fmt0, ap);
326 SM_VA_COPY(orgap, ap);
327 uio.uio_iov = iovp = iov;
332 /* Scan the format for conversions (`%' character). */
337 while ((wc = *fmt) != '\0')
346 if ((m = fmt - cp) != 0)
353 fmt++; /* skip over '%' */
362 reswitch: switch (ch)
367 ** ``If the space and + flags both appear, the space
368 ** flag will be ignored.''
381 ** ``A negative field width argument is taken as a
382 ** - flag followed by a positive field width.''
384 ** They don't exclude field widths read from args.
399 if ((ch = *fmt++) == '*')
402 prec = n < 0 ? -1 : n;
408 n = 10 * n + to_digit(ch);
414 if (argtable == NULL)
416 argtable = statargtable;
417 sm_find_arguments(fmt0, orgap,
422 prec = n < 0 ? -1 : n;
427 ** ``Note that 0 is taken as a flag, not as the
428 ** beginning of a field width.''
434 case '1': case '2': case '3': case '4':
435 case '5': case '6': case '7': case '8': case '9':
439 n = 10 * n + to_digit(ch);
441 } while (is_digit(ch));
445 if (argtable == NULL)
447 argtable = statargtable;
448 sm_find_arguments(fmt0, orgap,
473 *(cp = buf) = GETARG(int);
483 if ((LONGLONG_T) _uquad < 0)
485 _uquad = -(LONGLONG_T) _uquad;
503 ** This code implements floating point output
504 ** in the most portable manner possible,
505 ** relying only on 'sprintf' as defined by
506 ** the 1989 ANSI C standard.
507 ** We silently cap width and precision
508 ** at 120, to avoid buffer overflow.
511 val = GETARG(double);
537 sprintf(out, fmt, width, prec, val);
539 sprintf(out, fmt, width, val);
547 *GETARG(LONGLONG_T *) = ret;
548 else if (flags & LONGINT)
549 *GETARG(long *) = ret;
550 else if (flags & SHORTINT)
551 *GETARG(short *) = ret;
553 *GETARG(int *) = ret;
554 continue; /* no output */
565 ** ``The argument shall be a pointer to void. The
566 ** value of the pointer is converted to a sequence
567 ** of printable characters, in an implementation-
581 u.p = GETARG(void *);
582 if (sizeof(void *) == sizeof(ULONGLONG_T))
584 else if (sizeof(void *) == sizeof(long))
590 xdigs = "0123456789abcdef";
595 if ((cp = GETARG(char *)) == NULL)
600 ** can't use strlen; can only look for the
601 ** NUL in the first `prec' characters, and
602 ** strlen() will go further.
605 char *p = memchr(cp, 0, prec);
628 xdigs = "0123456789ABCDEF";
631 xdigs = "0123456789abcdef";
632 hex: _uquad = UARG();
634 /* leading 0x/X only if non-zero */
635 if (flags & ALT && _uquad != 0)
638 /* unsigned conversions */
642 ** ``... diouXx conversions ... if a precision is
643 ** specified, the 0 flag will be ignored.''
647 number: if ((dprec = prec) >= 0)
651 ** ``The result of converting a zero value with an
652 ** explicit precision of zero is no characters.''
657 if (_uquad != 0 || prec != 0)
660 ** Unsigned mod is hard, and unsigned mod
661 ** by a constant is easier than that by
662 ** a variable; hence this switch.
670 *--cp = to_char(_uquad & 7);
673 /* handle octal leading 0 */
674 if (flags & ALT && *cp != '0')
679 /* many numbers are 1 digit */
682 *--cp = to_char(_uquad % 10);
685 *--cp = to_char(_uquad);
691 *--cp = xdigs[_uquad & 15];
697 cp = "bug in sm_io_vfprintf: bad base";
702 size = buf + BUF - cp;
705 default: /* "%?" prints ?, unless ? is NUL */
708 /* pretend it was %c with argument ch */
717 ** All reasonable formats wind up here. At this point, `cp'
718 ** points to a string which (if not flags&LADJUST) should be
719 ** padded out to `width' places. If flags&ZEROPAD, it should
720 ** first be prefixed by any sign or other prefix; otherwise,
721 ** it should be blank padded before the prefix is emitted.
722 ** After any left-hand padding and prefixing, emit zeroes
723 ** required by a decimal [diouxX] precision, then print the
724 ** string proper, then emit zeroes required by any leftover
725 ** floating precision; finally, if LADJUST, pad with blanks.
727 ** Compute actual size, so we know how much to pad.
728 ** size excludes decimal prec; realsz includes it.
731 realsz = dprec > size ? dprec : size;
734 else if (flags & HEXPREFIX)
737 /* right-adjusting blank padding */
738 if ((flags & (LADJUST|ZEROPAD)) == 0)
739 PAD(width - realsz, blanks);
746 else if (flags & HEXPREFIX)
753 /* right-adjusting zero padding */
754 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
755 PAD(width - realsz, zeroes);
757 /* leading zeroes from decimal precision */
758 PAD(dprec - size, zeroes);
760 /* the string or number proper */
762 /* left-adjusting padding (always blank) */
764 PAD(width - realsz, blanks);
766 /* finally, adjust ret */
767 ret += width > realsz ? width : realsz;
769 FLUSH(); /* copy out the I/O vectors */
774 if ((argtable != NULL) && (argtable != statargtable))
776 return sm_error(fp) ? SM_IO_EOF : ret;
780 /* Type ids for argument type table. */
799 ** SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
801 ** Find all arguments when a positional parameter is encountered. Returns a
802 ** table, indexed by argument number, of pointers to each arguments. The
803 ** initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
804 ** It will be replaced with a malloc-ed one if it overflows.
807 ** fmt0 -- formating directives
808 ** ap -- vector list of data unit for formating consumption
809 ** argtable -- an indexable table (returned) of 'ap'
816 sm_find_arguments(fmt0, ap, argtable)
821 register char *fmt; /* format string */
822 register int ch; /* character from fmt */
823 register int n, n2; /* handy integer (short term usage) */
824 register char *cp; /* handy char pointer (short term usage) */
825 register int flags; /* flags as above */
826 unsigned char *typetable; /* table of types */
827 unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
828 int tablesize; /* current size of type table */
829 int tablemax; /* largest used index in table */
830 int nextarg; /* 1-based argument index */
832 /* Add an argument type to the table, expanding if necessary. */
833 #define ADDTYPE(type) \
834 ((nextarg >= tablesize) ? \
835 (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
836 typetable[nextarg++] = type, \
837 (nextarg > tablemax) ? tablemax = nextarg : 0)
840 ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
841 ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
844 ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
845 ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
847 /* Add * arguments to the type array. */
851 while (is_digit(*cp)) \
853 n2 = 10 * n2 + to_digit(*cp); \
858 int hold = nextarg; \
869 typetable = stattypetable;
870 tablesize = STATIC_ARG_TBL_SIZE;
873 (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
875 /* Scan the format for conversions (`%' character). */
878 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
882 fmt++; /* skip over '%' */
887 reswitch: switch (ch)
899 if ((ch = *fmt++) == '*')
911 case '1': case '2': case '3': case '4':
912 case '5': case '6': case '7': case '8': case '9':
916 n = 10 * n + to_digit(ch);
918 } while (is_digit(ch));
961 else if (flags & LONGINT)
963 else if (flags & SHORTINT)
967 continue; /* no output */
999 default: /* "%?" prints ?, unless ? is NUL */
1006 /* Build the argument table. */
1007 if (tablemax >= STATIC_ARG_TBL_SIZE)
1009 *argtable = (va_list *)
1010 sm_malloc(sizeof(va_list) * (tablemax + 1));
1013 for (n = 1; n <= tablemax; n++)
1015 SM_VA_COPY((*argtable)[n], ap);
1016 switch (typetable [n])
1019 (void) SM_VA_ARG(ap, int);
1022 (void) SM_VA_ARG(ap, int);
1025 (void) SM_VA_ARG(ap, int);
1028 (void) SM_VA_ARG(ap, short *);
1031 (void) SM_VA_ARG(ap, int);
1034 (void) SM_VA_ARG(ap, unsigned int);
1037 (void) SM_VA_ARG(ap, int *);
1040 (void) SM_VA_ARG(ap, long);
1043 (void) SM_VA_ARG(ap, unsigned long);
1046 (void) SM_VA_ARG(ap, long *);
1049 (void) SM_VA_ARG(ap, LONGLONG_T);
1052 (void) SM_VA_ARG(ap, ULONGLONG_T);
1055 (void) SM_VA_ARG(ap, LONGLONG_T *);
1058 (void) SM_VA_ARG(ap, double);
1061 (void) SM_VA_ARG(ap, char *);
1064 (void) SM_VA_ARG(ap, void *);
1069 if ((typetable != NULL) && (typetable != stattypetable))
1074 ** SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1077 ** tabletype -- type of table to grow
1078 ** tablesize -- requested new table size
1081 ** Raises an exception if can't allocate memory.
1085 sm_grow_type_table_x(typetable, tablesize)
1086 unsigned char **typetable;
1089 unsigned char *oldtable = *typetable;
1090 int newsize = *tablesize * 2;
1092 if (*tablesize == STATIC_ARG_TBL_SIZE)
1094 *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1096 (void) memmove(*typetable, oldtable, *tablesize);
1100 *typetable = (unsigned char *) sm_realloc_x(typetable,
1101 sizeof(unsigned char) * newsize);
1103 (void) memset(&typetable [*tablesize], T_UNUSED,
1104 (newsize - *tablesize));
1106 *tablesize = newsize;