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