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