new stdio; renamed from doprnt.c. There was a trivial vfprintf.c
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
CommitLineData
1e949ff4
KB
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
5f6de66c 3 * All rights reserved.
ad0e16d0 4 *
1e949ff4
KB
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
019bea33 8 * %sccs.include.redist.c%
ad0e16d0
KB
9 */
10
5f6de66c 11#if defined(LIBC_SCCS) && !defined(lint)
1e949ff4 12static char sccsid[] = "@(#)vfprintf.c 5.40 (Berkeley) %G%";
5f6de66c 13#endif /* LIBC_SCCS and not lint */
ad0e16d0 14
1e949ff4
KB
15/*
16 * Actual printf innards.
17 *
18 * This code is large and complicated...
19 */
20
0d15d742 21#include <sys/types.h>
5f6de66c 22#include <stdio.h>
1e949ff4
KB
23#include <string.h>
24#if __STDC__
25#include <stdarg.h>
26#else
27#include <varargs.h>
28#endif
29#include "local.h"
30#include "fvwrite.h"
ad0e16d0 31
1e949ff4
KB
32/*
33 * Define FLOATING_POINT to get floating point.
34 * Define CSH to get a csh-specific version (grr).
35 */
36#ifndef CSH
37#define FLOATING_POINT
38#endif
95a39ea1 39
1e949ff4
KB
40/* end of configuration stuff */
41
42
43#ifdef CSH
44/*
45 * C shell hacks. Ick, gag.
46 */
47#undef BUFSIZ
48#include "sh.h"
49
50printf(fmt, args)
51 char *fmt;
52{
53 FILE f;
2c2ee7b4 54
1e949ff4
KB
55 f._flags = __SWR;
56 return (vfprintf(&f, fmt, &args));
57}
58
59#define __sprint(fp, uio) cshprintv(uio)
60
61cshprintv(uio)
62 register struct __suio *uio;
63{
64 register char *p;
65 register int n, ch, iovcnt;
66 register struct __siov *iov = uio->uio_iov;
67
68 for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) {
69 for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) {
70#ifdef CSHPUTCHAR
71 ch = *p++;
72 CSHPUTCHAR; /* this horrid macro uses `ch' */
73#else
74#undef putchar
75 putchar(*p++);
76#endif
77 }
78 }
79 uio->uio_resid = 0;
80 uio->uio_iovcnt = 0;
81 return (0);
82}
83
84#else /* CSH */
85
86/*
87 * Flush out all the vectors defined by the given uio,
88 * then reset it so that it can be reused.
89 */
90static
91__sprint(fp, uio)
92 FILE *fp;
93 register struct __suio *uio;
94{
95 register int err;
96
97 if (uio->uio_resid == 0) {
98 uio->uio_iovcnt = 0;
99 return (0);
100 }
101 err = __sfvwrite(fp, uio);
102 uio->uio_resid = 0;
103 uio->uio_iovcnt = 0;
104 return (err);
105}
106
107/*
108 * Helper function for `fprintf to unbuffered unix file': creates a
109 * temporary buffer. We only work on write-only files; this avoids
110 * worries about ungetc buffers and so forth.
111 */
112static
113__sbprintf(fp, fmt, ap)
114 register FILE *fp;
115 char *fmt;
116 va_list ap;
117{
118 int ret;
119 FILE fake;
120 unsigned char buf[BUFSIZ];
121
122 /* copy the important variables */
123 fake._flags = fp->_flags & ~__SNBF;
124 fake._file = fp->_file;
125 fake._cookie = fp->_cookie;
126 fake._write = fp->_write;
127
128 /* set up the buffer */
129 fake._bf._base = fake._p = buf;
130 fake._bf._size = fake._w = sizeof(buf);
131 fake._lbfsize = 0; /* not actually used, but Just In Case */
132
133 /* do the work, then copy any error status */
134 ret = vfprintf(&fake, fmt, ap);
135 if (ret >= 0 && fflush(&fake))
136 ret = EOF;
137 if (fake._flags & __SERR)
138 fp->_flags |= __SERR;
139 return (ret);
140}
141
142#endif /* CSH */
143
144
145#ifdef FLOATING_POINT
146
147#include "floatio.h"
95a39ea1 148#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
1e949ff4 149#define DEFPREC 6
6c1ffc8c 150
1e949ff4 151static int cvt();
66d7f790 152
1e949ff4 153#else /* no FLOATING_POINT */
410bd0d3 154
1e949ff4 155#define BUF 40
5ae640f0 156
1e949ff4 157#endif /* FLOATING_POINT */
410bd0d3 158
1e949ff4
KB
159
160/*
161 * Macros for converting digits to letters and vice versa
162 */
163#define to_digit(c) ((c) - '0')
164#define is_digit(c) ((unsigned)to_digit(c) <= 9)
165#define to_char(n) ((n) + '0')
166
167/*
168 * Flags used during conversion.
169 */
410bd0d3
KB
170#define LONGINT 0x01 /* long integer */
171#define LONGDBL 0x02 /* long double; unimplemented */
172#define SHORTINT 0x04 /* short integer */
173#define ALT 0x08 /* alternate form */
174#define LADJUST 0x10 /* left adjustment */
b042624f
KB
175#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
176#define HEXPREFIX 0x40 /* add 0x or 0X prefix */
ad0e16d0 177
1e949ff4
KB
178vfprintf(fp, fmt0, ap)
179 FILE *fp;
180 char *fmt0;
181#if tahoe
182 register /* technically illegal, since we do not know what type va_list is */
183#endif
184 va_list ap;
ad0e16d0 185{
1e949ff4 186 register char *fmt; /* format string */
b042624f 187 register int ch; /* character from fmt */
1e949ff4
KB
188 register int n; /* handy integer (short term usage) */
189 register char *cp; /* handy char pointer (short term usage) */
190 register struct __siov *iovp;/* for PRINT macro */
191 register int flags; /* flags as above */
192 int ret; /* return value accumulator */
193 int width; /* width from format (%8d), or 0 */
194 int prec; /* precision from format (%.3d), or -1 */
195 char sign; /* sign prefix (' ', '+', '-', or \0) */
196#ifdef FLOATING_POINT
197 char softsign; /* temporary negative sign for floats */
b042624f 198 double _double; /* double precision arguments %[eEfgG] */
1e949ff4
KB
199 int fpprec; /* `extra' floating precision in [eEfgG] */
200#endif
b042624f 201 u_long _ulong; /* integer arguments %[diouxX] */
1e949ff4
KB
202 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
203 int dprec; /* a copy of prec if [diouxX], 0 otherwise */
2c2ee7b4 204 int fieldsz; /* field size expanded by sign, etc */
1e949ff4 205 int realsz; /* field size expanded by dprec */
2c2ee7b4 206 int size; /* size of converted field or string */
1e949ff4
KB
207 char *xdigs; /* digits for [xX] conversion */
208#define NIOV 8
209 struct __suio uio; /* output information: summary */
210 struct __siov iov[NIOV];/* ... and individual io vectors */
b042624f 211 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
1e949ff4 212 char ox[2]; /* space for 0x hex-prefix */
5f6de66c 213
1e949ff4
KB
214 /*
215 * Choose PADSIZE to trade efficiency vs size. If larger
216 * printf fields occur frequently, increase PADSIZE (and make
217 * the initialisers below longer).
218 */
219#define PADSIZE 16 /* pad chunk size */
220 static char blanks[PADSIZE] =
221 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
222 static char zeroes[PADSIZE] =
223 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
224
225 /*
226 * BEWARE, these `goto error' on error, and PAD uses `n'.
227 */
228#define PRINT(ptr, len) { \
229 iovp->iov_base = (ptr); \
230 iovp->iov_len = (len); \
231 uio.uio_resid += (len); \
232 iovp++; \
233 if (++uio.uio_iovcnt >= NIOV) { \
234 if (__sprint(fp, &uio)) \
235 goto error; \
236 iovp = iov; \
237 } \
238}
239#define PAD(howmany, with) { \
240 if ((n = (howmany)) > 0) { \
241 while (n > PADSIZE) { \
242 PRINT(with, PADSIZE); \
243 n -= PADSIZE; \
244 } \
245 PRINT(with, n); \
246 } \
247}
248#define FLUSH() { \
249 if (uio.uio_resid && __sprint(fp, &uio)) \
250 goto error; \
251 uio.uio_iovcnt = 0; \
252 iovp = iov; \
253}
254
255 /*
256 * To extend shorts properly, we need both signed and unsigned
257 * argument extraction methods.
258 */
259#define SARG() \
260 (flags&LONGINT ? va_arg(ap, long) : \
261 flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
262 (long)va_arg(ap, int))
263#define UARG() \
264 (flags&LONGINT ? va_arg(ap, u_long) : \
265 flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
266 (u_long)va_arg(ap, u_int))
267
268#ifndef CSH
269 /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
270 if (cantwrite(fp))
87d70ec1
KB
271 return (EOF);
272
1e949ff4
KB
273 /* optimise fprintf(stderr) (and other unbuffered Unix files) */
274 if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
275 fp->_file >= 0)
276 return (__sbprintf(fp, fmt0, ap));
277#endif /* CSH */
ad0e16d0 278
1e949ff4
KB
279 fmt = (char *)fmt0;
280 uio.uio_iov = iovp = iov;
281 uio.uio_resid = 0;
282 uio.uio_iovcnt = 0;
283 ret = 0;
284
285 /*
286 * Scan the format for conversions (`%' character).
287 */
288 for (;;) {
289 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
290 /* void */;
291 if ((n = fmt - cp) != 0) {
292 PRINT(cp, n);
293 ret += n;
294 }
295 if (ch == '\0')
296 goto done;
297 fmt++; /* skip over '%' */
298
299 flags = 0;
300 dprec = 0;
301#ifdef FLOATING_POINT
302 fpprec = 0;
303#endif
304 width = 0;
5f6de66c 305 prec = -1;
410bd0d3 306 sign = '\0';
5f6de66c 307
1e949ff4
KB
308rflag: ch = *fmt++;
309reswitch: switch (ch) {
410bd0d3 310 case ' ':
90041bd4
KB
311 /*
312 * ``If the space and + flags both appear, the space
313 * flag will be ignored.''
314 * -- ANSI X3J11
315 */
316 if (!sign)
317 sign = ' ';
410bd0d3 318 goto rflag;
5f6de66c 319 case '#':
410bd0d3
KB
320 flags |= ALT;
321 goto rflag;
5f6de66c 322 case '*':
66d7f790
KB
323 /*
324 * ``A negative field width argument is taken as a
1e949ff4 325 * - flag followed by a positive field width.''
66d7f790
KB
326 * -- ANSI X3J11
327 * They don't exclude field widths read from args.
328 */
1e949ff4 329 if ((width = va_arg(ap, int)) >= 0)
410bd0d3 330 goto rflag;
66d7f790 331 width = -width;
b042624f 332 /* FALLTHROUGH */
66d7f790 333 case '-':
410bd0d3
KB
334 flags |= LADJUST;
335 goto rflag;
5f6de66c 336 case '+':
40233183 337 sign = '+';
410bd0d3 338 goto rflag;
5f6de66c 339 case '.':
1e949ff4
KB
340 if ((ch = *fmt++) == '*') {
341 n = va_arg(ap, int);
342 prec = n < 0 ? -1 : n;
343 goto rflag;
344 }
345 n = 0;
346 while (is_digit(ch)) {
347 n = 10 * n + to_digit(ch);
348 ch = *fmt++;
66d7f790 349 }
410bd0d3 350 prec = n < 0 ? -1 : n;
1e949ff4 351 goto reswitch;
5f6de66c 352 case '0':
b042624f
KB
353 /*
354 * ``Note that 0 is taken as a flag, not as the
355 * beginning of a field width.''
356 * -- ANSI X3J11
357 */
358 flags |= ZEROPAD;
359 goto rflag;
5f6de66c
KB
360 case '1': case '2': case '3': case '4':
361 case '5': case '6': case '7': case '8': case '9':
410bd0d3 362 n = 0;
5f6de66c 363 do {
1e949ff4
KB
364 n = 10 * n + to_digit(ch);
365 ch = *fmt++;
366 } while (is_digit(ch));
410bd0d3 367 width = n;
1e949ff4
KB
368 goto reswitch;
369#ifdef FLOATING_POINT
66d7f790 370 case 'L':
ad5b98a5 371 flags |= LONGDBL;
410bd0d3 372 goto rflag;
1e949ff4 373#endif
66d7f790 374 case 'h':
410bd0d3
KB
375 flags |= SHORTINT;
376 goto rflag;
5f6de66c 377 case 'l':
410bd0d3
KB
378 flags |= LONGINT;
379 goto rflag;
40233183 380 case 'c':
1e949ff4 381 *(cp = buf) = va_arg(ap, int);
40233183 382 size = 1;
b042624f 383 sign = '\0';
1e949ff4 384 break;
2c2ee7b4
KB
385 case 'D':
386 flags |= LONGINT;
387 /*FALLTHROUGH*/
a5676d90 388 case 'd':
410bd0d3 389 case 'i':
1e949ff4 390 _ulong = SARG();
410bd0d3
KB
391 if ((long)_ulong < 0) {
392 _ulong = -_ulong;
40233183 393 sign = '-';
a5676d90 394 }
1e949ff4 395 base = DEC;
bb5070be 396 goto number;
1e949ff4 397#ifdef FLOATING_POINT
6c1ffc8c 398 case 'e':
0d15d742 399 case 'E':
66d7f790 400 case 'f':
5592524e 401 case 'g':
0d15d742 402 case 'G':
1e949ff4 403 _double = va_arg(ap, double);
95a39ea1 404 /*
90041bd4
KB
405 * don't do unrealistic precision; just pad it with
406 * zeroes later, so buffer size stays rational.
95a39ea1
KB
407 */
408 if (prec > MAXFRACT) {
1e949ff4 409 if (ch != 'g' && ch != 'G' || (flags&ALT))
051644d4 410 fpprec = prec - MAXFRACT;
95a39ea1 411 prec = MAXFRACT;
1e949ff4 412 } else if (prec == -1)
2c2ee7b4 413 prec = DEFPREC;
90041bd4 414 /*
1e949ff4
KB
415 * cvt may have to round up before the "start" of
416 * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
417 * if the first character is still NUL, it did.
418 * softsign avoids negative 0 if _double < 0 but
419 * no significant digits will be shown.
90041bd4 420 */
1e949ff4
KB
421 cp = buf;
422 *cp = '\0';
423 size = cvt(_double, prec, flags, &softsign, ch,
424 cp, buf + sizeof(buf));
90041bd4
KB
425 if (softsign)
426 sign = '-';
1e949ff4
KB
427 if (*cp == '\0')
428 cp++;
429 break;
430#endif /* FLOATING_POINT */
66d7f790 431 case 'n':
b042624f 432 if (flags & LONGINT)
1e949ff4 433 *va_arg(ap, long *) = ret;
b042624f 434 else if (flags & SHORTINT)
1e949ff4 435 *va_arg(ap, short *) = ret;
410bd0d3 436 else
1e949ff4
KB
437 *va_arg(ap, int *) = ret;
438 continue; /* no output */
2c2ee7b4
KB
439 case 'O':
440 flags |= LONGINT;
441 /*FALLTHROUGH*/
ad0e16d0 442 case 'o':
1e949ff4
KB
443 _ulong = UARG();
444 base = OCT;
bb5070be 445 goto nosign;
66d7f790 446 case 'p':
2c71023f 447 /*
c748d653
KB
448 * ``The argument shall be a pointer to void. The
449 * value of the pointer is converted to a sequence
450 * of printable characters, in an implementation-
451 * defined manner.''
452 * -- ANSI X3J11
2c71023f 453 */
b042624f 454 /* NOSTRICT */
1e949ff4
KB
455 _ulong = (u_long)va_arg(ap, void *);
456 base = HEX;
457 xdigs = "0123456789abcdef";
458 flags |= HEXPREFIX;
459 ch = 'x';
bb5070be 460 goto nosign;
ad0e16d0 461 case 's':
1e949ff4
KB
462 if ((cp = va_arg(ap, char *)) == NULL)
463 cp = "(null)";
c748d653
KB
464 if (prec >= 0) {
465 /*
466 * can't use strlen; can only look for the
467 * NUL in the first `prec' characters, and
468 * strlen() will go further.
469 */
1e949ff4 470 char *p = memchr(cp, 0, prec);
c748d653 471
1e949ff4
KB
472 if (p != NULL) {
473 size = p - cp;
c748d653
KB
474 if (size > prec)
475 size = prec;
b042624f 476 } else
c748d653 477 size = prec;
b042624f 478 } else
1e949ff4 479 size = strlen(cp);
b042624f 480 sign = '\0';
1e949ff4 481 break;
2c2ee7b4
KB
482 case 'U':
483 flags |= LONGINT;
484 /*FALLTHROUGH*/
ad0e16d0 485 case 'u':
1e949ff4
KB
486 _ulong = UARG();
487 base = DEC;
bb5070be 488 goto nosign;
ad0e16d0 489 case 'X':
1e949ff4
KB
490 xdigs = "0123456789ABCDEF";
491 goto hex;
ad0e16d0 492 case 'x':
1e949ff4
KB
493 xdigs = "0123456789abcdef";
494hex: _ulong = UARG();
495 base = HEX;
8a91f8d6 496 /* leading 0x/X only if non-zero */
b042624f
KB
497 if (flags & ALT && _ulong != 0)
498 flags |= HEXPREFIX;
bb5070be
KB
499
500 /* unsigned conversions */
b042624f 501nosign: sign = '\0';
89e72465
KB
502 /*
503 * ``... diouXx conversions ... if a precision is
504 * specified, the 0 flag will be ignored.''
505 * -- ANSI X3J11
506 */
b042624f
KB
507number: if ((dprec = prec) >= 0)
508 flags &= ~ZEROPAD;
509
8a91f8d6
KB
510 /*
511 * ``The result of converting a zero value with an
512 * explicit precision of zero is no characters.''
513 * -- ANSI X3J11
514 */
1e949ff4 515 cp = buf + BUF;
b042624f 516 if (_ulong != 0 || prec != 0) {
1e949ff4
KB
517 /*
518 * unsigned mod is hard, and unsigned mod
519 * by a constant is easier than that by
520 * a variable; hence this switch.
521 */
522 switch (base) {
523 case OCT:
524 do {
525 *--cp = to_char(_ulong & 7);
526 _ulong >>= 3;
527 } while (_ulong);
528 /* handle octal leading 0 */
529 if (flags & ALT && *cp != '0')
530 *--cp = '0';
531 break;
bb5070be 532
1e949ff4
KB
533 case DEC:
534 /* many numbers are 1 digit */
535 while (_ulong >= 10) {
536 *--cp = to_char(_ulong % 10);
537 _ulong /= 10;
538 }
539 *--cp = to_char(_ulong);
540 break;
bb5070be 541
1e949ff4
KB
542 case HEX:
543 do {
544 *--cp = xdigs[_ulong & 15];
545 _ulong >>= 4;
546 } while (_ulong);
547 break;
548
549 default:
550 cp = "bug in vfprintf: bad base";
551 goto skipsize;
552 }
bb5070be 553 }
1e949ff4
KB
554 size = buf + BUF - cp;
555 skipsize:
556 break;
557 default: /* "%?" prints ?, unless ? is NUL */
558 if (ch == '\0')
559 goto done;
560 /* pretend it was %c with argument ch */
561 cp = buf;
562 *cp = ch;
563 size = 1;
564 sign = '\0';
ad0e16d0 565 break;
ad0e16d0 566 }
1e949ff4
KB
567
568 /*
569 * All reasonable formats wind up here. At this point,
570 * `cp' points to a string which (if not flags&LADJUST)
571 * should be padded out to `width' places. If
572 * flags&ZEROPAD, it should first be prefixed by any
573 * sign or other prefix; otherwise, it should be blank
574 * padded before the prefix is emitted. After any
575 * left-hand padding and prefixing, emit zeroes
576 * required by a decimal [diouxX] precision, then print
577 * the string proper, then emit zeroes required by any
578 * leftover floating precision; finally, if LADJUST,
579 * pad with blanks.
580 */
581
582 /*
583 * compute actual size, so we know how much to pad.
584 * fieldsz excludes decimal prec; realsz includes it
585 */
586#ifdef FLOATING_POINT
587 fieldsz = size + fpprec;
588#else
589 fieldsz = size;
590#endif
591 if (sign)
592 fieldsz++;
593 else if (flags & HEXPREFIX)
594 fieldsz += 2;
595 realsz = dprec > fieldsz ? dprec : fieldsz;
596
597 /* right-adjusting blank padding */
598 if ((flags & (LADJUST|ZEROPAD)) == 0)
599 PAD(width - realsz, blanks);
600
601 /* prefix */
602 if (sign) {
603 PRINT(&sign, 1);
604 } else if (flags & HEXPREFIX) {
605 ox[0] = '0';
606 ox[1] = ch;
607 PRINT(ox, 2);
608 }
609
610 /* right-adjusting zero padding */
611 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
612 PAD(width - realsz, zeroes);
613
614 /* leading zeroes from decimal precision */
615 PAD(dprec - fieldsz, zeroes);
616
617 /* the string or number proper */
618 PRINT(cp, size);
619
620#ifdef FLOATING_POINT
621 /* trailing f.p. zeroes */
622 PAD(fpprec, zeroes);
623#endif
624
625 /* left-adjusting padding (always blank) */
626 if (flags & LADJUST)
627 PAD(width - realsz, blanks);
628
629 /* finally, adjust ret */
630 ret += width > realsz ? width : realsz;
631
632 FLUSH(); /* copy out the I/O vectors */
ad0e16d0 633 }
1e949ff4
KB
634done:
635 FLUSH();
636error:
637 return (__sferror(fp) ? EOF : ret);
b042624f 638 /* NOTREACHED */
ad0e16d0 639}
cd0a25f4 640
1e949ff4
KB
641#ifdef FLOATING_POINT
642static char *exponent();
643static char *round();
644
2c2ee7b4 645static
90041bd4 646cvt(number, prec, flags, signp, fmtch, startp, endp)
cd0a25f4 647 double number;
0d15d742 648 register int prec;
ef9c9d35 649 int flags;
1e949ff4
KB
650 char *signp;
651 int fmtch;
652 char *startp, *endp;
cd0a25f4 653{
5ae640f0 654 register char *p, *t;
5c1be79b 655 register double fract;
2c2ee7b4 656 int dotrim, expcnt, gformat;
5c1be79b 657 double integer, tmp, modf();
7ea8facc 658
2c2ee7b4 659 dotrim = expcnt = gformat = 0;
1e949ff4
KB
660 if (number < 0) {
661 number = -number;
662 *signp = '-';
663 } else
664 *signp = 0;
665
2c2ee7b4 666 fract = modf(number, &integer);
bb5070be 667
2c2ee7b4
KB
668 /* get an extra slot for rounding. */
669 t = ++startp;
0d15d742 670
862bbfbe 671 /*
2c2ee7b4
KB
672 * get integer portion of number; put into the end of the buffer; the
673 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
862bbfbe 674 */
2c2ee7b4
KB
675 for (p = endp - 1; integer; ++expcnt) {
676 tmp = modf(integer / 10, &integer);
1e949ff4 677 *p-- = to_char((int)((tmp + .01) * 10));
2c2ee7b4 678 }
1e949ff4 679 switch (fmtch) {
2c2ee7b4
KB
680 case 'f':
681 /* reverse integer into beginning of buffer */
682 if (expcnt)
683 for (; ++p < endp; *t++ = *p);
684 else
685 *t++ = '0';
862bbfbe 686 /*
2c2ee7b4
KB
687 * if precision required or alternate flag set, add in a
688 * decimal point.
862bbfbe 689 */
2c2ee7b4
KB
690 if (prec || flags&ALT)
691 *t++ = '.';
692 /* if requires more precision and some fraction left */
693 if (fract) {
694 if (prec)
695 do {
696 fract = modf(fract * 10, &tmp);
1e949ff4 697 *t++ = to_char((int)tmp);
2c2ee7b4
KB
698 } while (--prec && fract);
699 if (fract)
90041bd4
KB
700 startp = round(fract, (int *)NULL, startp,
701 t - 1, (char)0, signp);
2c2ee7b4
KB
702 }
703 for (; prec--; *t++ = '0');
704 break;
705 case 'e':
706 case 'E':
707eformat: if (expcnt) {
708 *t++ = *++p;
709 if (prec || flags&ALT)
5ae640f0 710 *t++ = '.';
2c2ee7b4
KB
711 /* if requires more precision and some integer left */
712 for (; prec && ++p < endp; --prec)
713 *t++ = *p;
714 /*
715 * if done precision and more of the integer component,
716 * round using it; adjust fract so we don't re-round
717 * later.
718 */
719 if (!prec && ++p < endp) {
862bbfbe 720 fract = 0;
90041bd4
KB
721 startp = round((double)0, &expcnt, startp,
722 t - 1, *p, signp);
862bbfbe 723 }
2c2ee7b4
KB
724 /* adjust expcnt for digit in front of decimal */
725 --expcnt;
862bbfbe 726 }
2c2ee7b4
KB
727 /* until first fractional digit, decrement exponent */
728 else if (fract) {
729 /* adjust expcnt for digit in front of decimal */
730 for (expcnt = -1;; --expcnt) {
731 fract = modf(fract * 10, &tmp);
732 if (tmp)
733 break;
862bbfbe 734 }
1e949ff4 735 *t++ = to_char((int)tmp);
2c2ee7b4 736 if (prec || flags&ALT)
5ae640f0 737 *t++ = '.';
cd0a25f4 738 }
862bbfbe 739 else {
2c2ee7b4
KB
740 *t++ = '0';
741 if (prec || flags&ALT)
5ae640f0 742 *t++ = '.';
cd0a25f4 743 }
2c2ee7b4
KB
744 /* if requires more precision and some fraction left */
745 if (fract) {
746 if (prec)
747 do {
748 fract = modf(fract * 10, &tmp);
1e949ff4 749 *t++ = to_char((int)tmp);
2c2ee7b4
KB
750 } while (--prec && fract);
751 if (fract)
90041bd4
KB
752 startp = round(fract, &expcnt, startp,
753 t - 1, (char)0, signp);
ad98686b 754 }
2c2ee7b4
KB
755 /* if requires more precision */
756 for (; prec--; *t++ = '0');
757
758 /* unless alternate flag, trim any g/G format trailing 0's */
759 if (gformat && !(flags&ALT)) {
760 while (t > startp && *--t == '0');
761 if (*t == '.')
762 --t;
763 ++t;
764 }
765 t = exponent(t, expcnt, fmtch);
766 break;
767 case 'g':
768 case 'G':
769 /* a precision of 0 is treated as a precision of 1. */
770 if (!prec)
771 ++prec;
772 /*
773 * ``The style used depends on the value converted; style e
774 * will be used only if the exponent resulting from the
775 * conversion is less than -4 or greater than the precision.''
776 * -- ANSI X3J11
777 */
778 if (expcnt > prec || !expcnt && fract && fract < .0001) {
779 /*
780 * g/G format counts "significant digits, not digits of
781 * precision; for the e/E format, this just causes an
782 * off-by-one problem, i.e. g/G considers the digit
783 * before the decimal point significant and e/E doesn't
784 * count it as precision.
785 */
786 --prec;
787 fmtch -= 2; /* G->E, g->e */
788 gformat = 1;
789 goto eformat;
790 }
791 /*
792 * reverse integer into beginning of buffer,
793 * note, decrement precision
794 */
795 if (expcnt)
796 for (; ++p < endp; *t++ = *p, --prec);
797 else
798 *t++ = '0';
799 /*
800 * if precision required or alternate flag set, add in a
801 * decimal point. If no digits yet, add in leading 0.
802 */
803 if (prec || flags&ALT) {
804 dotrim = 1;
805 *t++ = '.';
806 }
807 else
808 dotrim = 0;
809 /* if requires more precision and some fraction left */
810 if (fract) {
811 if (prec) {
812 do {
813 fract = modf(fract * 10, &tmp);
1e949ff4 814 *t++ = to_char((int)tmp);
2c2ee7b4
KB
815 } while(!tmp);
816 while (--prec && fract) {
817 fract = modf(fract * 10, &tmp);
1e949ff4 818 *t++ = to_char((int)tmp);
862bbfbe 819 }
862bbfbe 820 }
2c2ee7b4 821 if (fract)
90041bd4
KB
822 startp = round(fract, (int *)NULL, startp,
823 t - 1, (char)0, signp);
2c2ee7b4
KB
824 }
825 /* alternate format, adds 0's for precision, else trim 0's */
826 if (flags&ALT)
827 for (; prec--; *t++ = '0');
828 else if (dotrim) {
829 while (t > startp && *--t == '0');
830 if (*t != '.')
831 ++t;
832 }
862bbfbe 833 }
1e949ff4 834 return (t - startp);
2c2ee7b4 835}
862bbfbe 836
2c2ee7b4 837static char *
90041bd4 838round(fract, exp, start, end, ch, signp)
2c2ee7b4 839 double fract;
2c2ee7b4 840 int *exp;
90041bd4
KB
841 register char *start, *end;
842 char ch, *signp;
2c2ee7b4
KB
843{
844 double tmp;
845
846 if (fract)
1e949ff4 847 (void)modf(fract * 10, &tmp);
2c2ee7b4 848 else
1e949ff4 849 tmp = to_digit(ch);
2c2ee7b4
KB
850 if (tmp > 4)
851 for (;; --end) {
852 if (*end == '.')
853 --end;
854 if (++*end <= '9')
855 break;
856 *end = '0';
2c2ee7b4 857 if (end == start) {
90041bd4
KB
858 if (exp) { /* e/E; increment exponent */
859 *end = '1';
860 ++*exp;
861 }
862 else { /* f; add extra digit */
1e949ff4
KB
863 *--end = '1';
864 --start;
90041bd4 865 }
2c2ee7b4 866 break;
862bbfbe 867 }
cd0a25f4 868 }
90041bd4
KB
869 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
870 else if (*signp == '-')
871 for (;; --end) {
872 if (*end == '.')
873 --end;
874 if (*end != '0')
875 break;
876 if (end == start)
877 *signp = 0;
878 }
1e949ff4 879 return (start);
2c2ee7b4 880}
862bbfbe 881
2c2ee7b4
KB
882static char *
883exponent(p, exp, fmtch)
884 register char *p;
885 register int exp;
1e949ff4 886 int fmtch;
2c2ee7b4
KB
887{
888 register char *t;
889 char expbuf[MAXEXP];
862bbfbe 890
2c2ee7b4
KB
891 *p++ = fmtch;
892 if (exp < 0) {
893 exp = -exp;
894 *p++ = '-';
895 }
896 else
897 *p++ = '+';
898 t = expbuf + MAXEXP;
899 if (exp > 9) {
900 do {
1e949ff4 901 *--t = to_char(exp % 10);
2c2ee7b4 902 } while ((exp /= 10) > 9);
1e949ff4 903 *--t = to_char(exp);
2c2ee7b4
KB
904 for (; t < expbuf + MAXEXP; *p++ = *t++);
905 }
906 else {
907 *p++ = '0';
1e949ff4 908 *p++ = to_char(exp);
cd0a25f4 909 }
1e949ff4 910 return (p);
cd0a25f4 911}
1e949ff4 912#endif /* FLOATING_POINT */