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