Commit | Line | Data |
---|---|---|
ad0e16d0 | 1 | /* |
5f6de66c KB |
2 | * Copyright (c) 1988 Regents of the University of California. |
3 | * All rights reserved. | |
ad0e16d0 | 4 | * |
5f6de66c | 5 | * Redistribution and use in source and binary forms are permitted |
4afb9d15 KB |
6 | * provided that the above copyright notice and this paragraph are |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
ad0e16d0 KB |
16 | */ |
17 | ||
5f6de66c | 18 | #if defined(LIBC_SCCS) && !defined(lint) |
7d05582d | 19 | static char sccsid[] = "@(#)vfprintf.c 5.36 (Berkeley) %G%"; |
5f6de66c | 20 | #endif /* LIBC_SCCS and not lint */ |
ad0e16d0 | 21 | |
0d15d742 | 22 | #include <sys/types.h> |
5f6de66c KB |
23 | #include <varargs.h> |
24 | #include <stdio.h> | |
25 | #include <ctype.h> | |
ad0e16d0 | 26 | |
95a39ea1 KB |
27 | /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ |
28 | #define MAXEXP 308 | |
29 | /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ | |
30 | #define MAXFRACT 39 | |
31 | ||
2c2ee7b4 KB |
32 | #define DEFPREC 6 |
33 | ||
95a39ea1 | 34 | #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ |
6c1ffc8c | 35 | |
b042624f | 36 | #define PUTC(ch) (void) putc(ch, fp) |
66d7f790 | 37 | |
7d05582d KB |
38 | #define ARG(basetype) \ |
39 | _ulong = flags&LONGINT ? va_arg(argp, long basetype) : \ | |
40 | flags&SHORTINT ? (short basetype)va_arg(argp, int) : \ | |
41 | va_arg(argp, int) | |
410bd0d3 | 42 | |
5ae640f0 KB |
43 | #define todigit(c) ((c) - '0') |
44 | #define tochar(n) ((n) + '0') | |
45 | ||
410bd0d3 KB |
46 | /* have to deal with the negative buffer count kludge */ |
47 | #define NEGATIVE_COUNT_KLUDGE | |
48 | ||
49 | #define LONGINT 0x01 /* long integer */ | |
50 | #define LONGDBL 0x02 /* long double; unimplemented */ | |
51 | #define SHORTINT 0x04 /* short integer */ | |
52 | #define ALT 0x08 /* alternate form */ | |
53 | #define LADJUST 0x10 /* left adjustment */ | |
b042624f KB |
54 | #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ |
55 | #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ | |
ad0e16d0 | 56 | |
ef9c9d35 KB |
57 | _doprnt(fmt0, argp, fp) |
58 | u_char *fmt0; | |
5f6de66c | 59 | va_list argp; |
66d7f790 | 60 | register FILE *fp; |
ad0e16d0 | 61 | { |
b042624f KB |
62 | register u_char *fmt; /* format string */ |
63 | register int ch; /* character from fmt */ | |
64 | register int cnt; /* return value accumulator */ | |
65 | register int n; /* random handy integer */ | |
66 | register char *t; /* buffer pointer */ | |
67 | double _double; /* double precision arguments %[eEfgG] */ | |
68 | u_long _ulong; /* integer arguments %[diouxX] */ | |
2c2ee7b4 | 69 | int base; /* base for [diouxX] conversion */ |
b042624f | 70 | int dprec; /* decimal precision in [diouxX] */ |
2c2ee7b4 KB |
71 | int fieldsz; /* field size expanded by sign, etc */ |
72 | int flags; /* flags as above */ | |
b042624f | 73 | int fpprec; /* `extra' floating precision in [eEfgG] */ |
b042624f | 74 | int prec; /* precision from format (%.3d), or -1 */ |
b042624f | 75 | int realsz; /* field size expanded by decimal precision */ |
2c2ee7b4 KB |
76 | int size; /* size of converted field or string */ |
77 | int width; /* width from format (%8d), or 0 */ | |
90041bd4 KB |
78 | char sign; /* sign prefix (' ', '+', '-', or \0) */ |
79 | char softsign; /* temporary negative sign for floats */ | |
b042624f KB |
80 | char *digs; /* digits for [diouxX] conversion */ |
81 | char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ | |
5f6de66c | 82 | |
87d70ec1 KB |
83 | if (fp->_flag & _IORW) { |
84 | fp->_flag |= _IOWRT; | |
85 | fp->_flag &= ~(_IOEOF|_IOREAD); | |
86 | } | |
87 | if ((fp->_flag & _IOWRT) == 0) | |
88 | return (EOF); | |
89 | ||
ef9c9d35 | 90 | fmt = fmt0; |
5592524e | 91 | digs = "0123456789abcdef"; |
65942971 | 92 | for (cnt = 0;; ++fmt) { |
410bd0d3 | 93 | n = fp->_cnt; |
b042624f KB |
94 | for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; |
95 | ++cnt, ++fmt) | |
410bd0d3 KB |
96 | if (--n < 0 |
97 | #ifdef NEGATIVE_COUNT_KLUDGE | |
98 | && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) | |
99 | #endif | |
b042624f | 100 | || ch == '\n' && fp->_flag & _IOLBF) { |
410bd0d3 | 101 | fp->_cnt = n; |
051644d4 | 102 | fp->_ptr = t; |
b042624f | 103 | (void) _flsbuf((u_char)ch, fp); |
410bd0d3 | 104 | n = fp->_cnt; |
b042624f KB |
105 | t = (char *)fp->_ptr; |
106 | } else | |
410bd0d3 KB |
107 | *t++ = ch; |
108 | fp->_cnt = n; | |
051644d4 | 109 | fp->_ptr = t; |
410bd0d3 | 110 | if (!ch) |
b042624f | 111 | return (cnt); |
ad0e16d0 | 112 | |
5c1be79b | 113 | flags = 0; dprec = 0; fpprec = 0; width = 0; |
5f6de66c | 114 | prec = -1; |
410bd0d3 | 115 | sign = '\0'; |
5f6de66c | 116 | |
410bd0d3 KB |
117 | rflag: switch (*++fmt) { |
118 | case ' ': | |
90041bd4 KB |
119 | /* |
120 | * ``If the space and + flags both appear, the space | |
121 | * flag will be ignored.'' | |
122 | * -- ANSI X3J11 | |
123 | */ | |
124 | if (!sign) | |
125 | sign = ' '; | |
410bd0d3 | 126 | goto rflag; |
5f6de66c | 127 | case '#': |
410bd0d3 KB |
128 | flags |= ALT; |
129 | goto rflag; | |
5f6de66c | 130 | case '*': |
66d7f790 KB |
131 | /* |
132 | * ``A negative field width argument is taken as a | |
133 | * - flag followed by a positive field width.'' | |
134 | * -- ANSI X3J11 | |
135 | * They don't exclude field widths read from args. | |
136 | */ | |
137 | if ((width = va_arg(argp, int)) >= 0) | |
410bd0d3 | 138 | goto rflag; |
66d7f790 | 139 | width = -width; |
b042624f | 140 | /* FALLTHROUGH */ |
66d7f790 | 141 | case '-': |
410bd0d3 KB |
142 | flags |= LADJUST; |
143 | goto rflag; | |
5f6de66c | 144 | case '+': |
40233183 | 145 | sign = '+'; |
410bd0d3 | 146 | goto rflag; |
5f6de66c | 147 | case '.': |
66d7f790 | 148 | if (*++fmt == '*') |
410bd0d3 | 149 | n = va_arg(argp, int); |
66d7f790 | 150 | else { |
b042624f KB |
151 | n = 0; |
152 | while (isascii(*fmt) && isdigit(*fmt)) | |
153 | n = 10 * n + todigit(*fmt++); | |
66d7f790 | 154 | --fmt; |
66d7f790 | 155 | } |
410bd0d3 KB |
156 | prec = n < 0 ? -1 : n; |
157 | goto rflag; | |
5f6de66c | 158 | case '0': |
b042624f KB |
159 | /* |
160 | * ``Note that 0 is taken as a flag, not as the | |
161 | * beginning of a field width.'' | |
162 | * -- ANSI X3J11 | |
163 | */ | |
164 | flags |= ZEROPAD; | |
165 | goto rflag; | |
5f6de66c KB |
166 | case '1': case '2': case '3': case '4': |
167 | case '5': case '6': case '7': case '8': case '9': | |
410bd0d3 | 168 | n = 0; |
5f6de66c | 169 | do { |
5ae640f0 | 170 | n = 10 * n + todigit(*fmt); |
410bd0d3 KB |
171 | } while (isascii(*++fmt) && isdigit(*fmt)); |
172 | width = n; | |
5f6de66c | 173 | --fmt; |
aa876f9d | 174 | goto rflag; |
66d7f790 | 175 | case 'L': |
ad5b98a5 | 176 | flags |= LONGDBL; |
410bd0d3 | 177 | goto rflag; |
66d7f790 | 178 | case 'h': |
410bd0d3 KB |
179 | flags |= SHORTINT; |
180 | goto rflag; | |
5f6de66c | 181 | case 'l': |
410bd0d3 KB |
182 | flags |= LONGINT; |
183 | goto rflag; | |
40233183 | 184 | case 'c': |
b042624f | 185 | *(t = buf) = va_arg(argp, int); |
40233183 | 186 | size = 1; |
b042624f | 187 | sign = '\0'; |
40233183 | 188 | goto pforw; |
2c2ee7b4 KB |
189 | case 'D': |
190 | flags |= LONGINT; | |
191 | /*FALLTHROUGH*/ | |
a5676d90 | 192 | case 'd': |
410bd0d3 | 193 | case 'i': |
7d05582d | 194 | ARG(int); |
410bd0d3 KB |
195 | if ((long)_ulong < 0) { |
196 | _ulong = -_ulong; | |
40233183 | 197 | sign = '-'; |
a5676d90 | 198 | } |
a5676d90 | 199 | base = 10; |
bb5070be | 200 | goto number; |
6c1ffc8c | 201 | case 'e': |
0d15d742 | 202 | case 'E': |
66d7f790 | 203 | case 'f': |
5592524e | 204 | case 'g': |
0d15d742 | 205 | case 'G': |
5592524e | 206 | _double = va_arg(argp, double); |
95a39ea1 | 207 | /* |
90041bd4 KB |
208 | * don't do unrealistic precision; just pad it with |
209 | * zeroes later, so buffer size stays rational. | |
95a39ea1 KB |
210 | */ |
211 | if (prec > MAXFRACT) { | |
051644d4 KB |
212 | if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) |
213 | fpprec = prec - MAXFRACT; | |
95a39ea1 KB |
214 | prec = MAXFRACT; |
215 | } | |
2c2ee7b4 KB |
216 | else if (prec == -1) |
217 | prec = DEFPREC; | |
90041bd4 KB |
218 | /* |
219 | * softsign avoids negative 0 if _double is < 0 and | |
220 | * no significant digits will be shown | |
221 | */ | |
2c2ee7b4 | 222 | if (_double < 0) { |
90041bd4 | 223 | softsign = '-'; |
2c2ee7b4 KB |
224 | _double = -_double; |
225 | } | |
90041bd4 KB |
226 | else |
227 | softsign = 0; | |
2c2ee7b4 | 228 | /* |
90041bd4 | 229 | * cvt may have to round up past the "start" of the |
2c2ee7b4 KB |
230 | * buffer, i.e. ``intf("%.2f", (double)9.999);''; |
231 | * if the first char isn't NULL, it did. | |
232 | */ | |
233 | *buf = NULL; | |
90041bd4 | 234 | size = cvt(_double, prec, flags, &softsign, *fmt, buf, |
2c2ee7b4 | 235 | buf + sizeof(buf)); |
90041bd4 KB |
236 | if (softsign) |
237 | sign = '-'; | |
2c2ee7b4 | 238 | t = *buf ? buf : buf + 1; |
40233183 | 239 | goto pforw; |
66d7f790 | 240 | case 'n': |
b042624f | 241 | if (flags & LONGINT) |
410bd0d3 | 242 | *va_arg(argp, long *) = cnt; |
b042624f | 243 | else if (flags & SHORTINT) |
410bd0d3 KB |
244 | *va_arg(argp, short *) = cnt; |
245 | else | |
246 | *va_arg(argp, int *) = cnt; | |
66d7f790 | 247 | break; |
2c2ee7b4 KB |
248 | case 'O': |
249 | flags |= LONGINT; | |
250 | /*FALLTHROUGH*/ | |
ad0e16d0 | 251 | case 'o': |
7d05582d | 252 | ARG(unsigned); |
ad0e16d0 | 253 | base = 8; |
bb5070be | 254 | goto nosign; |
66d7f790 | 255 | case 'p': |
2c71023f | 256 | /* |
c748d653 KB |
257 | * ``The argument shall be a pointer to void. The |
258 | * value of the pointer is converted to a sequence | |
259 | * of printable characters, in an implementation- | |
260 | * defined manner.'' | |
261 | * -- ANSI X3J11 | |
2c71023f | 262 | */ |
b042624f | 263 | /* NOSTRICT */ |
2c71023f KB |
264 | _ulong = (u_long)va_arg(argp, void *); |
265 | base = 16; | |
bb5070be | 266 | goto nosign; |
ad0e16d0 | 267 | case 's': |
40233183 KB |
268 | if (!(t = va_arg(argp, char *))) |
269 | t = "(null)"; | |
c748d653 KB |
270 | if (prec >= 0) { |
271 | /* | |
272 | * can't use strlen; can only look for the | |
273 | * NUL in the first `prec' characters, and | |
274 | * strlen() will go further. | |
275 | */ | |
276 | char *p, *memchr(); | |
277 | ||
278 | if (p = memchr(t, 0, prec)) { | |
279 | size = p - t; | |
280 | if (size > prec) | |
281 | size = prec; | |
b042624f | 282 | } else |
c748d653 | 283 | size = prec; |
b042624f | 284 | } else |
c748d653 | 285 | size = strlen(t); |
b042624f | 286 | sign = '\0'; |
95a39ea1 | 287 | goto pforw; |
2c2ee7b4 KB |
288 | case 'U': |
289 | flags |= LONGINT; | |
290 | /*FALLTHROUGH*/ | |
ad0e16d0 | 291 | case 'u': |
7d05582d | 292 | ARG(unsigned); |
ad0e16d0 | 293 | base = 10; |
bb5070be | 294 | goto nosign; |
ad0e16d0 KB |
295 | case 'X': |
296 | digs = "0123456789ABCDEF"; | |
b042624f | 297 | /* FALLTHROUGH */ |
ad0e16d0 | 298 | case 'x': |
7d05582d | 299 | ARG(unsigned); |
40233183 | 300 | base = 16; |
8a91f8d6 | 301 | /* leading 0x/X only if non-zero */ |
b042624f KB |
302 | if (flags & ALT && _ulong != 0) |
303 | flags |= HEXPREFIX; | |
bb5070be KB |
304 | |
305 | /* unsigned conversions */ | |
b042624f | 306 | nosign: sign = '\0'; |
89e72465 KB |
307 | /* |
308 | * ``... diouXx conversions ... if a precision is | |
309 | * specified, the 0 flag will be ignored.'' | |
310 | * -- ANSI X3J11 | |
311 | */ | |
b042624f KB |
312 | number: if ((dprec = prec) >= 0) |
313 | flags &= ~ZEROPAD; | |
314 | ||
8a91f8d6 KB |
315 | /* |
316 | * ``The result of converting a zero value with an | |
317 | * explicit precision of zero is no characters.'' | |
318 | * -- ANSI X3J11 | |
319 | */ | |
b042624f KB |
320 | t = buf + BUF; |
321 | if (_ulong != 0 || prec != 0) { | |
322 | do { | |
323 | *--t = digs[_ulong % base]; | |
324 | _ulong /= base; | |
325 | } while (_ulong); | |
326 | digs = "0123456789abcdef"; | |
327 | if (flags & ALT && base == 8 && *t != '0') | |
328 | *--t = '0'; /* octal leading 0 */ | |
89e72465 | 329 | } |
b042624f | 330 | size = buf + BUF - t; |
bb5070be | 331 | |
b042624f KB |
332 | pforw: |
333 | /* | |
2c2ee7b4 KB |
334 | * All reasonable formats wind up here. At this point, |
335 | * `t' points to a string which (if not flags&LADJUST) | |
336 | * should be padded out to `width' places. If | |
337 | * flags&ZEROPAD, it should first be prefixed by any | |
338 | * sign or other prefix; otherwise, it should be blank | |
339 | * padded before the prefix is emitted. After any | |
340 | * left-hand padding and prefixing, emit zeroes | |
341 | * required by a decimal [diouxX] precision, then print | |
342 | * the string proper, then emit zeroes required by any | |
343 | * leftover floating precision; finally, if LADJUST, | |
344 | * pad with blanks. | |
b042624f | 345 | */ |
bb5070be | 346 | |
2c2ee7b4 KB |
347 | /* |
348 | * compute actual size, so we know how much to pad | |
349 | * fieldsz excludes decimal prec; realsz includes it | |
350 | */ | |
b042624f KB |
351 | fieldsz = size + fpprec; |
352 | if (sign) | |
353 | fieldsz++; | |
354 | if (flags & HEXPREFIX) | |
355 | fieldsz += 2; | |
356 | realsz = dprec > fieldsz ? dprec : fieldsz; | |
bb5070be | 357 | |
b042624f KB |
358 | /* right-adjusting blank padding */ |
359 | if ((flags & (LADJUST|ZEROPAD)) == 0 && width) | |
360 | for (n = realsz; n < width; n++) | |
361 | PUTC(' '); | |
362 | /* prefix */ | |
363 | if (sign) | |
364 | PUTC(sign); | |
365 | if (flags & HEXPREFIX) { | |
366 | PUTC('0'); | |
367 | PUTC((char)*fmt); | |
bb5070be | 368 | } |
b042624f KB |
369 | /* right-adjusting zero padding */ |
370 | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) | |
371 | for (n = realsz; n < width; n++) | |
372 | PUTC('0'); | |
373 | /* leading zeroes from decimal precision */ | |
374 | for (n = fieldsz; n < dprec; n++) | |
375 | PUTC('0'); | |
bb5070be | 376 | |
b042624f KB |
377 | /* the string or number proper */ |
378 | if (fp->_cnt - (n = size) >= 0 && | |
379 | (fp->_flag & _IOLBF) == 0) { | |
95a39ea1 | 380 | fp->_cnt -= n; |
b042624f | 381 | bcopy(t, (char *)fp->_ptr, n); |
95a39ea1 | 382 | fp->_ptr += n; |
b042624f KB |
383 | } else |
384 | while (--n >= 0) | |
385 | PUTC(*t++); | |
386 | /* trailing f.p. zeroes */ | |
387 | while (--fpprec >= 0) | |
95a39ea1 | 388 | PUTC('0'); |
b042624f KB |
389 | /* left-adjusting padding (always blank) */ |
390 | if (flags & LADJUST) | |
391 | for (n = realsz; n < width; n++) | |
95a39ea1 | 392 | PUTC(' '); |
b042624f KB |
393 | /* finally, adjust cnt */ |
394 | cnt += width > realsz ? width : realsz; | |
ad0e16d0 | 395 | break; |
b042624f KB |
396 | case '\0': /* "%?" prints ?, unless ? is NULL */ |
397 | return (cnt); | |
ad0e16d0 | 398 | default: |
b042624f KB |
399 | PUTC((char)*fmt); |
400 | cnt++; | |
ad0e16d0 | 401 | } |
ad0e16d0 | 402 | } |
b042624f | 403 | /* NOTREACHED */ |
ad0e16d0 | 404 | } |
cd0a25f4 | 405 | |
2c2ee7b4 | 406 | static |
90041bd4 | 407 | cvt(number, prec, flags, signp, fmtch, startp, endp) |
cd0a25f4 | 408 | double number; |
0d15d742 | 409 | register int prec; |
ef9c9d35 KB |
410 | int flags; |
411 | u_char fmtch; | |
90041bd4 | 412 | char *signp, *startp, *endp; |
cd0a25f4 | 413 | { |
5ae640f0 | 414 | register char *p, *t; |
5c1be79b | 415 | register double fract; |
2c2ee7b4 | 416 | int dotrim, expcnt, gformat; |
5c1be79b | 417 | double integer, tmp, modf(); |
90041bd4 | 418 | char *exponent(), *round(); |
cd0a25f4 | 419 | |
2c2ee7b4 KB |
420 | dotrim = expcnt = gformat = 0; |
421 | fract = modf(number, &integer); | |
bb5070be | 422 | |
2c2ee7b4 KB |
423 | /* get an extra slot for rounding. */ |
424 | t = ++startp; | |
0d15d742 | 425 | |
862bbfbe | 426 | /* |
2c2ee7b4 KB |
427 | * get integer portion of number; put into the end of the buffer; the |
428 | * .01 is added for modf(356.0 / 10, &integer) returning .59999999... | |
862bbfbe | 429 | */ |
2c2ee7b4 KB |
430 | for (p = endp - 1; integer; ++expcnt) { |
431 | tmp = modf(integer / 10, &integer); | |
432 | *p-- = tochar((int)((tmp + .01) * 10)); | |
433 | } | |
434 | switch(fmtch) { | |
435 | case 'f': | |
436 | /* reverse integer into beginning of buffer */ | |
437 | if (expcnt) | |
438 | for (; ++p < endp; *t++ = *p); | |
439 | else | |
440 | *t++ = '0'; | |
862bbfbe | 441 | /* |
2c2ee7b4 KB |
442 | * if precision required or alternate flag set, add in a |
443 | * decimal point. | |
862bbfbe | 444 | */ |
2c2ee7b4 KB |
445 | if (prec || flags&ALT) |
446 | *t++ = '.'; | |
447 | /* if requires more precision and some fraction left */ | |
448 | if (fract) { | |
449 | if (prec) | |
450 | do { | |
451 | fract = modf(fract * 10, &tmp); | |
452 | *t++ = tochar((int)tmp); | |
453 | } while (--prec && fract); | |
454 | if (fract) | |
90041bd4 KB |
455 | startp = round(fract, (int *)NULL, startp, |
456 | t - 1, (char)0, signp); | |
2c2ee7b4 KB |
457 | } |
458 | for (; prec--; *t++ = '0'); | |
459 | break; | |
460 | case 'e': | |
461 | case 'E': | |
462 | eformat: if (expcnt) { | |
463 | *t++ = *++p; | |
464 | if (prec || flags&ALT) | |
5ae640f0 | 465 | *t++ = '.'; |
2c2ee7b4 KB |
466 | /* if requires more precision and some integer left */ |
467 | for (; prec && ++p < endp; --prec) | |
468 | *t++ = *p; | |
469 | /* | |
470 | * if done precision and more of the integer component, | |
471 | * round using it; adjust fract so we don't re-round | |
472 | * later. | |
473 | */ | |
474 | if (!prec && ++p < endp) { | |
862bbfbe | 475 | fract = 0; |
90041bd4 KB |
476 | startp = round((double)0, &expcnt, startp, |
477 | t - 1, *p, signp); | |
862bbfbe | 478 | } |
2c2ee7b4 KB |
479 | /* adjust expcnt for digit in front of decimal */ |
480 | --expcnt; | |
862bbfbe | 481 | } |
2c2ee7b4 KB |
482 | /* until first fractional digit, decrement exponent */ |
483 | else if (fract) { | |
484 | /* adjust expcnt for digit in front of decimal */ | |
485 | for (expcnt = -1;; --expcnt) { | |
486 | fract = modf(fract * 10, &tmp); | |
487 | if (tmp) | |
488 | break; | |
862bbfbe | 489 | } |
2c2ee7b4 KB |
490 | *t++ = tochar((int)tmp); |
491 | if (prec || flags&ALT) | |
5ae640f0 | 492 | *t++ = '.'; |
cd0a25f4 | 493 | } |
862bbfbe | 494 | else { |
2c2ee7b4 KB |
495 | *t++ = '0'; |
496 | if (prec || flags&ALT) | |
5ae640f0 | 497 | *t++ = '.'; |
cd0a25f4 | 498 | } |
2c2ee7b4 KB |
499 | /* if requires more precision and some fraction left */ |
500 | if (fract) { | |
501 | if (prec) | |
502 | do { | |
503 | fract = modf(fract * 10, &tmp); | |
504 | *t++ = tochar((int)tmp); | |
505 | } while (--prec && fract); | |
506 | if (fract) | |
90041bd4 KB |
507 | startp = round(fract, &expcnt, startp, |
508 | t - 1, (char)0, signp); | |
ad98686b | 509 | } |
2c2ee7b4 KB |
510 | /* if requires more precision */ |
511 | for (; prec--; *t++ = '0'); | |
512 | ||
513 | /* unless alternate flag, trim any g/G format trailing 0's */ | |
514 | if (gformat && !(flags&ALT)) { | |
515 | while (t > startp && *--t == '0'); | |
516 | if (*t == '.') | |
517 | --t; | |
518 | ++t; | |
519 | } | |
520 | t = exponent(t, expcnt, fmtch); | |
521 | break; | |
522 | case 'g': | |
523 | case 'G': | |
524 | /* a precision of 0 is treated as a precision of 1. */ | |
525 | if (!prec) | |
526 | ++prec; | |
527 | /* | |
528 | * ``The style used depends on the value converted; style e | |
529 | * will be used only if the exponent resulting from the | |
530 | * conversion is less than -4 or greater than the precision.'' | |
531 | * -- ANSI X3J11 | |
532 | */ | |
533 | if (expcnt > prec || !expcnt && fract && fract < .0001) { | |
534 | /* | |
535 | * g/G format counts "significant digits, not digits of | |
536 | * precision; for the e/E format, this just causes an | |
537 | * off-by-one problem, i.e. g/G considers the digit | |
538 | * before the decimal point significant and e/E doesn't | |
539 | * count it as precision. | |
540 | */ | |
541 | --prec; | |
542 | fmtch -= 2; /* G->E, g->e */ | |
543 | gformat = 1; | |
544 | goto eformat; | |
545 | } | |
546 | /* | |
547 | * reverse integer into beginning of buffer, | |
548 | * note, decrement precision | |
549 | */ | |
550 | if (expcnt) | |
551 | for (; ++p < endp; *t++ = *p, --prec); | |
552 | else | |
553 | *t++ = '0'; | |
554 | /* | |
555 | * if precision required or alternate flag set, add in a | |
556 | * decimal point. If no digits yet, add in leading 0. | |
557 | */ | |
558 | if (prec || flags&ALT) { | |
559 | dotrim = 1; | |
560 | *t++ = '.'; | |
561 | } | |
562 | else | |
563 | dotrim = 0; | |
564 | /* if requires more precision and some fraction left */ | |
565 | if (fract) { | |
566 | if (prec) { | |
567 | do { | |
568 | fract = modf(fract * 10, &tmp); | |
569 | *t++ = tochar((int)tmp); | |
570 | } while(!tmp); | |
571 | while (--prec && fract) { | |
572 | fract = modf(fract * 10, &tmp); | |
573 | *t++ = tochar((int)tmp); | |
862bbfbe | 574 | } |
862bbfbe | 575 | } |
2c2ee7b4 | 576 | if (fract) |
90041bd4 KB |
577 | startp = round(fract, (int *)NULL, startp, |
578 | t - 1, (char)0, signp); | |
2c2ee7b4 KB |
579 | } |
580 | /* alternate format, adds 0's for precision, else trim 0's */ | |
581 | if (flags&ALT) | |
582 | for (; prec--; *t++ = '0'); | |
583 | else if (dotrim) { | |
584 | while (t > startp && *--t == '0'); | |
585 | if (*t != '.') | |
586 | ++t; | |
587 | } | |
862bbfbe | 588 | } |
2c2ee7b4 KB |
589 | return(t - startp); |
590 | } | |
862bbfbe | 591 | |
2c2ee7b4 | 592 | static char * |
90041bd4 | 593 | round(fract, exp, start, end, ch, signp) |
2c2ee7b4 | 594 | double fract; |
2c2ee7b4 | 595 | int *exp; |
90041bd4 KB |
596 | register char *start, *end; |
597 | char ch, *signp; | |
2c2ee7b4 KB |
598 | { |
599 | double tmp; | |
600 | ||
601 | if (fract) | |
862bbfbe | 602 | (void)modf(fract * 10, &tmp); |
2c2ee7b4 KB |
603 | else |
604 | tmp = todigit(ch); | |
605 | if (tmp > 4) | |
606 | for (;; --end) { | |
607 | if (*end == '.') | |
608 | --end; | |
609 | if (++*end <= '9') | |
610 | break; | |
611 | *end = '0'; | |
2c2ee7b4 | 612 | if (end == start) { |
90041bd4 KB |
613 | if (exp) { /* e/E; increment exponent */ |
614 | *end = '1'; | |
615 | ++*exp; | |
616 | } | |
617 | else { /* f; add extra digit */ | |
618 | *--end = '1'; | |
619 | --start; | |
620 | } | |
2c2ee7b4 | 621 | break; |
862bbfbe | 622 | } |
cd0a25f4 | 623 | } |
90041bd4 KB |
624 | /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ |
625 | else if (*signp == '-') | |
626 | for (;; --end) { | |
627 | if (*end == '.') | |
628 | --end; | |
629 | if (*end != '0') | |
630 | break; | |
631 | if (end == start) | |
632 | *signp = 0; | |
633 | } | |
2c2ee7b4 KB |
634 | return(start); |
635 | } | |
862bbfbe | 636 | |
2c2ee7b4 KB |
637 | static char * |
638 | exponent(p, exp, fmtch) | |
639 | register char *p; | |
640 | register int exp; | |
641 | u_char fmtch; | |
642 | { | |
643 | register char *t; | |
644 | char expbuf[MAXEXP]; | |
862bbfbe | 645 | |
2c2ee7b4 KB |
646 | *p++ = fmtch; |
647 | if (exp < 0) { | |
648 | exp = -exp; | |
649 | *p++ = '-'; | |
650 | } | |
651 | else | |
652 | *p++ = '+'; | |
653 | t = expbuf + MAXEXP; | |
654 | if (exp > 9) { | |
655 | do { | |
656 | *--t = tochar(exp % 10); | |
657 | } while ((exp /= 10) > 9); | |
658 | *--t = tochar(exp); | |
659 | for (; t < expbuf + MAXEXP; *p++ = *t++); | |
660 | } | |
661 | else { | |
662 | *p++ = '0'; | |
663 | *p++ = tochar(exp); | |
cd0a25f4 | 664 | } |
ef9c9d35 | 665 | return(p); |
cd0a25f4 | 666 | } |