add strdup
[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 5 * Redistribution and use in source and binary forms are permitted
4afb9d15
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
ad0e16d0
KB
16 */
17
5f6de66c 18#if defined(LIBC_SCCS) && !defined(lint)
7d05582d 19static char sccsid[] = "@(#)vfprintf.c 5.36 (Berkeley) %G%";
5f6de66c 20#endif /* LIBC_SCCS and not lint */
ad0e16d0 21
0d15d742 22#include <sys/types.h>
5f6de66c
KB
23#include <varargs.h>
24#include <stdio.h>
25#include <ctype.h>
ad0e16d0 26
95a39ea1
KB
27/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
28#define MAXEXP 308
29/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
30#define MAXFRACT 39
31
2c2ee7b4
KB
32#define DEFPREC 6
33
95a39ea1 34#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
6c1ffc8c 35
b042624f 36#define PUTC(ch) (void) putc(ch, fp)
66d7f790 37
7d05582d
KB
38#define ARG(basetype) \
39 _ulong = flags&LONGINT ? va_arg(argp, long basetype) : \
40 flags&SHORTINT ? (short basetype)va_arg(argp, int) : \
41 va_arg(argp, int)
410bd0d3 42
5ae640f0
KB
43#define todigit(c) ((c) - '0')
44#define tochar(n) ((n) + '0')
45
410bd0d3
KB
46/* have to deal with the negative buffer count kludge */
47#define NEGATIVE_COUNT_KLUDGE
48
49#define LONGINT 0x01 /* long integer */
50#define LONGDBL 0x02 /* long double; unimplemented */
51#define SHORTINT 0x04 /* short integer */
52#define ALT 0x08 /* alternate form */
53#define LADJUST 0x10 /* left adjustment */
b042624f
KB
54#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
55#define HEXPREFIX 0x40 /* add 0x or 0X prefix */
ad0e16d0 56
ef9c9d35
KB
57_doprnt(fmt0, argp, fp)
58 u_char *fmt0;
5f6de66c 59 va_list argp;
66d7f790 60 register FILE *fp;
ad0e16d0 61{
b042624f
KB
62 register u_char *fmt; /* format string */
63 register int ch; /* character from fmt */
64 register int cnt; /* return value accumulator */
65 register int n; /* random handy integer */
66 register char *t; /* buffer pointer */
67 double _double; /* double precision arguments %[eEfgG] */
68 u_long _ulong; /* integer arguments %[diouxX] */
2c2ee7b4 69 int base; /* base for [diouxX] conversion */
b042624f 70 int dprec; /* decimal precision in [diouxX] */
2c2ee7b4
KB
71 int fieldsz; /* field size expanded by sign, etc */
72 int flags; /* flags as above */
b042624f 73 int fpprec; /* `extra' floating precision in [eEfgG] */
b042624f 74 int prec; /* precision from format (%.3d), or -1 */
b042624f 75 int realsz; /* field size expanded by decimal precision */
2c2ee7b4
KB
76 int size; /* size of converted field or string */
77 int width; /* width from format (%8d), or 0 */
90041bd4
KB
78 char sign; /* sign prefix (' ', '+', '-', or \0) */
79 char softsign; /* temporary negative sign for floats */
b042624f
KB
80 char *digs; /* digits for [diouxX] conversion */
81 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
5f6de66c 82
87d70ec1
KB
83 if (fp->_flag & _IORW) {
84 fp->_flag |= _IOWRT;
85 fp->_flag &= ~(_IOEOF|_IOREAD);
86 }
87 if ((fp->_flag & _IOWRT) == 0)
88 return (EOF);
89
ef9c9d35 90 fmt = fmt0;
5592524e 91 digs = "0123456789abcdef";
65942971 92 for (cnt = 0;; ++fmt) {
410bd0d3 93 n = fp->_cnt;
b042624f
KB
94 for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
95 ++cnt, ++fmt)
410bd0d3
KB
96 if (--n < 0
97#ifdef NEGATIVE_COUNT_KLUDGE
98 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
99#endif
b042624f 100 || ch == '\n' && fp->_flag & _IOLBF) {
410bd0d3 101 fp->_cnt = n;
051644d4 102 fp->_ptr = t;
b042624f 103 (void) _flsbuf((u_char)ch, fp);
410bd0d3 104 n = fp->_cnt;
b042624f
KB
105 t = (char *)fp->_ptr;
106 } else
410bd0d3
KB
107 *t++ = ch;
108 fp->_cnt = n;
051644d4 109 fp->_ptr = t;
410bd0d3 110 if (!ch)
b042624f 111 return (cnt);
ad0e16d0 112
5c1be79b 113 flags = 0; dprec = 0; fpprec = 0; width = 0;
5f6de66c 114 prec = -1;
410bd0d3 115 sign = '\0';
5f6de66c 116
410bd0d3
KB
117rflag: switch (*++fmt) {
118 case ' ':
90041bd4
KB
119 /*
120 * ``If the space and + flags both appear, the space
121 * flag will be ignored.''
122 * -- ANSI X3J11
123 */
124 if (!sign)
125 sign = ' ';
410bd0d3 126 goto rflag;
5f6de66c 127 case '#':
410bd0d3
KB
128 flags |= ALT;
129 goto rflag;
5f6de66c 130 case '*':
66d7f790
KB
131 /*
132 * ``A negative field width argument is taken as a
133 * - flag followed by a positive field width.''
134 * -- ANSI X3J11
135 * They don't exclude field widths read from args.
136 */
137 if ((width = va_arg(argp, int)) >= 0)
410bd0d3 138 goto rflag;
66d7f790 139 width = -width;
b042624f 140 /* FALLTHROUGH */
66d7f790 141 case '-':
410bd0d3
KB
142 flags |= LADJUST;
143 goto rflag;
5f6de66c 144 case '+':
40233183 145 sign = '+';
410bd0d3 146 goto rflag;
5f6de66c 147 case '.':
66d7f790 148 if (*++fmt == '*')
410bd0d3 149 n = va_arg(argp, int);
66d7f790 150 else {
b042624f
KB
151 n = 0;
152 while (isascii(*fmt) && isdigit(*fmt))
153 n = 10 * n + todigit(*fmt++);
66d7f790 154 --fmt;
66d7f790 155 }
410bd0d3
KB
156 prec = n < 0 ? -1 : n;
157 goto rflag;
5f6de66c 158 case '0':
b042624f
KB
159 /*
160 * ``Note that 0 is taken as a flag, not as the
161 * beginning of a field width.''
162 * -- ANSI X3J11
163 */
164 flags |= ZEROPAD;
165 goto rflag;
5f6de66c
KB
166 case '1': case '2': case '3': case '4':
167 case '5': case '6': case '7': case '8': case '9':
410bd0d3 168 n = 0;
5f6de66c 169 do {
5ae640f0 170 n = 10 * n + todigit(*fmt);
410bd0d3
KB
171 } while (isascii(*++fmt) && isdigit(*fmt));
172 width = n;
5f6de66c 173 --fmt;
aa876f9d 174 goto rflag;
66d7f790 175 case 'L':
ad5b98a5 176 flags |= LONGDBL;
410bd0d3 177 goto rflag;
66d7f790 178 case 'h':
410bd0d3
KB
179 flags |= SHORTINT;
180 goto rflag;
5f6de66c 181 case 'l':
410bd0d3
KB
182 flags |= LONGINT;
183 goto rflag;
40233183 184 case 'c':
b042624f 185 *(t = buf) = va_arg(argp, int);
40233183 186 size = 1;
b042624f 187 sign = '\0';
40233183 188 goto pforw;
2c2ee7b4
KB
189 case 'D':
190 flags |= LONGINT;
191 /*FALLTHROUGH*/
a5676d90 192 case 'd':
410bd0d3 193 case 'i':
7d05582d 194 ARG(int);
410bd0d3
KB
195 if ((long)_ulong < 0) {
196 _ulong = -_ulong;
40233183 197 sign = '-';
a5676d90 198 }
a5676d90 199 base = 10;
bb5070be 200 goto number;
6c1ffc8c 201 case 'e':
0d15d742 202 case 'E':
66d7f790 203 case 'f':
5592524e 204 case 'g':
0d15d742 205 case 'G':
5592524e 206 _double = va_arg(argp, double);
95a39ea1 207 /*
90041bd4
KB
208 * don't do unrealistic precision; just pad it with
209 * zeroes later, so buffer size stays rational.
95a39ea1
KB
210 */
211 if (prec > MAXFRACT) {
051644d4
KB
212 if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
213 fpprec = prec - MAXFRACT;
95a39ea1
KB
214 prec = MAXFRACT;
215 }
2c2ee7b4
KB
216 else if (prec == -1)
217 prec = DEFPREC;
90041bd4
KB
218 /*
219 * softsign avoids negative 0 if _double is < 0 and
220 * no significant digits will be shown
221 */
2c2ee7b4 222 if (_double < 0) {
90041bd4 223 softsign = '-';
2c2ee7b4
KB
224 _double = -_double;
225 }
90041bd4
KB
226 else
227 softsign = 0;
2c2ee7b4 228 /*
90041bd4 229 * cvt may have to round up past the "start" of the
2c2ee7b4
KB
230 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
231 * if the first char isn't NULL, it did.
232 */
233 *buf = NULL;
90041bd4 234 size = cvt(_double, prec, flags, &softsign, *fmt, buf,
2c2ee7b4 235 buf + sizeof(buf));
90041bd4
KB
236 if (softsign)
237 sign = '-';
2c2ee7b4 238 t = *buf ? buf : buf + 1;
40233183 239 goto pforw;
66d7f790 240 case 'n':
b042624f 241 if (flags & LONGINT)
410bd0d3 242 *va_arg(argp, long *) = cnt;
b042624f 243 else if (flags & SHORTINT)
410bd0d3
KB
244 *va_arg(argp, short *) = cnt;
245 else
246 *va_arg(argp, int *) = cnt;
66d7f790 247 break;
2c2ee7b4
KB
248 case 'O':
249 flags |= LONGINT;
250 /*FALLTHROUGH*/
ad0e16d0 251 case 'o':
7d05582d 252 ARG(unsigned);
ad0e16d0 253 base = 8;
bb5070be 254 goto nosign;
66d7f790 255 case 'p':
2c71023f 256 /*
c748d653
KB
257 * ``The argument shall be a pointer to void. The
258 * value of the pointer is converted to a sequence
259 * of printable characters, in an implementation-
260 * defined manner.''
261 * -- ANSI X3J11
2c71023f 262 */
b042624f 263 /* NOSTRICT */
2c71023f
KB
264 _ulong = (u_long)va_arg(argp, void *);
265 base = 16;
bb5070be 266 goto nosign;
ad0e16d0 267 case 's':
40233183
KB
268 if (!(t = va_arg(argp, char *)))
269 t = "(null)";
c748d653
KB
270 if (prec >= 0) {
271 /*
272 * can't use strlen; can only look for the
273 * NUL in the first `prec' characters, and
274 * strlen() will go further.
275 */
276 char *p, *memchr();
277
278 if (p = memchr(t, 0, prec)) {
279 size = p - t;
280 if (size > prec)
281 size = prec;
b042624f 282 } else
c748d653 283 size = prec;
b042624f 284 } else
c748d653 285 size = strlen(t);
b042624f 286 sign = '\0';
95a39ea1 287 goto pforw;
2c2ee7b4
KB
288 case 'U':
289 flags |= LONGINT;
290 /*FALLTHROUGH*/
ad0e16d0 291 case 'u':
7d05582d 292 ARG(unsigned);
ad0e16d0 293 base = 10;
bb5070be 294 goto nosign;
ad0e16d0
KB
295 case 'X':
296 digs = "0123456789ABCDEF";
b042624f 297 /* FALLTHROUGH */
ad0e16d0 298 case 'x':
7d05582d 299 ARG(unsigned);
40233183 300 base = 16;
8a91f8d6 301 /* leading 0x/X only if non-zero */
b042624f
KB
302 if (flags & ALT && _ulong != 0)
303 flags |= HEXPREFIX;
bb5070be
KB
304
305 /* unsigned conversions */
b042624f 306nosign: sign = '\0';
89e72465
KB
307 /*
308 * ``... diouXx conversions ... if a precision is
309 * specified, the 0 flag will be ignored.''
310 * -- ANSI X3J11
311 */
b042624f
KB
312number: if ((dprec = prec) >= 0)
313 flags &= ~ZEROPAD;
314
8a91f8d6
KB
315 /*
316 * ``The result of converting a zero value with an
317 * explicit precision of zero is no characters.''
318 * -- ANSI X3J11
319 */
b042624f
KB
320 t = buf + BUF;
321 if (_ulong != 0 || prec != 0) {
322 do {
323 *--t = digs[_ulong % base];
324 _ulong /= base;
325 } while (_ulong);
326 digs = "0123456789abcdef";
327 if (flags & ALT && base == 8 && *t != '0')
328 *--t = '0'; /* octal leading 0 */
89e72465 329 }
b042624f 330 size = buf + BUF - t;
bb5070be 331
b042624f
KB
332pforw:
333 /*
2c2ee7b4
KB
334 * All reasonable formats wind up here. At this point,
335 * `t' points to a string which (if not flags&LADJUST)
336 * should be padded out to `width' places. If
337 * flags&ZEROPAD, it should first be prefixed by any
338 * sign or other prefix; otherwise, it should be blank
339 * padded before the prefix is emitted. After any
340 * left-hand padding and prefixing, emit zeroes
341 * required by a decimal [diouxX] precision, then print
342 * the string proper, then emit zeroes required by any
343 * leftover floating precision; finally, if LADJUST,
344 * pad with blanks.
b042624f 345 */
bb5070be 346
2c2ee7b4
KB
347 /*
348 * compute actual size, so we know how much to pad
349 * fieldsz excludes decimal prec; realsz includes it
350 */
b042624f
KB
351 fieldsz = size + fpprec;
352 if (sign)
353 fieldsz++;
354 if (flags & HEXPREFIX)
355 fieldsz += 2;
356 realsz = dprec > fieldsz ? dprec : fieldsz;
bb5070be 357
b042624f
KB
358 /* right-adjusting blank padding */
359 if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
360 for (n = realsz; n < width; n++)
361 PUTC(' ');
362 /* prefix */
363 if (sign)
364 PUTC(sign);
365 if (flags & HEXPREFIX) {
366 PUTC('0');
367 PUTC((char)*fmt);
bb5070be 368 }
b042624f
KB
369 /* right-adjusting zero padding */
370 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
371 for (n = realsz; n < width; n++)
372 PUTC('0');
373 /* leading zeroes from decimal precision */
374 for (n = fieldsz; n < dprec; n++)
375 PUTC('0');
bb5070be 376
b042624f
KB
377 /* the string or number proper */
378 if (fp->_cnt - (n = size) >= 0 &&
379 (fp->_flag & _IOLBF) == 0) {
95a39ea1 380 fp->_cnt -= n;
b042624f 381 bcopy(t, (char *)fp->_ptr, n);
95a39ea1 382 fp->_ptr += n;
b042624f
KB
383 } else
384 while (--n >= 0)
385 PUTC(*t++);
386 /* trailing f.p. zeroes */
387 while (--fpprec >= 0)
95a39ea1 388 PUTC('0');
b042624f
KB
389 /* left-adjusting padding (always blank) */
390 if (flags & LADJUST)
391 for (n = realsz; n < width; n++)
95a39ea1 392 PUTC(' ');
b042624f
KB
393 /* finally, adjust cnt */
394 cnt += width > realsz ? width : realsz;
ad0e16d0 395 break;
b042624f
KB
396 case '\0': /* "%?" prints ?, unless ? is NULL */
397 return (cnt);
ad0e16d0 398 default:
b042624f
KB
399 PUTC((char)*fmt);
400 cnt++;
ad0e16d0 401 }
ad0e16d0 402 }
b042624f 403 /* NOTREACHED */
ad0e16d0 404}
cd0a25f4 405
2c2ee7b4 406static
90041bd4 407cvt(number, prec, flags, signp, fmtch, startp, endp)
cd0a25f4 408 double number;
0d15d742 409 register int prec;
ef9c9d35
KB
410 int flags;
411 u_char fmtch;
90041bd4 412 char *signp, *startp, *endp;
cd0a25f4 413{
5ae640f0 414 register char *p, *t;
5c1be79b 415 register double fract;
2c2ee7b4 416 int dotrim, expcnt, gformat;
5c1be79b 417 double integer, tmp, modf();
90041bd4 418 char *exponent(), *round();
cd0a25f4 419
2c2ee7b4
KB
420 dotrim = expcnt = gformat = 0;
421 fract = modf(number, &integer);
bb5070be 422
2c2ee7b4
KB
423 /* get an extra slot for rounding. */
424 t = ++startp;
0d15d742 425
862bbfbe 426 /*
2c2ee7b4
KB
427 * get integer portion of number; put into the end of the buffer; the
428 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
862bbfbe 429 */
2c2ee7b4
KB
430 for (p = endp - 1; integer; ++expcnt) {
431 tmp = modf(integer / 10, &integer);
432 *p-- = tochar((int)((tmp + .01) * 10));
433 }
434 switch(fmtch) {
435 case 'f':
436 /* reverse integer into beginning of buffer */
437 if (expcnt)
438 for (; ++p < endp; *t++ = *p);
439 else
440 *t++ = '0';
862bbfbe 441 /*
2c2ee7b4
KB
442 * if precision required or alternate flag set, add in a
443 * decimal point.
862bbfbe 444 */
2c2ee7b4
KB
445 if (prec || flags&ALT)
446 *t++ = '.';
447 /* if requires more precision and some fraction left */
448 if (fract) {
449 if (prec)
450 do {
451 fract = modf(fract * 10, &tmp);
452 *t++ = tochar((int)tmp);
453 } while (--prec && fract);
454 if (fract)
90041bd4
KB
455 startp = round(fract, (int *)NULL, startp,
456 t - 1, (char)0, signp);
2c2ee7b4
KB
457 }
458 for (; prec--; *t++ = '0');
459 break;
460 case 'e':
461 case 'E':
462eformat: if (expcnt) {
463 *t++ = *++p;
464 if (prec || flags&ALT)
5ae640f0 465 *t++ = '.';
2c2ee7b4
KB
466 /* if requires more precision and some integer left */
467 for (; prec && ++p < endp; --prec)
468 *t++ = *p;
469 /*
470 * if done precision and more of the integer component,
471 * round using it; adjust fract so we don't re-round
472 * later.
473 */
474 if (!prec && ++p < endp) {
862bbfbe 475 fract = 0;
90041bd4
KB
476 startp = round((double)0, &expcnt, startp,
477 t - 1, *p, signp);
862bbfbe 478 }
2c2ee7b4
KB
479 /* adjust expcnt for digit in front of decimal */
480 --expcnt;
862bbfbe 481 }
2c2ee7b4
KB
482 /* until first fractional digit, decrement exponent */
483 else if (fract) {
484 /* adjust expcnt for digit in front of decimal */
485 for (expcnt = -1;; --expcnt) {
486 fract = modf(fract * 10, &tmp);
487 if (tmp)
488 break;
862bbfbe 489 }
2c2ee7b4
KB
490 *t++ = tochar((int)tmp);
491 if (prec || flags&ALT)
5ae640f0 492 *t++ = '.';
cd0a25f4 493 }
862bbfbe 494 else {
2c2ee7b4
KB
495 *t++ = '0';
496 if (prec || flags&ALT)
5ae640f0 497 *t++ = '.';
cd0a25f4 498 }
2c2ee7b4
KB
499 /* if requires more precision and some fraction left */
500 if (fract) {
501 if (prec)
502 do {
503 fract = modf(fract * 10, &tmp);
504 *t++ = tochar((int)tmp);
505 } while (--prec && fract);
506 if (fract)
90041bd4
KB
507 startp = round(fract, &expcnt, startp,
508 t - 1, (char)0, signp);
ad98686b 509 }
2c2ee7b4
KB
510 /* if requires more precision */
511 for (; prec--; *t++ = '0');
512
513 /* unless alternate flag, trim any g/G format trailing 0's */
514 if (gformat && !(flags&ALT)) {
515 while (t > startp && *--t == '0');
516 if (*t == '.')
517 --t;
518 ++t;
519 }
520 t = exponent(t, expcnt, fmtch);
521 break;
522 case 'g':
523 case 'G':
524 /* a precision of 0 is treated as a precision of 1. */
525 if (!prec)
526 ++prec;
527 /*
528 * ``The style used depends on the value converted; style e
529 * will be used only if the exponent resulting from the
530 * conversion is less than -4 or greater than the precision.''
531 * -- ANSI X3J11
532 */
533 if (expcnt > prec || !expcnt && fract && fract < .0001) {
534 /*
535 * g/G format counts "significant digits, not digits of
536 * precision; for the e/E format, this just causes an
537 * off-by-one problem, i.e. g/G considers the digit
538 * before the decimal point significant and e/E doesn't
539 * count it as precision.
540 */
541 --prec;
542 fmtch -= 2; /* G->E, g->e */
543 gformat = 1;
544 goto eformat;
545 }
546 /*
547 * reverse integer into beginning of buffer,
548 * note, decrement precision
549 */
550 if (expcnt)
551 for (; ++p < endp; *t++ = *p, --prec);
552 else
553 *t++ = '0';
554 /*
555 * if precision required or alternate flag set, add in a
556 * decimal point. If no digits yet, add in leading 0.
557 */
558 if (prec || flags&ALT) {
559 dotrim = 1;
560 *t++ = '.';
561 }
562 else
563 dotrim = 0;
564 /* if requires more precision and some fraction left */
565 if (fract) {
566 if (prec) {
567 do {
568 fract = modf(fract * 10, &tmp);
569 *t++ = tochar((int)tmp);
570 } while(!tmp);
571 while (--prec && fract) {
572 fract = modf(fract * 10, &tmp);
573 *t++ = tochar((int)tmp);
862bbfbe 574 }
862bbfbe 575 }
2c2ee7b4 576 if (fract)
90041bd4
KB
577 startp = round(fract, (int *)NULL, startp,
578 t - 1, (char)0, signp);
2c2ee7b4
KB
579 }
580 /* alternate format, adds 0's for precision, else trim 0's */
581 if (flags&ALT)
582 for (; prec--; *t++ = '0');
583 else if (dotrim) {
584 while (t > startp && *--t == '0');
585 if (*t != '.')
586 ++t;
587 }
862bbfbe 588 }
2c2ee7b4
KB
589 return(t - startp);
590}
862bbfbe 591
2c2ee7b4 592static char *
90041bd4 593round(fract, exp, start, end, ch, signp)
2c2ee7b4 594 double fract;
2c2ee7b4 595 int *exp;
90041bd4
KB
596 register char *start, *end;
597 char ch, *signp;
2c2ee7b4
KB
598{
599 double tmp;
600
601 if (fract)
862bbfbe 602 (void)modf(fract * 10, &tmp);
2c2ee7b4
KB
603 else
604 tmp = todigit(ch);
605 if (tmp > 4)
606 for (;; --end) {
607 if (*end == '.')
608 --end;
609 if (++*end <= '9')
610 break;
611 *end = '0';
2c2ee7b4 612 if (end == start) {
90041bd4
KB
613 if (exp) { /* e/E; increment exponent */
614 *end = '1';
615 ++*exp;
616 }
617 else { /* f; add extra digit */
618 *--end = '1';
619 --start;
620 }
2c2ee7b4 621 break;
862bbfbe 622 }
cd0a25f4 623 }
90041bd4
KB
624 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
625 else if (*signp == '-')
626 for (;; --end) {
627 if (*end == '.')
628 --end;
629 if (*end != '0')
630 break;
631 if (end == start)
632 *signp = 0;
633 }
2c2ee7b4
KB
634 return(start);
635}
862bbfbe 636
2c2ee7b4
KB
637static char *
638exponent(p, exp, fmtch)
639 register char *p;
640 register int exp;
641 u_char fmtch;
642{
643 register char *t;
644 char expbuf[MAXEXP];
862bbfbe 645
2c2ee7b4
KB
646 *p++ = fmtch;
647 if (exp < 0) {
648 exp = -exp;
649 *p++ = '-';
650 }
651 else
652 *p++ = '+';
653 t = expbuf + MAXEXP;
654 if (exp > 9) {
655 do {
656 *--t = tochar(exp % 10);
657 } while ((exp /= 10) > 9);
658 *--t = tochar(exp);
659 for (; t < expbuf + MAXEXP; *p++ = *t++);
660 }
661 else {
662 *p++ = '0';
663 *p++ = tochar(exp);
cd0a25f4 664 }
ef9c9d35 665 return(p);
cd0a25f4 666}