handle exponents greater than 2 digits
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
CommitLineData
ad0e16d0 1/*
5f6de66c
KB
2 * Copyright (c) 1988 Regents of the University of California.
3 * All rights reserved.
ad0e16d0 4 *
5f6de66c
KB
5 * Redistribution and use in source and binary forms are permitted
6 * provided that this notice is preserved and that due credit is given
7 * to the University of California at Berkeley. The name of the University
8 * may not be used to endorse or promote products derived from this
9 * software without specific prior written permission. This software
10 * is provided ``as is'' without express or implied warranty.
ad0e16d0
KB
11 */
12
5f6de66c 13#if defined(LIBC_SCCS) && !defined(lint)
5ae640f0 14static char sccsid[] = "@(#)vfprintf.c 5.27 (Berkeley) %G%";
5f6de66c 15#endif /* LIBC_SCCS and not lint */
ad0e16d0 16
0d15d742 17#include <sys/types.h>
5f6de66c
KB
18#include <varargs.h>
19#include <stdio.h>
20#include <ctype.h>
ad0e16d0 21
95a39ea1
KB
22/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
23#define MAXEXP 308
24/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
25#define MAXFRACT 39
26
27#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
6c1ffc8c 28
ef9c9d35 29#define PUTC(ch) {++cnt; putc((char)ch, fp);}
66d7f790 30
410bd0d3
KB
31#define ARG() \
32 _ulong = flags&LONGINT ? va_arg(argp, long) : \
33 flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
34
5ae640f0
KB
35#define todigit(c) ((c) - '0')
36#define tochar(n) ((n) + '0')
37
410bd0d3
KB
38/* have to deal with the negative buffer count kludge */
39#define NEGATIVE_COUNT_KLUDGE
40
41#define LONGINT 0x01 /* long integer */
42#define LONGDBL 0x02 /* long double; unimplemented */
43#define SHORTINT 0x04 /* short integer */
44#define ALT 0x08 /* alternate form */
45#define LADJUST 0x10 /* left adjustment */
ad0e16d0 46
ef9c9d35
KB
47_doprnt(fmt0, argp, fp)
48 u_char *fmt0;
5f6de66c 49 va_list argp;
66d7f790 50 register FILE *fp;
ad0e16d0 51{
ef9c9d35
KB
52 register u_char *fmt;
53 register int ch, cnt, n;
54 register char *t;
66d7f790 55 double _double;
40233183 56 u_long _ulong;
95a39ea1
KB
57 int base, flags, fpprec, prec, size, width;
58 char padc, sign, *digs, buf[BUF], *_cvt();
5f6de66c 59
ef9c9d35 60 fmt = fmt0;
5592524e 61 digs = "0123456789abcdef";
65942971 62 for (cnt = 0;; ++fmt) {
410bd0d3
KB
63 n = fp->_cnt;
64 for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
65 if (--n < 0
66#ifdef NEGATIVE_COUNT_KLUDGE
67 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
68#endif
69 || ch == '\n' && fp->_flag&_IOLBF) {
70 fp->_cnt = n;
71 fp->_ptr = t;
72 (void)_flsbuf(ch, fp);
73 n = fp->_cnt;
74 t = fp->_ptr;
40233183 75 }
410bd0d3
KB
76 else
77 *t++ = ch;
78 fp->_cnt = n;
79 fp->_ptr = t;
80 if (!ch)
40233183 81 return(cnt);
ad0e16d0 82
95a39ea1 83 flags = fpprec = width = 0;
5f6de66c
KB
84 prec = -1;
85 padc = ' ';
410bd0d3 86 sign = '\0';
5f6de66c 87
410bd0d3
KB
88rflag: switch (*++fmt) {
89 case ' ':
90 sign = ' ';
91 goto rflag;
5f6de66c 92 case '#':
410bd0d3
KB
93 flags |= ALT;
94 goto rflag;
5f6de66c 95 case '*':
66d7f790
KB
96 /*
97 * ``A negative field width argument is taken as a
98 * - flag followed by a positive field width.''
99 * -- ANSI X3J11
100 * They don't exclude field widths read from args.
101 */
102 if ((width = va_arg(argp, int)) >= 0)
410bd0d3 103 goto rflag;
66d7f790
KB
104 width = -width;
105 /*FALLTHROUGH*/
106 case '-':
410bd0d3
KB
107 flags |= LADJUST;
108 goto rflag;
5f6de66c 109 case '+':
40233183 110 sign = '+';
410bd0d3 111 goto rflag;
5f6de66c 112 case '.':
66d7f790 113 if (*++fmt == '*')
410bd0d3
KB
114 n = va_arg(argp, int);
115 else if (isascii(*fmt) && isdigit(*fmt)) {
116 n = 0;
5f6de66c 117 do {
5ae640f0 118 n = 10 * n + todigit(*fmt);
410bd0d3 119 } while (isascii(*++fmt) && isdigit(*fmt));
5f6de66c 120 --fmt;
ad0e16d0 121 }
66d7f790 122 else {
66d7f790 123 --fmt;
410bd0d3
KB
124 prec = 0;
125 goto rflag;
66d7f790 126 }
410bd0d3
KB
127 prec = n < 0 ? -1 : n;
128 goto rflag;
5f6de66c
KB
129 case '0':
130 padc = '0';
66d7f790 131 /*FALLTHROUGH*/
5f6de66c
KB
132 case '1': case '2': case '3': case '4':
133 case '5': case '6': case '7': case '8': case '9':
410bd0d3 134 n = 0;
5f6de66c 135 do {
5ae640f0 136 n = 10 * n + todigit(*fmt);
410bd0d3
KB
137 } while (isascii(*++fmt) && isdigit(*fmt));
138 width = n;
5f6de66c 139 --fmt;
aa876f9d 140 goto rflag;
66d7f790 141 case 'L':
ad5b98a5 142 flags |= LONGDBL;
410bd0d3 143 goto rflag;
66d7f790 144 case 'h':
410bd0d3
KB
145 flags |= SHORTINT;
146 goto rflag;
5f6de66c 147 case 'l':
410bd0d3
KB
148 flags |= LONGINT;
149 goto rflag;
40233183 150 case 'c':
aa876f9d 151 buf[0] = va_arg(argp, int);
40233183 152 size = 1;
aa876f9d 153 t = buf;
40233183 154 goto pforw;
a5676d90 155 case 'd':
410bd0d3
KB
156 case 'i':
157 ARG();
158 if ((long)_ulong < 0) {
159 _ulong = -_ulong;
40233183 160 sign = '-';
a5676d90 161 }
a5676d90 162 base = 10;
bb5070be 163 goto number;
6c1ffc8c 164 case 'e':
0d15d742 165 case 'E':
66d7f790 166 case 'f':
5592524e 167 case 'g':
0d15d742 168 case 'G':
5592524e 169 _double = va_arg(argp, double);
95a39ea1
KB
170 /*
171 * don't bother to do unrealistic precision; just
172 * pad it with zeroes later. This keeps buffer size
173 * rational.
174 */
175 if (prec > MAXFRACT) {
176 fpprec = prec - MAXFRACT;
177 prec = MAXFRACT;
178 }
bb5070be 179 size = _cvt(_double, prec, flags, *fmt, padc, &sign,
ef9c9d35 180 buf, buf + sizeof(buf)) - buf;
aa876f9d 181 t = buf;
bb5070be
KB
182 /*
183 * zero-padded sign put out here; blank padded sign
184 * placed in number in _cvt().
185 */
186 if (sign && padc == '0') {
187 PUTC(sign);
188 --width;
189 }
40233183 190 goto pforw;
66d7f790 191 case 'n':
ad5b98a5 192 if (flags&LONGINT)
410bd0d3
KB
193 *va_arg(argp, long *) = cnt;
194 else if (flags&SHORTINT)
195 *va_arg(argp, short *) = cnt;
196 else
197 *va_arg(argp, int *) = cnt;
66d7f790 198 break;
ad0e16d0 199 case 'o':
410bd0d3 200 ARG();
ad0e16d0 201 base = 8;
bb5070be 202 goto nosign;
66d7f790 203 case 'p':
2c71023f 204 /*
c748d653
KB
205 * ``The argument shall be a pointer to void. The
206 * value of the pointer is converted to a sequence
207 * of printable characters, in an implementation-
208 * defined manner.''
209 * -- ANSI X3J11
2c71023f 210 */
bb5070be 211 /*NOSTRICT*/
2c71023f
KB
212 _ulong = (u_long)va_arg(argp, void *);
213 base = 16;
bb5070be 214 goto nosign;
ad0e16d0 215 case 's':
40233183
KB
216 if (!(t = va_arg(argp, char *)))
217 t = "(null)";
c748d653
KB
218 if (prec >= 0) {
219 /*
220 * can't use strlen; can only look for the
221 * NUL in the first `prec' characters, and
222 * strlen() will go further.
223 */
224 char *p, *memchr();
225
226 if (p = memchr(t, 0, prec)) {
227 size = p - t;
228 if (size > prec)
229 size = prec;
230 }
231 else
232 size = prec;
233 }
234 else
235 size = strlen(t);
95a39ea1 236 goto pforw;
ad0e16d0 237 case 'u':
410bd0d3 238 ARG();
ad0e16d0 239 base = 10;
bb5070be 240 goto nosign;
ad0e16d0
KB
241 case 'X':
242 digs = "0123456789ABCDEF";
5f6de66c 243 /*FALLTHROUGH*/
ad0e16d0 244 case 'x':
410bd0d3 245 ARG();
40233183 246 base = 16;
8a91f8d6
KB
247 /* leading 0x/X only if non-zero */
248 if (!_ulong)
ed76755f 249 flags &= ~ALT;
bb5070be
KB
250
251 /* unsigned conversions */
252nosign: sign = NULL;
89e72465
KB
253 /*
254 * ``... diouXx conversions ... if a precision is
255 * specified, the 0 flag will be ignored.''
256 * -- ANSI X3J11
257 */
258number: if (prec >= 0)
259 padc = ' ';
8a91f8d6
KB
260 /*
261 * ``The result of converting a zero value with an
262 * explicit precision of zero is no characters.''
263 * -- ANSI X3J11
264 */
89e72465
KB
265 if (!_ulong && !prec) {
266 size = 0;
267 goto pforw;
268 }
bb5070be 269
95a39ea1 270 t = buf + BUF - 1;
5f6de66c 271 do {
40233183
KB
272 *t-- = digs[_ulong % base];
273 _ulong /= base;
274 } while(_ulong);
95a39ea1 275 for (size = buf + BUF - 1 - t; size < prec; ++size)
ed76755f 276 *t-- = '0';
95a39ea1 277 digs = "0123456789abcdef";
bb5070be
KB
278
279 /* alternate mode for hex and octal numbers */
ed76755f
KB
280 if (flags&ALT)
281 switch (base) {
282 case 16:
283 /* avoid "00000x35" */
bb5070be 284 if (padc == ' ') {
ed76755f
KB
285 *t-- = *fmt;
286 *t-- = '0';
95a39ea1 287 size += 2;
ed76755f 288 }
bb5070be
KB
289 else {
290 PUTC('0');
291 PUTC(*fmt);
95a39ea1 292 width -= 2;
bb5070be 293 }
ed76755f
KB
294 break;
295 case 8:
296 if (t[1] != '0') {
297 *t-- = '0';
95a39ea1 298 ++size;
ed76755f
KB
299 }
300 break;
40233183 301 }
bb5070be
KB
302
303 if (sign) {
304 /* avoid "0000-3" */
95a39ea1 305 if (padc == ' ') {
bb5070be 306 *t-- = sign;
95a39ea1
KB
307 ++size;
308 }
309 else {
bb5070be 310 PUTC(sign);
95a39ea1
KB
311 --width;
312 }
bb5070be 313 }
95a39ea1 314 ++t;
bb5070be 315
95a39ea1
KB
316pforw: if (!(flags&LADJUST) && width)
317 for (n = size + fpprec; n++ < width;)
f853d467 318 PUTC(padc);
95a39ea1
KB
319 if (fp->_cnt - (n = size) >= 0) {
320 cnt += n;
321 fp->_cnt -= n;
322 bcopy(t, fp->_ptr, n);
323 fp->_ptr += n;
324 }
325 else for (; n--; ++t)
40233183 326 PUTC(*t);
95a39ea1
KB
327 while (fpprec--)
328 PUTC('0');
329 if (flags&LADJUST)
330 for (n = size + fpprec; ++n < width;)
331 PUTC(' ');
ad0e16d0 332 break;
5f6de66c 333 case '\0': /* "%?" prints ?, unless ? is NULL */
40233183 334 return(cnt);
ad0e16d0 335 default:
f853d467 336 PUTC(*fmt);
ad0e16d0 337 }
ad0e16d0 338 }
40233183 339 /*NOTREACHED*/
ad0e16d0 340}
cd0a25f4 341
0d15d742
KB
342#define EFORMAT 0x01
343#define FFORMAT 0x02
344#define GFORMAT 0x04
40233183 345#define DEFPREC 6
0d15d742 346
ef9c9d35 347static char *
bb5070be 348_cvt(number, prec, flags, fmtch, padc, sign, startp, endp)
cd0a25f4 349 double number;
0d15d742 350 register int prec;
ef9c9d35
KB
351 int flags;
352 u_char fmtch;
bb5070be 353 char padc, *sign, *startp, *endp;
cd0a25f4 354{
5ae640f0 355 register char *p, *t;
0d15d742 356 register int expcnt, format;
862bbfbe 357 double fract, integer, tmp, modf();
0d15d742 358 int decpt;
5ae640f0 359 char *savep, exponent[MAXEXP];
cd0a25f4 360
40233183 361 if (prec == -1)
cd0a25f4 362 prec = DEFPREC;
cd0a25f4 363
40233183 364 if (number < 0) {
bb5070be 365 *sign = '-';
862bbfbe
KB
366 number = -number;
367 }
bb5070be
KB
368
369 /* if blank padded, add sign in as part of the number */
370 if (*sign && padc == ' ')
371 *startp++ = *sign;
862bbfbe 372
0d15d742
KB
373 switch(fmtch) {
374 case 'e':
375 case 'E':
376 format = EFORMAT;
377 break;
378 case 'f':
379 format = FFORMAT;
380 break;
381 case 'g':
382 case 'G':
383 format = GFORMAT;
384 fmtch -= 2;
385 }
386
862bbfbe
KB
387 /*
388 * if the alternate flag is set, or, at least one digit of precision
389 * was requested, add a decimal point, unless it's the g/G format
40233183 390 * in which case we require two digits of precision, as it counts
862bbfbe
KB
391 * precision differently.
392 */
410bd0d3 393 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
cd0a25f4 394
862bbfbe 395 expcnt = 0;
ef9c9d35 396 p = endp - 1;
862bbfbe
KB
397 fract = modf(number, &integer);
398 if (integer) {
862bbfbe
KB
399 /* get integer part of number; count decimal places */
400 for (; integer; ++expcnt) {
401 tmp = modf(integer / 10, &integer);
5ae640f0 402 *p-- = tochar((int)((tmp + .03) * 10));
cd0a25f4 403 }
862bbfbe
KB
404
405 /* copy, in reverse order, to start of buffer */
5ae640f0
KB
406 t = startp;
407 *t++ = *++p;
862bbfbe
KB
408
409 /*
410 * if the format is g/G, and the resulting exponent will be
411 * greater than the precision, use e/E format. If e/E format,
412 * put in a decimal point as needed, and decrement precision
413 * count for each digit after the decimal point.
414 */
415 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
416 if (format&GFORMAT) {
417 format |= EFORMAT;
418
419 /* first digit is precision for g/G format */
420 if (prec)
421 --prec;
422 }
423 if (decpt)
5ae640f0
KB
424 *t++ = '.';
425 for (; ++p < endp && prec; --prec, *t++ = *p);
862bbfbe 426
410bd0d3 427 /* precision ran out, round */
862bbfbe
KB
428 if (p < endp) {
429 if (*p > '4') {
5ae640f0
KB
430 for (savep = t--;; *t-- = '0') {
431 if (*t == '.')
432 --t;
433 if (++*t <= '9')
862bbfbe
KB
434 break;
435 }
5ae640f0 436 t = savep;
862bbfbe
KB
437 }
438 fract = 0;
439 }
440 }
441 /*
40233183
KB
442 * g/G in f format; if out of precision, replace digits with
443 * zeroes, note, have to round first.
862bbfbe
KB
444 */
445 else if (format&GFORMAT) {
5ae640f0 446 for (; ++p < endp && prec; --prec, *t++ = *p);
862bbfbe
KB
447 /* precision ran out; round and then add zeroes */
448 if (p < endp) {
449 if (*p > '4') {
5ae640f0
KB
450 for (savep = t--; ++*t > '9';
451 *t-- = '0');
452 t = savep;
862bbfbe
KB
453 }
454 do {
5ae640f0 455 *t++ = '0';
862bbfbe
KB
456 } while (++p < endp);
457 fract = 0;
458 }
459 if (decpt)
5ae640f0 460 *t++ = '.';
cd0a25f4 461 }
862bbfbe
KB
462 /* f format */
463 else {
5ae640f0 464 for (; ++p < endp; *t++ = *p);
862bbfbe 465 if (decpt)
5ae640f0 466 *t++ = '.';
cd0a25f4 467 }
5ae640f0 468 p = t;
862bbfbe
KB
469 }
470 /*
410bd0d3
KB
471 * if no fraction, the number was zero, and if no precision, can't
472 * show anything after the decimal point.
862bbfbe
KB
473 */
474 else if (!fract || !prec) {
475 *startp++ = '0';
410bd0d3 476 if (decpt && !(format&GFORMAT))
862bbfbe 477 *startp++ = '.';
410bd0d3 478 *startp = '\0';
ef9c9d35 479 return(startp);
862bbfbe
KB
480 }
481 /*
482 * if the format is g/G, and the resulting exponent will be less than
483 * -4 use e/E format. If e/E format, compute exponent value.
484 */
485 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
486 format |= EFORMAT;
487 if (fract)
488 for (p = startp; fract;) {
489 fract = modf(fract * 10, &tmp);
490 if (!tmp) {
491 --expcnt;
492 continue;
493 }
5ae640f0 494 *p++ = tochar((int)tmp);
862bbfbe
KB
495 break;
496 }
cd0a25f4 497 else
862bbfbe
KB
498 *p++ = '0';
499
500 /* g/G format, decrement precision for first digit */
501 if (format&GFORMAT && prec)
502 --prec;
503
504 /* add decimal after first non-zero digit */
505 if (decpt)
506 *p++ = '.';
cd0a25f4 507 }
862bbfbe
KB
508 /*
509 * f format or g/G printed as f format; don't worry about decimal
510 * point, if g/G format doesn't need it, will get stripped later.
511 */
cd0a25f4 512 else {
862bbfbe
KB
513 p = startp;
514 *p++ = '0';
515 *p++ = '.';
516 }
517
aa876f9d
KB
518 /* finish out requested precision */
519 while (fract && prec-- > 0) {
520 fract = modf(fract * 10, &tmp);
5ae640f0 521 *p++ = tochar((int)tmp);
aa876f9d
KB
522 }
523 while (prec-- > 0)
524 *p++ = '0';
862bbfbe
KB
525
526 /*
527 * if any fractional value left, "round" it back up to the beginning
528 * of the number, fixing the exponent as necessary, and avoiding the
529 * decimal point.
530 */
531 if (fract) {
532 (void)modf(fract * 10, &tmp);
533 if (tmp > 4) {
534 for (savep = p--;; *p-- = '0') {
535 if (*p == '.')
536 --p;
537 if (p == startp) {
538 *p = '1';
539 ++expcnt;
540 break;
541 }
542 if (++*p <= '9')
543 break;
544 }
545 p = savep;
cd0a25f4 546 }
862bbfbe
KB
547 }
548
549 /*
550 * if a g/G format and not alternate flag, lose trailing zeroes,
551 * if e/E or g/G format, and last char is decimal point, lose it.
552 */
410bd0d3 553 if (!(flags&ALT)) {
862bbfbe
KB
554 if (format&GFORMAT)
555 for (; p[-1] == '0'; --p);
556 if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
557 --p;
558 }
559
560 /* if an e/E format, add exponent */
561 if (format&EFORMAT) {
562 *p++ = fmtch;
563 if (--expcnt < 0) {
564 expcnt = -expcnt;
565 *p++ = '-';
cd0a25f4 566 }
862bbfbe
KB
567 else
568 *p++ = '+';
5ae640f0
KB
569 t = exponent + MAXEXP;
570 if (expcnt > 9) {
571 do {
572 *--t = tochar(expcnt % 10);
573 } while ((expcnt /= 10) > 9);
574 *--t = tochar(expcnt);
575 for (; t < exponent + MAXEXP; *p++ = *t++);
576 }
577 else {
578 *p++ = '0';
579 *p++ = tochar(expcnt);
580 }
cd0a25f4 581 }
ef9c9d35 582 return(p);
cd0a25f4 583}