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