| /* |
| Copyright (C) 1993 Free Software Foundation |
| |
| This file is part of the GNU IO Library. This library is free |
| software; you can redistribute it and/or modify it under the |
| terms of the GNU General Public License as published by the |
| Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this library; see the file COPYING. If not, write to the Free |
| Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| |
| As a special exception, if you link this library with files |
| compiled with a GNU compiler to produce an executable, this does not cause |
| the resulting executable to be covered by the GNU General Public License. |
| This exception does not however invalidate any other reasons why |
| the executable file might be covered by the GNU General Public License. */ |
| |
| /* |
| * Copyright (c) 1990 Regents of the University of California. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms are permitted |
| * provided that the above copyright notice and this paragraph are |
| * duplicated in all such forms and that any documentation, |
| * advertising materials, and other materials related to such |
| * distribution and use acknowledge that the software was developed |
| * by the University of California, Berkeley. The name of the |
| * University may not be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| |
| #if defined(LIBC_SCCS) && !defined(lint) |
| static char sccsid[] = "%W% (Berkeley) %G%"; |
| #endif /* LIBC_SCCS and not lint */ |
| |
| /* |
| * Actual printf innards. |
| * |
| * This code is large and complicated... |
| */ |
| |
| #include <sys/types.h> |
| #include "libioP.h" |
| #include <string.h> |
| #ifdef __STDC__ |
| #include <stdarg.h> |
| #else |
| #include <varargs.h> |
| #endif |
| |
| #ifndef _IO_USE_DTOA |
| int __cvt_double __P((double number, register int prec, int flags, int *signp, int fmtch, char *startp, char *endp)); |
| #endif |
| |
| /* |
| * Define FLOATING_POINT to get floating point. |
| */ |
| #ifndef NO_FLOATING_POINT |
| #define FLOATING_POINT |
| #endif |
| |
| /* end of configuration stuff */ |
| |
| |
| /* |
| * Helper "class" for `fprintf to unbuffered': creates a |
| * temporary buffer. */ |
| |
| struct helper_file |
| { |
| struct _IO_FILE_plus _f; |
| _IO_FILE *_put_stream; |
| }; |
| |
| static int |
| _IO_helper_overflow (fp, c) |
| _IO_FILE *fp; |
| int c; |
| { |
| _IO_FILE *target = ((struct helper_file*)fp)->_put_stream; |
| int used = fp->_IO_write_ptr - fp->_IO_write_base; |
| if (used) |
| { |
| _IO_sputn(target, fp->_IO_write_base, used); |
| fp->_IO_write_ptr -= used; |
| } |
| return _IO_putc (c, fp); |
| } |
| |
| static struct _IO_jump_t _IO_helper_jumps = { |
| JUMP_INIT_DUMMY, |
| JUMP_INIT(finish, _IO_default_finish), |
| JUMP_INIT(overflow, _IO_helper_overflow), |
| JUMP_INIT(underflow, _IO_default_underflow), |
| JUMP_INIT(uflow, _IO_default_uflow), |
| JUMP_INIT(pbackfail, _IO_default_pbackfail), |
| JUMP_INIT(xsputn, _IO_default_xsputn), |
| JUMP_INIT(xsgetn, _IO_default_xsgetn), |
| JUMP_INIT(seekoff, _IO_default_seekoff), |
| JUMP_INIT(seekpos, _IO_default_seekpos), |
| JUMP_INIT(setbuf, _IO_default_setbuf), |
| JUMP_INIT(sync, _IO_default_sync), |
| JUMP_INIT(doallocate, _IO_default_doallocate), |
| JUMP_INIT(read, _IO_default_read), |
| JUMP_INIT(write, _IO_default_write), |
| JUMP_INIT(seek, _IO_default_seek), |
| JUMP_INIT(close, _IO_default_close), |
| JUMP_INIT(stat, _IO_default_stat) |
| }; |
| |
| static int |
| helper_vfprintf (fp, fmt0, ap) |
| _IO_FILE *fp; |
| char const *fmt0; |
| _IO_va_list ap; |
| { |
| char buf[_IO_BUFSIZ]; |
| struct helper_file helper; |
| register _IO_FILE *hp = (_IO_FILE*)&helper; |
| int result, to_flush; |
| |
| /* initialize helper */ |
| helper._put_stream = fp; |
| hp->_IO_write_base = buf; |
| hp->_IO_write_ptr = buf; |
| hp->_IO_write_end = buf+_IO_BUFSIZ; |
| hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS; |
| _IO_JUMPS(hp) = &_IO_helper_jumps; |
| |
| /* Now print to helper instead. */ |
| result = _IO_vfprintf(hp, fmt0, ap); |
| |
| /* Now flush anything from the helper to the fp. */ |
| if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0) |
| { |
| if (_IO_sputn(fp, hp->_IO_write_base, to_flush) != to_flush) |
| return EOF; |
| } |
| return result; |
| } |
| |
| #ifdef FLOATING_POINT |
| |
| #include "floatio.h" |
| #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ |
| #define DEFPREC 6 |
| extern double modf __P((double, double*)); |
| |
| #else /* no FLOATING_POINT */ |
| |
| #define BUF 40 |
| |
| #endif /* FLOATING_POINT */ |
| |
| |
| /* |
| * Macros for converting digits to letters and vice versa |
| */ |
| #define to_digit(c) ((c) - '0') |
| #define is_digit(c) ((unsigned)to_digit(c) <= 9) |
| #define to_char(n) ((n) + '0') |
| |
| /* |
| * Flags used during conversion. |
| */ |
| #define LONGINT 0x01 /* long integer */ |
| #define LONGDBL 0x02 /* long double; unimplemented */ |
| #define SHORTINT 0x04 /* short integer */ |
| #define ALT 0x08 /* alternate form */ |
| #define LADJUST 0x10 /* left adjustment */ |
| #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ |
| #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ |
| |
| int |
| _IO_vfprintf (fp, fmt0, ap) |
| _IO_FILE *fp; |
| char const *fmt0; |
| _IO_va_list ap; |
| { |
| register const char *fmt; /* format string */ |
| register int ch; /* character from fmt */ |
| register int n; /* handy integer (short term usage) */ |
| register char *cp; /* handy char pointer (short term usage) */ |
| const char *fmark; /* for remembering a place in fmt */ |
| register int flags; /* flags as above */ |
| int ret; /* return value accumulator */ |
| int width; /* width from format (%8d), or 0 */ |
| int prec; /* precision from format (%.3d), or -1 */ |
| char sign; /* sign prefix (' ', '+', '-', or \0) */ |
| #ifdef FLOATING_POINT |
| int softsign; /* temporary negative sign for floats */ |
| double _double; /* double precision arguments %[eEfgG] */ |
| #ifndef _IO_USE_DTOA |
| int fpprec; /* `extra' floating precision in [eEfgG] */ |
| #endif |
| #endif |
| unsigned long _ulong; /* integer arguments %[diouxX] */ |
| enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ |
| int dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
| int dpad; /* extra 0 padding needed for integers */ |
| int fieldsz; /* field size expanded by sign, dpad etc */ |
| /* The initialization of 'size' is to suppress a warning that |
| 'size' might be used unitialized. It seems gcc can't |
| quite grok this spaghetti code ... */ |
| int size = 0; /* size of converted field or string */ |
| char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
| char ox[2]; /* space for 0x hex-prefix */ |
| |
| /* |
| * BEWARE, these `goto error' on error, and PAD uses `n'. |
| */ |
| #define PRINT(ptr, len) \ |
| do { if (_IO_sputn(fp,ptr, len) != len) goto error; } while (0) |
| #define PAD_SP(howmany) if (_IO_padn(fp, ' ', howmany) < (howmany)) goto error; |
| #define PAD_0(howmany) if (_IO_padn(fp, '0', howmany) < (howmany)) goto error; |
| |
| /* |
| * To extend shorts properly, we need both signed and unsigned |
| * argument extraction methods. |
| */ |
| #define SARG() \ |
| (flags&LONGINT ? va_arg(ap, long) : \ |
| flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ |
| (long)va_arg(ap, int)) |
| #define UARG() \ |
| (flags&LONGINT ? va_arg(ap, unsigned long) : \ |
| flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \ |
| (unsigned long)va_arg(ap, unsigned int)) |
| |
| /* optimise stderr (and other unbuffered Unix files) */ |
| if (fp->_IO_file_flags & _IO_UNBUFFERED) |
| return helper_vfprintf(fp, fmt0, ap); |
| |
| fmt = fmt0; |
| ret = 0; |
| |
| /* |
| * Scan the format for conversions (`%' character). |
| */ |
| for (;;) { |
| for (fmark = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) |
| /* void */; |
| if ((n = fmt - fmark) != 0) { |
| PRINT(fmark, n); |
| ret += n; |
| } |
| if (ch == '\0') |
| goto done; |
| fmt++; /* skip over '%' */ |
| |
| flags = 0; |
| dprec = 0; |
| #if defined(FLOATING_POINT) && !defined (_IO_USE_DTOA) |
| fpprec = 0; |
| #endif |
| width = 0; |
| prec = -1; |
| sign = '\0'; |
| |
| rflag: ch = *fmt++; |
| reswitch: switch (ch) { |
| case ' ': |
| /* |
| * ``If the space and + flags both appear, the space |
| * flag will be ignored.'' |
| * -- ANSI X3J11 |
| */ |
| if (!sign) |
| sign = ' '; |
| goto rflag; |
| case '#': |
| flags |= ALT; |
| goto rflag; |
| case '*': |
| /* |
| * ``A negative field width argument is taken as a |
| * - flag followed by a positive field width.'' |
| * -- ANSI X3J11 |
| * They don't exclude field widths read from args. |
| */ |
| if ((width = va_arg(ap, int)) >= 0) |
| goto rflag; |
| width = -width; |
| /* FALLTHROUGH */ |
| case '-': |
| flags |= LADJUST; |
| flags &= ~ZEROPAD; /* '-' disables '0' */ |
| goto rflag; |
| case '+': |
| sign = '+'; |
| goto rflag; |
| case '.': |
| if ((ch = *fmt++) == '*') { |
| n = va_arg(ap, int); |
| prec = n < 0 ? -1 : n; |
| goto rflag; |
| } |
| n = 0; |
| while (is_digit(ch)) { |
| n = 10 * n + to_digit(ch); |
| ch = *fmt++; |
| } |
| prec = n < 0 ? -1 : n; |
| goto reswitch; |
| case '0': |
| /* |
| * ``Note that 0 is taken as a flag, not as the |
| * beginning of a field width.'' |
| * -- ANSI X3J11 |
| */ |
| if (!(flags & LADJUST)) |
| flags |= ZEROPAD; /* '-' disables '0' */ |
| goto rflag; |
| case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| n = 0; |
| do { |
| n = 10 * n + to_digit(ch); |
| ch = *fmt++; |
| } while (is_digit(ch)); |
| width = n; |
| goto reswitch; |
| #ifdef FLOATING_POINT |
| case 'L': |
| flags |= LONGDBL; |
| goto rflag; |
| #endif |
| case 'h': |
| flags |= SHORTINT; |
| goto rflag; |
| case 'l': |
| flags |= LONGINT; |
| goto rflag; |
| case 'c': |
| *(cp = buf) = va_arg(ap, int); |
| size = 1; |
| sign = '\0'; |
| break; |
| case 'D': |
| flags |= LONGINT; |
| /*FALLTHROUGH*/ |
| case 'd': |
| case 'i': |
| _ulong = SARG(); |
| if ((long)_ulong < 0) { |
| _ulong = -_ulong; |
| sign = '-'; |
| } |
| base = DEC; |
| goto number; |
| #ifdef FLOATING_POINT |
| case 'e': |
| case 'E': |
| case 'f': |
| case 'F': |
| case 'g': |
| case 'G': |
| _double = va_arg(ap, double); |
| #ifdef _IO_USE_DTOA |
| { |
| int fmt_flags = 0; |
| int fill = ' '; |
| if (flags & ALT) |
| fmt_flags |= _IO_SHOWPOINT; |
| if (flags & LADJUST) |
| fmt_flags |= _IO_LEFT; |
| else if (flags & ZEROPAD) |
| fmt_flags |= _IO_INTERNAL, fill = '0'; |
| n = _IO_outfloat(_double, fp, ch, width, |
| prec < 0 ? DEFPREC : prec, |
| fmt_flags, sign, fill); |
| if (n < 0) |
| goto error; |
| ret += n; |
| } |
| /* CHECK ERROR! */ |
| continue; |
| #else |
| /* |
| * don't do unrealistic precision; just pad it with |
| * zeroes later, so buffer size stays rational. |
| */ |
| if (prec > MAXFRACT) { |
| if ((ch != 'g' && ch != 'G') || (flags&ALT)) |
| fpprec = prec - MAXFRACT; |
| prec = MAXFRACT; |
| } else if (prec == -1) |
| prec = DEFPREC; |
| /* __cvt_double may have to round up before the |
| "start" of its buffer, i.e. |
| ``intf("%.2f", (double)9.999);''; |
| if the first character is still NUL, it did. |
| softsign avoids negative 0 if _double < 0 but |
| no significant digits will be shown. */ |
| cp = buf; |
| *cp = '\0'; |
| size = __cvt_double(_double, prec, flags, &softsign, |
| ch, cp, buf + sizeof(buf)); |
| if (softsign) |
| sign = '-'; |
| if (*cp == '\0') |
| cp++; |
| break; |
| #endif |
| #endif /* FLOATING_POINT */ |
| case 'n': |
| if (flags & LONGINT) |
| *va_arg(ap, long *) = ret; |
| else if (flags & SHORTINT) |
| *va_arg(ap, short *) = ret; |
| else |
| *va_arg(ap, int *) = ret; |
| continue; /* no output */ |
| case 'O': |
| flags |= LONGINT; |
| /*FALLTHROUGH*/ |
| case 'o': |
| _ulong = UARG(); |
| base = OCT; |
| goto nosign; |
| case 'p': |
| /* |
| * ``The argument shall be a pointer to void. The |
| * value of the pointer is converted to a sequence |
| * of printable characters, in an implementation- |
| * defined manner.'' |
| * -- ANSI X3J11 |
| */ |
| /* NOSTRICT */ |
| _ulong = (unsigned long)va_arg(ap, void *); |
| base = HEX; |
| flags |= HEXPREFIX; |
| ch = 'x'; |
| goto nosign; |
| case 's': |
| if ((cp = va_arg(ap, char *)) == NULL) |
| cp = "(null)"; |
| if (prec >= 0) { |
| /* |
| * can't use strlen; can only look for the |
| * NUL in the first `prec' characters, and |
| * strlen() will go further. |
| */ |
| char *p = (char*)memchr(cp, 0, prec); |
| |
| if (p != NULL) { |
| size = p - cp; |
| if (size > prec) |
| size = prec; |
| } else |
| size = prec; |
| } else |
| size = strlen(cp); |
| sign = '\0'; |
| break; |
| case 'U': |
| flags |= LONGINT; |
| /*FALLTHROUGH*/ |
| case 'u': |
| _ulong = UARG(); |
| base = DEC; |
| goto nosign; |
| case 'X': |
| case 'x': |
| _ulong = UARG(); |
| base = HEX; |
| /* leading 0x/X only if non-zero */ |
| if (flags & ALT && _ulong != 0) |
| flags |= HEXPREFIX; |
| |
| /* unsigned conversions */ |
| nosign: sign = '\0'; |
| /* |
| * ``... diouXx conversions ... if a precision is |
| * specified, the 0 flag will be ignored.'' |
| * -- ANSI X3J11 |
| */ |
| number: if ((dprec = prec) >= 0) |
| flags &= ~ZEROPAD; |
| |
| /* |
| * ``The result of converting a zero value with an |
| * explicit precision of zero is no characters.'' |
| * -- ANSI X3J11 |
| */ |
| cp = buf + BUF; |
| if (_ulong != 0 || prec != 0) { |
| char *xdigs; /* digits for [xX] conversion */ |
| /* |
| * unsigned mod is hard, and unsigned mod |
| * by a constant is easier than that by |
| * a variable; hence this switch. |
| */ |
| switch (base) { |
| case OCT: |
| do { |
| *--cp = to_char(_ulong & 7); |
| _ulong >>= 3; |
| } while (_ulong); |
| /* handle octal leading 0 */ |
| if (flags & ALT && *cp != '0') |
| *--cp = '0'; |
| break; |
| |
| case DEC: |
| /* many numbers are 1 digit */ |
| while (_ulong >= 10) { |
| *--cp = to_char(_ulong % 10); |
| _ulong /= 10; |
| } |
| *--cp = to_char(_ulong); |
| break; |
| |
| case HEX: |
| if (ch == 'X') |
| xdigs = "0123456789ABCDEF"; |
| else /* ch == 'x' || ch == 'p' */ |
| xdigs = "0123456789abcdef"; |
| do { |
| *--cp = xdigs[_ulong & 15]; |
| _ulong >>= 4; |
| } while (_ulong); |
| break; |
| |
| default: |
| cp = "bug in vform: bad base"; |
| goto skipsize; |
| } |
| } |
| size = buf + BUF - cp; |
| skipsize: |
| break; |
| default: /* "%?" prints ?, unless ? is NUL */ |
| if (ch == '\0') |
| goto done; |
| /* pretend it was %c with argument ch */ |
| cp = buf; |
| *cp = ch; |
| size = 1; |
| sign = '\0'; |
| break; |
| } |
| |
| /* |
| * All reasonable formats wind up here. At this point, |
| * `cp' points to a string which (if not flags&LADJUST) |
| * should be padded out to `width' places. If |
| * flags&ZEROPAD, it should first be prefixed by any |
| * sign or other prefix; otherwise, it should be blank |
| * padded before the prefix is emitted. After any |
| * left-hand padding and prefixing, emit zeroes |
| * required by a decimal [diouxX] precision, then print |
| * the string proper, then emit zeroes required by any |
| * leftover floating precision; finally, if LADJUST, |
| * pad with blanks. |
| */ |
| |
| /* |
| * compute actual size, so we know how much to pad. |
| */ |
| #if defined(FLOATING_POINT) && !defined (_IO_USE_DTOA) |
| fieldsz = size + fpprec; |
| #else |
| fieldsz = size; |
| #endif |
| dpad = dprec - size; |
| if (dpad < 0) |
| dpad = 0; |
| |
| if (sign) |
| fieldsz++; |
| else if (flags & HEXPREFIX) |
| fieldsz += 2; |
| fieldsz += dpad; |
| |
| /* right-adjusting blank padding */ |
| if ((flags & (LADJUST|ZEROPAD)) == 0) |
| PAD_SP(width - fieldsz); |
| |
| /* prefix */ |
| if (sign) { |
| PRINT(&sign, 1); |
| } else if (flags & HEXPREFIX) { |
| ox[0] = '0'; |
| ox[1] = ch; |
| PRINT(ox, 2); |
| } |
| |
| /* right-adjusting zero padding */ |
| if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) |
| PAD_0(width - fieldsz); |
| |
| /* leading zeroes from decimal precision */ |
| PAD_0(dpad); |
| |
| /* the string or number proper */ |
| PRINT(cp, size); |
| |
| #if defined(FLOATING_POINT) && !defined (_IO_USE_DTOA) |
| /* trailing f.p. zeroes */ |
| PAD_0(fpprec); |
| #endif |
| |
| /* left-adjusting padding (always blank) */ |
| if (flags & LADJUST) |
| PAD_SP(width - fieldsz); |
| |
| /* finally, adjust ret */ |
| ret += width > fieldsz ? width : fieldsz; |
| |
| } |
| done: |
| return ret; |
| error: |
| return EOF; |
| /* NOTREACHED */ |
| } |
| |
| #if defined(FLOATING_POINT) && !defined(_IO_USE_DTOA) |
| |
| static char *exponent(register char *p, register int exp, int fmtch) |
| { |
| register char *t; |
| char expbuf[MAXEXP]; |
| |
| *p++ = fmtch; |
| if (exp < 0) { |
| exp = -exp; |
| *p++ = '-'; |
| } |
| else |
| *p++ = '+'; |
| t = expbuf + MAXEXP; |
| if (exp > 9) { |
| do { |
| *--t = to_char(exp % 10); |
| } while ((exp /= 10) > 9); |
| *--t = to_char(exp); |
| for (; t < expbuf + MAXEXP; *p++ = *t++); |
| } |
| else { |
| *p++ = '0'; |
| *p++ = to_char(exp); |
| } |
| return (p); |
| } |
| |
| static char * round(double fract, int *exp, |
| register char *start, register char *end, |
| char ch, int *signp) |
| { |
| double tmp; |
| |
| if (fract) |
| (void)modf(fract * 10, &tmp); |
| else |
| tmp = to_digit(ch); |
| if (tmp > 4) |
| for (;; --end) { |
| if (*end == '.') |
| --end; |
| if (++*end <= '9') |
| break; |
| *end = '0'; |
| if (end == start) { |
| if (exp) { /* e/E; increment exponent */ |
| *end = '1'; |
| ++*exp; |
| } |
| else { /* f; add extra digit */ |
| *--end = '1'; |
| --start; |
| } |
| break; |
| } |
| } |
| /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ |
| else if (*signp == '-') |
| for (;; --end) { |
| if (*end == '.') |
| --end; |
| if (*end != '0') |
| break; |
| if (end == start) |
| *signp = 0; |
| } |
| return (start); |
| } |
| |
| int __cvt_double(double number, register int prec, int flags, int *signp, |
| int fmtch, char *startp, char *endp) |
| { |
| register char *p, *t; |
| register double fract; |
| int dotrim = 0, expcnt, gformat = 0; |
| double integer, tmp; |
| |
| expcnt = 0; |
| if (number < 0) { |
| number = -number; |
| *signp = '-'; |
| } else |
| *signp = 0; |
| |
| fract = modf(number, &integer); |
| |
| /* get an extra slot for rounding. */ |
| t = ++startp; |
| |
| /* |
| * get integer portion of number; put into the end of the buffer; the |
| * .01 is added for modf(356.0 / 10, &integer) returning .59999999... |
| */ |
| for (p = endp - 1; p >= startp && integer; ++expcnt) { |
| tmp = modf(integer / 10, &integer); |
| *p-- = to_char((int)((tmp + .01) * 10)); |
| } |
| switch (fmtch) { |
| case 'f': |
| case 'F': |
| /* reverse integer into beginning of buffer */ |
| if (expcnt) |
| for (; ++p < endp; *t++ = *p); |
| else |
| *t++ = '0'; |
| /* |
| * if precision required or alternate flag set, add in a |
| * decimal point. |
| */ |
| if (prec || flags&ALT) |
| *t++ = '.'; |
| /* if requires more precision and some fraction left */ |
| if (fract) { |
| if (prec) |
| do { |
| fract = modf(fract * 10, &tmp); |
| *t++ = to_char((int)tmp); |
| } while (--prec && fract); |
| if (fract) |
| startp = round(fract, (int *)NULL, startp, |
| t - 1, (char)0, signp); |
| } |
| for (; prec--; *t++ = '0'); |
| break; |
| case 'e': |
| case 'E': |
| eformat: if (expcnt) { |
| *t++ = *++p; |
| if (prec || flags&ALT) |
| *t++ = '.'; |
| /* if requires more precision and some integer left */ |
| for (; prec && ++p < endp; --prec) |
| *t++ = *p; |
| /* |
| * if done precision and more of the integer component, |
| * round using it; adjust fract so we don't re-round |
| * later. |
| */ |
| if (!prec && ++p < endp) { |
| fract = 0; |
| startp = round((double)0, &expcnt, startp, |
| t - 1, *p, signp); |
| } |
| /* adjust expcnt for digit in front of decimal */ |
| --expcnt; |
| } |
| /* until first fractional digit, decrement exponent */ |
| else if (fract) { |
| /* adjust expcnt for digit in front of decimal */ |
| for (expcnt = -1;; --expcnt) { |
| fract = modf(fract * 10, &tmp); |
| if (tmp) |
| break; |
| } |
| *t++ = to_char((int)tmp); |
| if (prec || flags&ALT) |
| *t++ = '.'; |
| } |
| else { |
| *t++ = '0'; |
| if (prec || flags&ALT) |
| *t++ = '.'; |
| } |
| /* if requires more precision and some fraction left */ |
| if (fract) { |
| if (prec) |
| do { |
| fract = modf(fract * 10, &tmp); |
| *t++ = to_char((int)tmp); |
| } while (--prec && fract); |
| if (fract) |
| startp = round(fract, &expcnt, startp, |
| t - 1, (char)0, signp); |
| } |
| /* if requires more precision */ |
| for (; prec--; *t++ = '0'); |
| |
| /* unless alternate flag, trim any g/G format trailing 0's */ |
| if (gformat && !(flags&ALT)) { |
| while (t > startp && *--t == '0'); |
| if (*t == '.') |
| --t; |
| ++t; |
| } |
| t = exponent(t, expcnt, fmtch); |
| break; |
| case 'g': |
| case 'G': |
| /* a precision of 0 is treated as a precision of 1. */ |
| if (!prec) |
| ++prec; |
| /* |
| * ``The style used depends on the value converted; style e |
| * will be used only if the exponent resulting from the |
| * conversion is less than -4 or greater than the precision.'' |
| * -- ANSI X3J11 |
| */ |
| if (expcnt > prec || (!expcnt && fract && fract < .0001)) { |
| /* |
| * g/G format counts "significant digits, not digits of |
| * precision; for the e/E format, this just causes an |
| * off-by-one problem, i.e. g/G considers the digit |
| * before the decimal point significant and e/E doesn't |
| * count it as precision. |
| */ |
| --prec; |
| fmtch -= 2; /* G->E, g->e */ |
| gformat = 1; |
| goto eformat; |
| } |
| /* |
| * reverse integer into beginning of buffer, |
| * note, decrement precision |
| */ |
| if (expcnt) |
| for (; ++p < endp; *t++ = *p, --prec); |
| else |
| *t++ = '0'; |
| /* |
| * if precision required or alternate flag set, add in a |
| * decimal point. If no digits yet, add in leading 0. |
| */ |
| if (prec || flags&ALT) { |
| dotrim = 1; |
| *t++ = '.'; |
| } |
| else |
| dotrim = 0; |
| /* if requires more precision and some fraction left */ |
| if (fract) { |
| if (prec) { |
| /* If no integer part, don't count initial |
| * zeros as significant digits. */ |
| do { |
| fract = modf(fract * 10, &tmp); |
| *t++ = to_char((int)tmp); |
| } while(!tmp && !expcnt); |
| while (--prec && fract) { |
| fract = modf(fract * 10, &tmp); |
| *t++ = to_char((int)tmp); |
| } |
| } |
| if (fract) |
| startp = round(fract, (int *)NULL, startp, |
| t - 1, (char)0, signp); |
| } |
| /* alternate format, adds 0's for precision, else trim 0's */ |
| if (flags&ALT) |
| for (; prec--; *t++ = '0'); |
| else if (dotrim) { |
| while (t > startp && *--t == '0'); |
| if (*t != '.') |
| ++t; |
| } |
| } |
| return (t - startp); |
| } |
| |
| #endif /* defined(FLOATING_POINT) && !defined(_IO_USE_DTOA) */ |