1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek. 9 * 10 * Copyright (c) 2011 The FreeBSD Foundation 11 * 12 * Portions of this software were developed by David Chisnall 13 * under sponsorship from the FreeBSD Foundation. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * $FreeBSD$ 40 */ 41 42 /* 43 * This file defines common routines used by both printf and wprintf. 44 * You must define CHAR to either char or wchar_t prior to including this. 45 */ 46 47 48 #ifndef NO_FLOATING_POINT 49 50 #define dtoa __dtoa 51 #define freedtoa __freedtoa 52 53 #include <float.h> 54 #include <math.h> 55 #include "floatio.h" 56 #include "gdtoa.h" 57 58 #define DEFPREC 6 59 60 static int exponent(CHAR *, int, CHAR); 61 62 #endif /* !NO_FLOATING_POINT */ 63 64 static CHAR *__ujtoa(uintmax_t, CHAR *, int, int, const char *); 65 static CHAR *__ultoa(u_long, CHAR *, int, int, const char *); 66 67 #define NIOV 8 68 struct io_state { 69 FILE *fp; 70 struct __suio uio; /* output information: summary */ 71 struct __siov iov[NIOV];/* ... and individual io vectors */ 72 }; 73 74 static inline void 75 io_init(struct io_state *iop, FILE *fp) 76 { 77 78 iop->uio.uio_iov = iop->iov; 79 iop->uio.uio_resid = 0; 80 iop->uio.uio_iovcnt = 0; 81 iop->fp = fp; 82 } 83 84 /* 85 * WARNING: The buffer passed to io_print() is not copied immediately; it must 86 * remain valid until io_flush() is called. 87 */ 88 static inline int 89 io_print(struct io_state *iop, const CHAR * __restrict ptr, int len, locale_t locale) 90 { 91 92 iop->iov[iop->uio.uio_iovcnt].iov_base = (char *)ptr; 93 iop->iov[iop->uio.uio_iovcnt].iov_len = len; 94 iop->uio.uio_resid += len; 95 if (++iop->uio.uio_iovcnt >= NIOV) 96 return (__sprint(iop->fp, &iop->uio, locale)); 97 else 98 return (0); 99 } 100 101 /* 102 * Choose PADSIZE to trade efficiency vs. size. If larger printf 103 * fields occur frequently, increase PADSIZE and make the initialisers 104 * below longer. 105 */ 106 #define PADSIZE 16 /* pad chunk size */ 107 static const CHAR blanks[PADSIZE] = 108 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 109 static const CHAR zeroes[PADSIZE] = 110 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 111 112 /* 113 * Pad with blanks or zeroes. 'with' should point to either the blanks array 114 * or the zeroes array. 115 */ 116 static inline int 117 io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with, 118 locale_t locale) 119 { 120 int n; 121 122 while (howmany > 0) { 123 n = (howmany >= PADSIZE) ? PADSIZE : howmany; 124 if (io_print(iop, with, n, locale)) 125 return (-1); 126 howmany -= n; 127 } 128 return (0); 129 } 130 131 /* 132 * Print exactly len characters of the string spanning p to ep, truncating 133 * or padding with 'with' as necessary. 134 */ 135 static inline int 136 io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep, 137 int len, const CHAR * __restrict with, locale_t locale) 138 { 139 int p_len; 140 141 p_len = ep - p; 142 if (p_len > len) 143 p_len = len; 144 if (p_len > 0) { 145 if (io_print(iop, p, p_len, locale)) 146 return (-1); 147 } else { 148 p_len = 0; 149 } 150 return (io_pad(iop, len - p_len, with, locale)); 151 } 152 153 static inline int 154 io_flush(struct io_state *iop, locale_t locale) 155 { 156 157 return (__sprint(iop->fp, &iop->uio, locale)); 158 } 159 160 /* 161 * Convert an unsigned long to ASCII for printf purposes, returning 162 * a pointer to the first character of the string representation. 163 * Octal numbers can be forced to have a leading zero; hex numbers 164 * use the given digits. 165 */ 166 static CHAR * 167 __ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs) 168 { 169 CHAR *cp = endp; 170 long sval; 171 172 /* 173 * Handle the three cases separately, in the hope of getting 174 * better/faster code. 175 */ 176 switch (base) { 177 case 10: 178 if (val < 10) { /* many numbers are 1 digit */ 179 *--cp = to_char(val); 180 return (cp); 181 } 182 /* 183 * On many machines, unsigned arithmetic is harder than 184 * signed arithmetic, so we do at most one unsigned mod and 185 * divide; this is sufficient to reduce the range of 186 * the incoming value to where signed arithmetic works. 187 */ 188 if (val > LONG_MAX) { 189 *--cp = to_char(val % 10); 190 sval = val / 10; 191 } else 192 sval = val; 193 do { 194 *--cp = to_char(sval % 10); 195 sval /= 10; 196 } while (sval != 0); 197 break; 198 199 case 8: 200 do { 201 *--cp = to_char(val & 7); 202 val >>= 3; 203 } while (val); 204 if (octzero && *cp != '0') 205 *--cp = '0'; 206 break; 207 208 case 16: 209 do { 210 *--cp = xdigs[val & 15]; 211 val >>= 4; 212 } while (val); 213 break; 214 215 default: /* oops */ 216 abort(); 217 } 218 return (cp); 219 } 220 221 /* Identical to __ultoa, but for intmax_t. */ 222 static CHAR * 223 __ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs) 224 { 225 CHAR *cp = endp; 226 intmax_t sval; 227 228 /* quick test for small values; __ultoa is typically much faster */ 229 /* (perhaps instead we should run until small, then call __ultoa?) */ 230 if (val <= ULONG_MAX) 231 return (__ultoa((u_long)val, endp, base, octzero, xdigs)); 232 switch (base) { 233 case 10: 234 if (val < 10) { 235 *--cp = to_char(val % 10); 236 return (cp); 237 } 238 if (val > INTMAX_MAX) { 239 *--cp = to_char(val % 10); 240 sval = val / 10; 241 } else 242 sval = val; 243 do { 244 *--cp = to_char(sval % 10); 245 sval /= 10; 246 } while (sval != 0); 247 break; 248 249 case 8: 250 do { 251 *--cp = to_char(val & 7); 252 val >>= 3; 253 } while (val); 254 if (octzero && *cp != '0') 255 *--cp = '0'; 256 break; 257 258 case 16: 259 do { 260 *--cp = xdigs[val & 15]; 261 val >>= 4; 262 } while (val); 263 break; 264 265 default: 266 abort(); 267 } 268 return (cp); 269 } 270 271 #ifndef NO_FLOATING_POINT 272 273 static int 274 exponent(CHAR *p0, int exp, CHAR fmtch) 275 { 276 CHAR *p, *t; 277 CHAR expbuf[MAXEXPDIG]; 278 279 p = p0; 280 *p++ = fmtch; 281 if (exp < 0) { 282 exp = -exp; 283 *p++ = '-'; 284 } 285 else 286 *p++ = '+'; 287 t = expbuf + MAXEXPDIG; 288 if (exp > 9) { 289 do { 290 *--t = to_char(exp % 10); 291 } while ((exp /= 10) > 9); 292 *--t = to_char(exp); 293 for (; t < expbuf + MAXEXPDIG; *p++ = *t++); 294 } 295 else { 296 /* 297 * Exponents for decimal floating point conversions 298 * (%[eEgG]) must be at least two characters long, 299 * whereas exponents for hexadecimal conversions can 300 * be only one character long. 301 */ 302 if (fmtch == 'e' || fmtch == 'E') 303 *p++ = '0'; 304 *p++ = to_char(exp); 305 } 306 return (p - p0); 307 } 308 309 #endif /* !NO_FLOATING_POINT */ 310