xref: /original-bsd/lib/libc/stdio/vfprintf.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 06/04/93";
13 #endif /* LIBC_SCCS and not lint */
14 
15 /*
16  * Actual printf innards.
17  *
18  * This code is large and complicated...
19  */
20 
21 #include <sys/types.h>
22 
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #if __STDC__
29 #include <stdarg.h>
30 #else
31 #include <varargs.h>
32 #endif
33 
34 #include "local.h"
35 #include "fvwrite.h"
36 
37 /* Define FLOATING_POINT to get floating point. */
38 #define	FLOATING_POINT
39 
40 /*
41  * Flush out all the vectors defined by the given uio,
42  * then reset it so that it can be reused.
43  */
44 static int
45 __sprint(fp, uio)
46 	FILE *fp;
47 	register struct __suio *uio;
48 {
49 	register int err;
50 
51 	if (uio->uio_resid == 0) {
52 		uio->uio_iovcnt = 0;
53 		return (0);
54 	}
55 	err = __sfvwrite(fp, uio);
56 	uio->uio_resid = 0;
57 	uio->uio_iovcnt = 0;
58 	return (err);
59 }
60 
61 /*
62  * Helper function for `fprintf to unbuffered unix file': creates a
63  * temporary buffer.  We only work on write-only files; this avoids
64  * worries about ungetc buffers and so forth.
65  */
66 static int
67 __sbprintf(fp, fmt, ap)
68 	register FILE *fp;
69 	const char *fmt;
70 	va_list ap;
71 {
72 	int ret;
73 	FILE fake;
74 	unsigned char buf[BUFSIZ];
75 
76 	/* copy the important variables */
77 	fake._flags = fp->_flags & ~__SNBF;
78 	fake._file = fp->_file;
79 	fake._cookie = fp->_cookie;
80 	fake._write = fp->_write;
81 
82 	/* set up the buffer */
83 	fake._bf._base = fake._p = buf;
84 	fake._bf._size = fake._w = sizeof(buf);
85 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
86 
87 	/* do the work, then copy any error status */
88 	ret = vfprintf(&fake, fmt, ap);
89 	if (ret >= 0 && fflush(&fake))
90 		ret = EOF;
91 	if (fake._flags & __SERR)
92 		fp->_flags |= __SERR;
93 	return (ret);
94 }
95 
96 /*
97  * Macros for converting digits to letters and vice versa
98  */
99 #define	to_digit(c)	((c) - '0')
100 #define is_digit(c)	((unsigned)to_digit(c) <= 9)
101 #define	to_char(n)	((n) + '0')
102 
103 /*
104  * Convert an unsigned long to ASCII for printf purposes, returning
105  * a pointer to the first character of the string representation.
106  * Octal numbers can be forced to have a leading zero; hex numbers
107  * use the given digits.
108  */
109 static char *
110 __ultoa(val, endp, base, octzero, xdigs)
111 	register u_long val;
112 	char *endp;
113 	int base, octzero;
114 	char *xdigs;
115 {
116 	register char *cp = endp;
117 	register long sval;
118 
119 	/*
120 	 * Handle the three cases separately, in the hope of getting
121 	 * better/faster code.
122 	 */
123 	switch (base) {
124 	case 10:
125 		if (val < 10) {	/* many numbers are 1 digit */
126 			*--cp = to_char(val);
127 			return (cp);
128 		}
129 		/*
130 		 * On many machines, unsigned arithmetic is harder than
131 		 * signed arithmetic, so we do at most one unsigned mod and
132 		 * divide; this is sufficient to reduce the range of
133 		 * the incoming value to where signed arithmetic works.
134 		 */
135 		if (val > LONG_MAX) {
136 			*--cp = to_char(val % 10);
137 			sval = val / 10;
138 		} else
139 			sval = val;
140 		do {
141 			*--cp = to_char(sval % 10);
142 			sval /= 10;
143 		} while (sval != 0);
144 		break;
145 
146 	case 8:
147 		do {
148 			*--cp = to_char(val & 7);
149 			val >>= 3;
150 		} while (val);
151 		if (octzero && *cp != '0')
152 			*--cp = '0';
153 		break;
154 
155 	case 16:
156 		do {
157 			*--cp = xdigs[val & 15];
158 			val >>= 4;
159 		} while (val);
160 		break;
161 
162 	default:			/* oops */
163 		abort();
164 	}
165 	return (cp);
166 }
167 
168 /* Identical to __ultoa, but for quads. */
169 static char *
170 __uqtoa(val, endp, base, octzero, xdigs)
171 	register u_quad_t val;
172 	char *endp;
173 	int base, octzero;
174 	char *xdigs;
175 {
176 	register char *cp = endp;
177 	register quad_t sval;
178 
179 	/* quick test for small values; __ultoa is typically much faster */
180 	/* (perhaps instead we should run until small, then call __ultoa?) */
181 	if (val <= ULONG_MAX)
182 		return (__ultoa((u_long)val, endp, base, octzero, xdigs));
183 	switch (base) {
184 	case 10:
185 		if (val < 10) {
186 			*--cp = to_char(val % 10);
187 			return (cp);
188 		}
189 		if (val > QUAD_MAX) {
190 			*--cp = to_char(val % 10);
191 			sval = val / 10;
192 		} else
193 			sval = val;
194 		do {
195 			*--cp = to_char(sval % 10);
196 			sval /= 10;
197 		} while (sval != 0);
198 		break;
199 
200 	case 8:
201 		do {
202 			*--cp = to_char(val & 7);
203 			val >>= 3;
204 		} while (val);
205 		if (octzero && *cp != '0')
206 			*--cp = '0';
207 		break;
208 
209 	case 16:
210 		do {
211 			*--cp = xdigs[val & 15];
212 			val >>= 4;
213 		} while (val);
214 		break;
215 
216 	default:
217 		abort();
218 	}
219 	return (cp);
220 }
221 
222 #ifdef FLOATING_POINT
223 #include <math.h>
224 #include "floatio.h"
225 
226 #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
227 #define	DEFPREC		6
228 
229 static char *cvt __P((double, int, int, char *, int *, int, int *));
230 static int exponent __P((char *, int, int));
231 
232 #else /* no FLOATING_POINT */
233 
234 #define	BUF		68
235 
236 #endif /* FLOATING_POINT */
237 
238 
239 /*
240  * Flags used during conversion.
241  */
242 #define	ALT		0x001		/* alternate form */
243 #define	HEXPREFIX	0x002		/* add 0x or 0X prefix */
244 #define	LADJUST		0x004		/* left adjustment */
245 #define	LONGDBL		0x008		/* long double; unimplemented */
246 #define	LONGINT		0x010		/* long integer */
247 #define	QUADINT		0x020		/* quad integer */
248 #define	SHORTINT	0x040		/* short integer */
249 #define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
250 #define FPT		0x100		/* Floating point number */
251 int
252 vfprintf(fp, fmt0, ap)
253 	FILE *fp;
254 	const char *fmt0;
255 	va_list ap;
256 {
257 	register char *fmt;	/* format string */
258 	register int ch;	/* character from fmt */
259 	register int n;		/* handy integer (short term usage) */
260 	register char *cp;	/* handy char pointer (short term usage) */
261 	register struct __siov *iovp;/* for PRINT macro */
262 	register int flags;	/* flags as above */
263 	int ret;		/* return value accumulator */
264 	int width;		/* width from format (%8d), or 0 */
265 	int prec;		/* precision from format (%.3d), or -1 */
266 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
267 #ifdef FLOATING_POINT
268 	char softsign;		/* temporary negative sign for floats */
269 	double _double;		/* double precision arguments %[eEfgG] */
270 	int expt;		/* integer value of exponent */
271 	int expsize;		/* character count for expstr */
272 	int ndig;		/* actual number of digits returned by cvt */
273 	char expstr[7];		/* buffer for exponent string */
274 #endif
275 	u_long	ulval;		/* integer arguments %[diouxX] */
276 	u_quad_t uqval;		/* %q integers */
277 	int base;		/* base for [diouxX] conversion */
278 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
279 	int fieldsz;		/* field size expanded by sign, etc */
280 	int realsz;		/* field size expanded by dprec */
281 	int size;		/* size of converted field or string */
282 	char *xdigs;		/* digits for [xX] conversion */
283 #define NIOV 8
284 	struct __suio uio;	/* output information: summary */
285 	struct __siov iov[NIOV];/* ... and individual io vectors */
286 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
287 	char ox[2];		/* space for 0x hex-prefix */
288 
289 	/*
290 	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
291 	 * fields occur frequently, increase PADSIZE and make the initialisers
292 	 * below longer.
293 	 */
294 #define	PADSIZE	16		/* pad chunk size */
295 	static char blanks[PADSIZE] =
296 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
297 	static char zeroes[PADSIZE] =
298 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
299 
300 	/*
301 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
302 	 */
303 #define	PRINT(ptr, len) { \
304 	iovp->iov_base = (ptr); \
305 	iovp->iov_len = (len); \
306 	uio.uio_resid += (len); \
307 	iovp++; \
308 	if (++uio.uio_iovcnt >= NIOV) { \
309 		if (__sprint(fp, &uio)) \
310 			goto error; \
311 		iovp = iov; \
312 	} \
313 }
314 #define	PAD(howmany, with) { \
315 	if ((n = (howmany)) > 0) { \
316 		while (n > PADSIZE) { \
317 			PRINT(with, PADSIZE); \
318 			n -= PADSIZE; \
319 		} \
320 		PRINT(with, n); \
321 	} \
322 }
323 #define	FLUSH() { \
324 	if (uio.uio_resid && __sprint(fp, &uio)) \
325 		goto error; \
326 	uio.uio_iovcnt = 0; \
327 	iovp = iov; \
328 }
329 
330 	/*
331 	 * To extend shorts properly, we need both signed and unsigned
332 	 * argument extraction methods.
333 	 */
334 #define	SARG() \
335 	(flags&LONGINT ? va_arg(ap, long) : \
336 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
337 	    (long)va_arg(ap, int))
338 #define	UARG() \
339 	(flags&LONGINT ? va_arg(ap, u_long) : \
340 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
341 	    (u_long)va_arg(ap, u_int))
342 
343 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
344 	if (cantwrite(fp))
345 		return (EOF);
346 
347 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
348 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
349 	    fp->_file >= 0)
350 		return (__sbprintf(fp, fmt0, ap));
351 
352 	fmt = (char *)fmt0;
353 	uio.uio_iov = iovp = iov;
354 	uio.uio_resid = 0;
355 	uio.uio_iovcnt = 0;
356 	ret = 0;
357 
358 	/*
359 	 * Scan the format for conversions (`%' character).
360 	 */
361 	for (;;) {
362 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
363 			/* void */;
364 		if ((n = fmt - cp) != 0) {
365 			PRINT(cp, n);
366 			ret += n;
367 		}
368 		if (ch == '\0')
369 			goto done;
370 		fmt++;		/* skip over '%' */
371 
372 		flags = 0;
373 		dprec = 0;
374 		width = 0;
375 		prec = -1;
376 		sign = '\0';
377 
378 rflag:		ch = *fmt++;
379 reswitch:	switch (ch) {
380 		case ' ':
381 			/*
382 			 * ``If the space and + flags both appear, the space
383 			 * flag will be ignored.''
384 			 *	-- ANSI X3J11
385 			 */
386 			if (!sign)
387 				sign = ' ';
388 			goto rflag;
389 		case '#':
390 			flags |= ALT;
391 			goto rflag;
392 		case '*':
393 			/*
394 			 * ``A negative field width argument is taken as a
395 			 * - flag followed by a positive field width.''
396 			 *	-- ANSI X3J11
397 			 * They don't exclude field widths read from args.
398 			 */
399 			if ((width = va_arg(ap, int)) >= 0)
400 				goto rflag;
401 			width = -width;
402 			/* FALLTHROUGH */
403 		case '-':
404 			flags |= LADJUST;
405 			goto rflag;
406 		case '+':
407 			sign = '+';
408 			goto rflag;
409 		case '.':
410 			if ((ch = *fmt++) == '*') {
411 				n = va_arg(ap, int);
412 				prec = n < 0 ? -1 : n;
413 				goto rflag;
414 			}
415 			n = 0;
416 			while (is_digit(ch)) {
417 				n = 10 * n + to_digit(ch);
418 				ch = *fmt++;
419 			}
420 			prec = n < 0 ? -1 : n;
421 			goto reswitch;
422 		case '0':
423 			/*
424 			 * ``Note that 0 is taken as a flag, not as the
425 			 * beginning of a field width.''
426 			 *	-- ANSI X3J11
427 			 */
428 			flags |= ZEROPAD;
429 			goto rflag;
430 		case '1': case '2': case '3': case '4':
431 		case '5': case '6': case '7': case '8': case '9':
432 			n = 0;
433 			do {
434 				n = 10 * n + to_digit(ch);
435 				ch = *fmt++;
436 			} while (is_digit(ch));
437 			width = n;
438 			goto reswitch;
439 #ifdef FLOATING_POINT
440 		case 'L':
441 			flags |= LONGDBL;
442 			goto rflag;
443 #endif
444 		case 'h':
445 			flags |= SHORTINT;
446 			goto rflag;
447 		case 'l':
448 			flags |= LONGINT;
449 			goto rflag;
450 		case 'q':
451 			flags |= QUADINT;
452 			goto rflag;
453 		case 'c':
454 			*(cp = buf) = va_arg(ap, int);
455 			size = 1;
456 			sign = '\0';
457 			break;
458 		case 'D':
459 			flags |= LONGINT;
460 			/*FALLTHROUGH*/
461 		case 'd':
462 		case 'i':
463 			if (flags & QUADINT) {
464 				uqval = va_arg(ap, quad_t);
465 				if ((quad_t)uqval < 0) {
466 					uqval = -uqval;
467 					sign = '-';
468 				}
469 			} else {
470 				ulval = SARG();
471 				if ((long)ulval < 0) {
472 					ulval = -ulval;
473 					sign = '-';
474 				}
475 			}
476 			base = 10;
477 			goto number;
478 #ifdef FLOATING_POINT
479 		case 'e':		/* anomalous precision */
480 		case 'E':
481 			prec = (prec == -1) ?
482 				DEFPREC + 1 : prec + 1;
483 			/* FALLTHROUGH */
484 			goto fp_begin;
485 		case 'f':		/* always print trailing zeroes */
486 			if (prec != 0)
487 				flags |= ALT;
488 		case 'g':
489 		case 'G':
490 			if (prec == -1)
491 				prec = DEFPREC;
492 fp_begin:		_double = va_arg(ap, double);
493 			/* do this before tricky precision changes */
494 			if (isinf(_double)) {
495 				if (_double < 0)
496 					sign = '-';
497 				cp = "Inf";
498 				size = 3;
499 				break;
500 			}
501 			if (isnan(_double)) {
502 				cp = "NaN";
503 				size = 3;
504 				break;
505 			}
506 			flags |= FPT;
507 			cp = cvt(_double, prec, flags, &softsign,
508 				&expt, ch, &ndig);
509 			if (ch == 'g' || ch == 'G') {
510 				if (expt <= -4 || expt > prec)
511 					ch = (ch == 'g') ? 'e' : 'E';
512 				else
513 					ch = 'g';
514 			}
515 			if (ch <= 'e') {	/* 'e' or 'E' fmt */
516 				--expt;
517 				expsize = exponent(expstr, expt, ch);
518 				size = expsize + ndig;
519 				if (ndig > 1 || flags & ALT)
520 					++size;
521 			} else if (ch == 'f') {		/* f fmt */
522 				if (expt > 0) {
523 					size = expt;
524 					if (prec || flags & ALT)
525 						size += prec + 1;
526 				} else	/* "0.X" */
527 					size = prec + 2;
528 			} else if (expt >= ndig) {	/* fixed g fmt */
529 				size = expt;
530 				if (flags & ALT)
531 					++size;
532 			} else
533 				size = ndig + (expt > 0 ?
534 					1 : 2 - expt);
535 
536 			if (softsign)
537 				sign = '-';
538 			break;
539 #endif /* FLOATING_POINT */
540 		case 'n':
541 			if (flags & QUADINT)
542 				*va_arg(ap, quad_t *) = ret;
543 			else if (flags & LONGINT)
544 				*va_arg(ap, long *) = ret;
545 			else if (flags & SHORTINT)
546 				*va_arg(ap, short *) = ret;
547 			else
548 				*va_arg(ap, int *) = ret;
549 			continue;	/* no output */
550 		case 'O':
551 			flags |= LONGINT;
552 			/*FALLTHROUGH*/
553 		case 'o':
554 			if (flags & QUADINT)
555 				uqval = va_arg(ap, u_quad_t);
556 			else
557 				ulval = UARG();
558 			base = 8;
559 			goto nosign;
560 		case 'p':
561 			/*
562 			 * ``The argument shall be a pointer to void.  The
563 			 * value of the pointer is converted to a sequence
564 			 * of printable characters, in an implementation-
565 			 * defined manner.''
566 			 *	-- ANSI X3J11
567 			 */
568 			ulval = (u_long)va_arg(ap, void *);
569 			base = 16;
570 			xdigs = "0123456789abcdef";
571 			flags = (flags & ~QUADINT) | HEXPREFIX;
572 			ch = 'x';
573 			goto nosign;
574 		case 's':
575 			if ((cp = va_arg(ap, char *)) == NULL)
576 				cp = "(null)";
577 			if (prec >= 0) {
578 				/*
579 				 * can't use strlen; can only look for the
580 				 * NUL in the first `prec' characters, and
581 				 * strlen() will go further.
582 				 */
583 				char *p = memchr(cp, 0, prec);
584 
585 				if (p != NULL) {
586 					size = p - cp;
587 					if (size > prec)
588 						size = prec;
589 				} else
590 					size = prec;
591 			} else
592 				size = strlen(cp);
593 			sign = '\0';
594 			break;
595 		case 'U':
596 			flags |= LONGINT;
597 			/*FALLTHROUGH*/
598 		case 'u':
599 			if (flags & QUADINT)
600 				uqval = va_arg(ap, u_quad_t);
601 			else
602 				ulval = UARG();
603 			base = 10;
604 			goto nosign;
605 		case 'X':
606 			xdigs = "0123456789ABCDEF";
607 			goto hex;
608 		case 'x':
609 			xdigs = "0123456789abcdef";
610 hex:			if (flags & QUADINT)
611 				uqval = va_arg(ap, u_quad_t);
612 			else
613 				ulval = UARG();
614 			base = 16;
615 			/* leading 0x/X only if non-zero */
616 			if (flags & ALT &&
617 			    (flags & QUADINT ? uqval != 0 : ulval != 0))
618 				flags |= HEXPREFIX;
619 
620 			/* unsigned conversions */
621 nosign:			sign = '\0';
622 			/*
623 			 * ``... diouXx conversions ... if a precision is
624 			 * specified, the 0 flag will be ignored.''
625 			 *	-- ANSI X3J11
626 			 */
627 number:			if ((dprec = prec) >= 0)
628 				flags &= ~ZEROPAD;
629 
630 			/*
631 			 * ``The result of converting a zero value with an
632 			 * explicit precision of zero is no characters.''
633 			 *	-- ANSI X3J11
634 			 */
635 			cp = buf + BUF;
636 			if (flags & QUADINT) {
637 				if (uqval != 0 || prec != 0)
638 					cp = __uqtoa(uqval, cp, base,
639 					    flags & ALT, xdigs);
640 			} else {
641 				if (ulval != 0 || prec != 0)
642 					cp = __ultoa(ulval, cp, base,
643 					    flags & ALT, xdigs);
644 			}
645 			size = buf + BUF - cp;
646 			break;
647 		default:	/* "%?" prints ?, unless ? is NUL */
648 			if (ch == '\0')
649 				goto done;
650 			/* pretend it was %c with argument ch */
651 			cp = buf;
652 			*cp = ch;
653 			size = 1;
654 			sign = '\0';
655 			break;
656 		}
657 
658 		/*
659 		 * All reasonable formats wind up here.  At this point, `cp'
660 		 * points to a string which (if not flags&LADJUST) should be
661 		 * padded out to `width' places.  If flags&ZEROPAD, it should
662 		 * first be prefixed by any sign or other prefix; otherwise,
663 		 * it should be blank padded before the prefix is emitted.
664 		 * After any left-hand padding and prefixing, emit zeroes
665 		 * required by a decimal [diouxX] precision, then print the
666 		 * string proper, then emit zeroes required by any leftover
667 		 * floating precision; finally, if LADJUST, pad with blanks.
668 		 *
669 		 * Compute actual size, so we know how much to pad.
670 		 * fieldsz excludes decimal prec; realsz includes it.
671 		 */
672 		fieldsz = size;
673 		if (sign)
674 			fieldsz++;
675 		else if (flags & HEXPREFIX)
676 			fieldsz += 2;
677 		realsz = dprec > fieldsz ? dprec : fieldsz;
678 
679 		/* right-adjusting blank padding */
680 		if ((flags & (LADJUST|ZEROPAD)) == 0)
681 			PAD(width - realsz, blanks);
682 
683 		/* prefix */
684 		if (sign) {
685 			PRINT(&sign, 1);
686 		} else if (flags & HEXPREFIX) {
687 			ox[0] = '0';
688 			ox[1] = ch;
689 			PRINT(ox, 2);
690 		}
691 
692 		/* right-adjusting zero padding */
693 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
694 			PAD(width - realsz, zeroes);
695 
696 		/* leading zeroes from decimal precision */
697 		PAD(dprec - fieldsz, zeroes);
698 
699 		/* the string or number proper */
700 #ifdef FLOATING_POINT
701 		if ((flags & FPT) == 0) {
702 			PRINT(cp, size);
703 		} else {	/* glue together f_p fragments */
704 			if (ch >= 'f') {	/* 'f' or 'g' */
705 				if (_double == 0) {
706 				/* kludge for __dtoa irregularity */
707 					if (prec == 0 ||
708 					    (flags & ALT) == 0) {
709 						PRINT("0", 1);
710 					} else {
711 						PRINT("0.", 2);
712 						PAD(ndig - 1, zeroes);
713 					}
714 				} else if (expt <= 0) {
715 					PRINT("0.", 2);
716 					PAD(-expt, zeroes);
717 					PRINT(cp, ndig);
718 				} else if (expt >= ndig) {
719 					PRINT(cp, ndig);
720 					PAD(expt - ndig, zeroes);
721 					if (flags & ALT)
722 						PRINT(".", 1);
723 				} else {
724 					PRINT(cp, expt);
725 					cp += expt;
726 					PRINT(".", 1);
727 					PRINT(cp, ndig-expt);
728 				}
729 			} else {	/* 'e' or 'E' */
730 				if (ndig > 1 || flags & ALT) {
731 					ox[0] = *cp++;
732 					ox[1] = '.';
733 					PRINT(ox, 2);
734 					if (_double || flags & ALT == 0) {
735 						PRINT(cp, ndig-1);
736 					} else	/* 0.[0..] */
737 						/* __dtoa irregularity */
738 						PAD(ndig - 1, zeroes);
739 				} else	/* XeYYY */
740 					PRINT(cp, 1);
741 				PRINT(expstr, expsize);
742 			}
743 		}
744 #else
745 		PRINT(cp, size);
746 #endif
747 		/* left-adjusting padding (always blank) */
748 		if (flags & LADJUST)
749 			PAD(width - realsz, blanks);
750 
751 		/* finally, adjust ret */
752 		ret += width > realsz ? width : realsz;
753 
754 		FLUSH();	/* copy out the I/O vectors */
755 	}
756 done:
757 	FLUSH();
758 error:
759 	return (__sferror(fp) ? EOF : ret);
760 	/* NOTREACHED */
761 }
762 
763 #ifdef FLOATING_POINT
764 
765 extern char *__dtoa __P((double, int, int, int *, int *, char **));
766 
767 static char *
768 cvt(value, ndigits, flags, sign, decpt, ch, length)
769 	double value;
770 	int ndigits, flags, *decpt, ch, *length;
771 	char *sign;
772 {
773 	int mode, dsgn;
774 	char *digits, *bp, *rve;
775 
776 	if (ch == 'f')
777 		mode = 3;
778 	else {
779 		mode = 2;
780 	}
781 	if (value < 0) {
782 		value = -value;
783 		*sign = '-';
784 	} else
785 		*sign = '\000';
786 	digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
787 	if (flags & ALT) {	/* Print trailing zeros */
788 		bp = digits + ndigits;
789 		if (ch == 'f') {
790 			if (*digits == '0' && value)
791 				*decpt = -ndigits + 1;
792 			bp += *decpt;
793 		}
794 		if (value == 0)	/* kludge for __dtoa irregularity */
795 			rve = bp;
796 		while (rve < bp)
797 			*rve++ = '0';
798 	}
799 	*length = rve - digits;
800 	return (digits);
801 }
802 
803 static int
804 exponent(p0, exp, fmtch)
805 	char *p0;
806 	int exp, fmtch;
807 {
808 	register char *p, *t;
809 	char expbuf[MAXEXP];
810 
811 	p = p0;
812 	*p++ = fmtch;
813 	if (exp < 0) {
814 		exp = -exp;
815 		*p++ = '-';
816 	}
817 	else
818 		*p++ = '+';
819 	t = expbuf + MAXEXP;
820 	if (exp > 9) {
821 		do {
822 			*--t = to_char(exp % 10);
823 		} while ((exp /= 10) > 9);
824 		*--t = to_char(exp);
825 		for (; t < expbuf + MAXEXP; *p++ = *t++);
826 	}
827 	else {
828 		*p++ = '0';
829 		*p++ = to_char(exp);
830 	}
831 	return (p - p0);
832 }
833 #endif /* FLOATING_POINT */
834