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