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