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