xref: /plan9-contrib/sys/src/ape/lib/ap/stdio/vfprintf.c (revision 22df390c30710ddd2119f3e7bb6c92dc399cabb9)
1 /*
2  * pANS stdio -- vfprintf
3  */
4 #include "iolib.h"
5 #include <stdarg.h>
6 #include <math.h>
7 #include <string.h>
8 /*
9  * Leading flags
10  */
11 #define	SPACE	1		/* ' ' prepend space if no sign printed */
12 #define	ALT	2		/* '#' use alternate conversion */
13 #define	SIGN	4		/* '+' prepend sign, even if positive */
14 #define	LEFT	8		/* '-' left-justify */
15 #define	ZPAD	16		/* '0' zero-pad */
16 /*
17  * Trailing flags
18  */
19 #define	SHORT	32		/* 'h' convert a short integer */
20 #define	LONG	64		/* 'l' convert a long integer */
21 #define	LDBL	128		/* 'L' convert a long double */
22 #define	PTR	256		/*     convert a void * (%p) */
23 #define	VLONG	512		/* 'll' convert a long long integer */
24 
25 static int lflag[] = {	/* leading flags */
26 0,	0,	0,	0,	0,	0,	0,	0,	/* ^@ ^A ^B ^C ^D ^E ^F ^G */
27 0,	0,	0,	0,	0,	0,	0,	0,	/* ^H ^I ^J ^K ^L ^M ^N ^O */
28 0,	0,	0,	0,	0,	0,	0,	0,	/* ^P ^Q ^R ^S ^T ^U ^V ^W */
29 0,	0,	0,	0,	0,	0,	0,	0,	/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
30 SPACE,	0,	0,	ALT,	0,	0,	0,	0,	/* sp  !  "  #  $  %  &  ' */
31 0,	0,	0,	SIGN,	0,	LEFT,	0,	0,	/*  (  )  *  +  ,  -  .  / */
32 ZPAD,	0,	0,	0,	0,	0,	0,	0,	/*  0  1  2  3  4  5  6  7 */
33 0,	0,	0,	0,	0,	0,	0,	0,	/*  8  9  :  ;  <  =  >  ? */
34 0,	0,	0,	0,	0,	0,	0,	0,	/*  @  A  B  C  D  E  F  G */
35 0,	0,	0,	0,	0,	0,	0,	0,	/*  H  I  J  K  L  M  N  O */
36 0,	0,	0,	0,	0,	0,	0,	0,	/*  P  Q  R  S  T  U  V  W */
37 0,	0,	0,	0,	0,	0,	0,	0,	/*  X  Y  Z  [  \  ]  ^  _ */
38 0,	0,	0,	0,	0,	0,	0,	0,	/*  `  a  b  c  d  e  f  g */
39 0,	0,	0,	0,	0,	0,	0,	0,	/*  h  i  j  k  l  m  n  o */
40 0,	0,	0,	0,	0,	0,	0,	0,	/*  p  q  r  s  t  u  v  w */
41 0,	0,	0,	0,	0,	0,	0,	0,	/*  x  y  z  {  |  }  ~ ^? */
42 
43 0,	0,	0,	0,	0,	0,	0,	0,
44 0,	0,	0,	0,	0,	0,	0,	0,
45 0,	0,	0,	0,	0,	0,	0,	0,
46 0,	0,	0,	0,	0,	0,	0,	0,
47 0,	0,	0,	0,	0,	0,	0,	0,
48 0,	0,	0,	0,	0,	0,	0,	0,
49 0,	0,	0,	0,	0,	0,	0,	0,
50 0,	0,	0,	0,	0,	0,	0,	0,
51 0,	0,	0,	0,	0,	0,	0,	0,
52 0,	0,	0,	0,	0,	0,	0,	0,
53 0,	0,	0,	0,	0,	0,	0,	0,
54 0,	0,	0,	0,	0,	0,	0,	0,
55 0,	0,	0,	0,	0,	0,	0,	0,
56 0,	0,	0,	0,	0,	0,	0,	0,
57 0,	0,	0,	0,	0,	0,	0,	0,
58 0,	0,	0,	0,	0,	0,	0,	0,
59 };
60 
61 static int tflag[] = {	/* trailing flags */
62 0,	0,	0,	0,	0,	0,	0,	0,	/* ^@ ^A ^B ^C ^D ^E ^F ^G */
63 0,	0,	0,	0,	0,	0,	0,	0,	/* ^H ^I ^J ^K ^L ^M ^N ^O */
64 0,	0,	0,	0,	0,	0,	0,	0,	/* ^P ^Q ^R ^S ^T ^U ^V ^W */
65 0,	0,	0,	0,	0,	0,	0,	0,	/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
66 0,	0,	0,	0,	0,	0,	0,	0,	/* sp  !  "  #  $  %  &  ' */
67 0,	0,	0,	0,	0,	0,	0,	0,	/*  (  )  *  +  ,  -  .  / */
68 0,	0,	0,	0,	0,	0,	0,	0,	/*  0  1  2  3  4  5  6  7 */
69 0,	0,	0,	0,	0,	0,	0,	0,	/*  8  9  :  ;  <  =  >  ? */
70 0,	0,	0,	0,	0,	0,	0,	0,	/*  @  A  B  C  D  E  F  G */
71 0,	0,	0,	0,	LDBL,	0,	0,	0,	/*  H  I  J  K  L  M  N  O */
72 0,	0,	0,	0,	0,	0,	0,	0,	/*  P  Q  R  S  T  U  V  W */
73 0,	0,	0,	0,	0,	0,	0,	0,	/*  X  Y  Z  [  \  ]  ^  _ */
74 0,	0,	0,	0,	0,	0,	0,	0,	/*  `  a  b  c  d  e  f  g */
75 SHORT,	0,	0,	0,	LONG,	0,	0,	0,	/*  h  i  j  k  l  m  n  o */
76 0,	0,	0,	0,	0,	0,	0,	0,	/*  p  q  r  s  t  u  v  w */
77 0,	0,	0,	0,	0,	0,	0,	0,	/*  x  y  z  {  |  }  ~ ^? */
78 
79 0,	0,	0,	0,	0,	0,	0,	0,
80 0,	0,	0,	0,	0,	0,	0,	0,
81 0,	0,	0,	0,	0,	0,	0,	0,
82 0,	0,	0,	0,	0,	0,	0,	0,
83 0,	0,	0,	0,	0,	0,	0,	0,
84 0,	0,	0,	0,	0,	0,	0,	0,
85 0,	0,	0,	0,	0,	0,	0,	0,
86 0,	0,	0,	0,	0,	0,	0,	0,
87 0,	0,	0,	0,	0,	0,	0,	0,
88 0,	0,	0,	0,	0,	0,	0,	0,
89 0,	0,	0,	0,	0,	0,	0,	0,
90 0,	0,	0,	0,	0,	0,	0,	0,
91 0,	0,	0,	0,	0,	0,	0,	0,
92 0,	0,	0,	0,	0,	0,	0,	0,
93 0,	0,	0,	0,	0,	0,	0,	0,
94 0,	0,	0,	0,	0,	0,	0,	0,
95 };
96 
97 static int ocvt_E(FILE *, va_list *, int, int, int);
98 static int ocvt_G(FILE *, va_list *, int, int, int);
99 static int ocvt_X(FILE *, va_list *, int, int, int);
100 static int ocvt_c(FILE *, va_list *, int, int, int);
101 static int ocvt_d(FILE *, va_list *, int, int, int);
102 static int ocvt_e(FILE *, va_list *, int, int, int);
103 static int ocvt_f(FILE *, va_list *, int, int, int);
104 static int ocvt_g(FILE *, va_list *, int, int, int);
105 static int ocvt_n(FILE *, va_list *, int, int, int);
106 static int ocvt_o(FILE *, va_list *, int, int, int);
107 static int ocvt_p(FILE *, va_list *, int, int, int);
108 static int ocvt_s(FILE *, va_list *, int, int, int);
109 static int ocvt_u(FILE *, va_list *, int, int, int);
110 static int ocvt_x(FILE *, va_list *, int, int, int);
111 
112 static int(*ocvt[])(FILE *, va_list *, int, int, int) = {
113 0,	0,	0,	0,	0,	0,	0,	0,	/* ^@ ^A ^B ^C ^D ^E ^F ^G */
114 0,	0,	0,	0,	0,	0,	0,	0,	/* ^H ^I ^J ^K ^L ^M ^N ^O */
115 0,	0,	0,	0,	0,	0,	0,	0,	/* ^P ^Q ^R ^S ^T ^U ^V ^W */
116 0,	0,	0,	0,	0,	0,	0,	0,	/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
117 0,	0,	0,	0,	0,	0,	0,	0,	/* sp  !  "  #  $  %  &  ' */
118 0,	0,	0,	0,	0,	0,	0,	0,	/*  (  )  *  +  ,  -  .  / */
119 0,	0,	0,	0,	0,	0,	0,	0,	/*  0  1  2  3  4  5  6  7 */
120 0,	0,	0,	0,	0,	0,	0,	0,	/*  8  9  :  ;  <  =  >  ? */
121 0,	0,	0,	0,	0,	ocvt_E,	0,	ocvt_G,	/*  @  A  B  C  D  E  F  G */
122 0,	0,	0,	0,	0,	0,	0,	0,	/*  H  I  J  K  L  M  N  O */
123 0,	0,	0,	0,	0,	0,	0,	0,	/*  P  Q  R  S  T  U  V  W */
124 ocvt_X,	0,	0,	0,	0,	0,	0,	0,	/*  X  Y  Z  [  \  ]  ^  _ */
125 0,	0,	0,	ocvt_c,	ocvt_d,	ocvt_e,	ocvt_f,	ocvt_g,	/*  `  a  b  c  d  e  f  g */
126 0,	ocvt_d,	0,	0,	0,	0,	ocvt_n,	ocvt_o,	/*  h  i  j  k  l  m  n  o */
127 ocvt_p,	0,	0,	ocvt_s,	0,	ocvt_u,	0,	0,	/*  p  q  r  s  t  u  v  w */
128 ocvt_x,	0,	0,	0,	0,	0,	0,	0,	/*  x  y  z  {  |  }  ~ ^? */
129 
130 0,	0,	0,	0,	0,	0,	0,	0,
131 0,	0,	0,	0,	0,	0,	0,	0,
132 0,	0,	0,	0,	0,	0,	0,	0,
133 0,	0,	0,	0,	0,	0,	0,	0,
134 0,	0,	0,	0,	0,	0,	0,	0,
135 0,	0,	0,	0,	0,	0,	0,	0,
136 0,	0,	0,	0,	0,	0,	0,	0,
137 0,	0,	0,	0,	0,	0,	0,	0,
138 0,	0,	0,	0,	0,	0,	0,	0,
139 0,	0,	0,	0,	0,	0,	0,	0,
140 0,	0,	0,	0,	0,	0,	0,	0,
141 0,	0,	0,	0,	0,	0,	0,	0,
142 0,	0,	0,	0,	0,	0,	0,	0,
143 0,	0,	0,	0,	0,	0,	0,	0,
144 0,	0,	0,	0,	0,	0,	0,	0,
145 0,	0,	0,	0,	0,	0,	0,	0,
146 };
147 
148 static int nprint;
149 
150 int
vfprintf(FILE * f,const char * as,va_list args)151 vfprintf(FILE *f, const char *as, va_list args)
152 {
153 	int tfl, flags, width, precision;
154 	unsigned char *s;
155 
156 	nprint = 0;
157 	s = (unsigned char *)as;
158 	while(*s){
159 		if(*s != '%'){
160 			putc(*s++, f);
161 			nprint++;
162 			continue;
163 		}
164 		s++;
165 		flags = 0;
166 		while(lflag[*s]) flags |= lflag[*s++];
167 		if(*s == '*'){
168 			width = va_arg(args, int);
169 			s++;
170 			if(width<0){
171 				flags |= LEFT;
172 				width = -width;
173 			}
174 		}
175 		else{
176 			width = 0;
177 			while('0'<=*s && *s<='9') width = width*10 + *s++ - '0';
178 		}
179 		if(*s == '.'){
180 			s++;
181 			if(*s == '*'){
182 				precision = va_arg(args, int);
183 				s++;
184 			}
185 			else{
186 				precision = 0;
187 				while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0';
188 			}
189 		}
190 		else
191 			precision = -1;
192 		while(tfl = tflag[*s]){
193 			if(tfl == LONG && (flags & LONG)){
194 				flags &= ~LONG;
195 				tfl = VLONG;
196 			}
197 			flags |= tfl;
198 			s++;
199 		}
200 		if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision);
201 		else if(*s){
202 			putc(*s++, f);
203 			nprint++;
204 		}
205 	}
206 	return ferror(f)? -1: nprint;;
207 }
208 
209 static int
ocvt_c(FILE * f,va_list * args,int flags,int width,int precision)210 ocvt_c(FILE *f, va_list *args, int flags, int width, int precision)
211 {
212 	int i;
213 
214 	USED(precision);
215 	if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f);
216 	putc((unsigned char)va_arg(*args, int), f);
217 	if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
218 	return width<1 ? 1 : width;
219 }
220 
221 static int
ocvt_s(FILE * f,va_list * args,int flags,int width,int precision)222 ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
223 {
224 	int i, n = 0;
225 	char *s;
226 
227 	s = va_arg(*args, char *);
228 	if(!s)
229 		s = "";
230 	if(!(flags&LEFT)){
231 		if(precision >= 0)
232 			for(i=0; i!=precision && s[i]; i++);
233 		else
234 			for(i=0; s[i]; i++);
235 		for(; i<width; i++){
236 			putc(' ', f);
237 			n++;
238 		}
239 	}
240 	if(precision >= 0){
241 		for(i=0; i!=precision && *s; i++){
242 			putc(*s++, f);
243 			n++;
244 		}
245 	} else{
246 		for(i=0;*s;i++){
247 			putc(*s++, f);
248 			n++;
249 		}
250 	}
251 	if(flags&LEFT){
252 		for(; i<width; i++){
253 			putc(' ', f);
254 			n++;
255 		}
256 	}
257 	return n;
258 }
259 
260 static int
ocvt_n(FILE * f,va_list * args,int flags,int width,int precision)261 ocvt_n(FILE *f, va_list *args, int flags, int width, int precision)
262 {
263 	USED(f, width, precision);
264 	if(flags&SHORT)
265 		*va_arg(*args, short *) = nprint;
266 	else if(flags&LONG)
267 		*va_arg(*args, long *) = nprint;
268 	else if(flags&VLONG)
269 		*va_arg(*args, long long*) = nprint;
270 	else
271 		*va_arg(*args, int *) = nprint;
272 	return 0;
273 }
274 
275 /*
276  * Generic fixed-point conversion
277  *	f is the output FILE *;
278  *	args is the va_list * from which to get the number;
279  *	flags, width and precision are the results of printf-cracking;
280  *	radix is the number base to print in;
281  *	alphabet is the set of digits to use;
282  *	prefix is the prefix to print before non-zero numbers when
283  *	using ``alternate form.''
284  */
285 static int
ocvt_fixed(FILE * f,va_list * args,int flags,int width,int precision,int radix,int sgned,char alphabet[],char * prefix)286 ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision,
287 	int radix, int sgned, char alphabet[], char *prefix)
288 {
289 	char digits[128];	/* no reasonable machine will ever overflow this */
290 	char *sign;
291 	char *dp;
292 	long long snum;
293 	unsigned long long num;
294 	int nout, npad, nlzero;
295 
296 	if(sgned){
297 		if(flags&PTR) snum = /* (intptr_t) */ (long long)
298 			va_arg(*args, void *);
299 		else if(flags&SHORT) snum = va_arg(*args, short);
300 		else if(flags&LONG) snum = va_arg(*args, long);
301 		else if(flags&VLONG) snum = va_arg(*args, long long);
302 		else snum = va_arg(*args, int);
303 		if(snum < 0){
304 			sign = "-";
305 			num = -snum;
306 		} else{
307 			if(flags&SIGN) sign = "+";
308 			else if(flags&SPACE) sign = " ";
309 			else sign = "";
310 			num = snum;
311 		}
312 	} else {
313 		sign = "";
314 		if(flags&PTR) num = /* (uintptr_t) */ (unsigned long long)
315 			va_arg(*args, void *);
316 		else if(flags&SHORT) num = va_arg(*args, unsigned short);
317 		else if(flags&LONG) num = va_arg(*args, unsigned long);
318 		else if(flags&VLONG) num = va_arg(*args, unsigned long long);
319 		else num = va_arg(*args, unsigned int);
320 	}
321 	if(num == 0) prefix = "";
322 	dp = digits;
323 	do{
324 		*dp++ = alphabet[num%radix];
325 		num /= radix;
326 	}while(num);
327 	if(precision==0 && dp-digits==1 && dp[-1]=='0')
328 		dp--;
329 	nlzero = precision-(dp-digits);
330 	if(nlzero < 0) nlzero = 0;
331 	if(flags&ALT){
332 		if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = "";
333 	}
334 	else prefix = "";
335 	nout = dp-digits+nlzero+strlen(prefix)+strlen(sign);
336 	npad = width-nout;
337 	if(npad < 0) npad = 0;
338 	nout += npad;
339 	if(!(flags&LEFT)){
340 		if(flags&ZPAD && precision <= 0){
341 			fputs(sign, f);
342 			fputs(prefix, f);
343 			while(npad){
344 				putc('0', f);
345 				--npad;
346 			}
347 		} else{
348 			while(npad){
349 				putc(' ', f);
350 				--npad;
351 			}
352 			fputs(sign, f);
353 			fputs(prefix, f);
354 		}
355 		while(nlzero){
356 			putc('0', f);
357 			--nlzero;
358 		}
359 		while(dp!=digits) putc(*--dp, f);
360 	}
361 	else{
362 		fputs(sign, f);
363 		fputs(prefix, f);
364 		while(nlzero){
365 			putc('0', f);
366 			--nlzero;
367 		}
368 		while(dp != digits) putc(*--dp, f);
369 		while(npad){
370 			putc(' ', f);
371 			--npad;
372 		}
373 	}
374 	return nout;
375 }
376 
377 static int
ocvt_X(FILE * f,va_list * args,int flags,int width,int precision)378 ocvt_X(FILE *f, va_list *args, int flags, int width, int precision)
379 {
380 	return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X");
381 }
382 
383 static int
ocvt_d(FILE * f,va_list * args,int flags,int width,int precision)384 ocvt_d(FILE *f, va_list *args, int flags, int width, int precision)
385 {
386 	return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", "");
387 }
388 
389 static int
ocvt_o(FILE * f,va_list * args,int flags,int width,int precision)390 ocvt_o(FILE *f, va_list *args, int flags, int width, int precision)
391 {
392 	return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0");
393 }
394 
395 static int
ocvt_p(FILE * f,va_list * args,int flags,int width,int precision)396 ocvt_p(FILE *f, va_list *args, int flags, int width, int precision)
397 {
398 	return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0,
399 		"0123456789ABCDEF", "0x");
400 }
401 
402 static int
ocvt_u(FILE * f,va_list * args,int flags,int width,int precision)403 ocvt_u(FILE *f, va_list *args, int flags, int width, int precision)
404 {
405 	return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", "");
406 }
407 
408 static int
ocvt_x(FILE * f,va_list * args,int flags,int width,int precision)409 ocvt_x(FILE *f, va_list *args, int flags, int width, int precision)
410 {
411 	return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x");
412 }
413 
414 static int ocvt_flt(FILE *, va_list *, int, int, int, char);
415 
416 static int
ocvt_E(FILE * f,va_list * args,int flags,int width,int precision)417 ocvt_E(FILE *f, va_list *args, int flags, int width, int precision)
418 {
419 	return ocvt_flt(f, args, flags, width, precision, 'E');
420 }
421 
422 static int
ocvt_G(FILE * f,va_list * args,int flags,int width,int precision)423 ocvt_G(FILE *f, va_list *args, int flags, int width, int precision)
424 {
425 	return ocvt_flt(f, args, flags, width, precision, 'G');
426 }
427 
428 static int
ocvt_e(FILE * f,va_list * args,int flags,int width,int precision)429 ocvt_e(FILE *f, va_list *args, int flags, int width, int precision)
430 {
431 	return ocvt_flt(f, args, flags, width, precision, 'e');
432 }
433 
434 static int
ocvt_f(FILE * f,va_list * args,int flags,int width,int precision)435 ocvt_f(FILE *f, va_list *args, int flags, int width, int precision)
436 {
437 	return ocvt_flt(f, args, flags, width, precision, 'f');
438 }
439 
440 static int
ocvt_g(FILE * f,va_list * args,int flags,int width,int precision)441 ocvt_g(FILE *f, va_list *args, int flags, int width, int precision)
442 {
443 	return ocvt_flt(f, args, flags, width, precision, 'g');
444 }
445 
446 static int
ocvt_flt(FILE * f,va_list * args,int flags,int width,int precision,char afmt)447 ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt)
448 {
449 	extern char *_dtoa(double, int, int, int*, int*, char **);
450 	int echr;
451 	char *digits, *edigits;
452 	int exponent;
453 	char fmt;
454 	int sign;
455 	int ndig;
456 	int nout, i;
457 	char ebuf[20];	/* no sensible machine will overflow this */
458 	char *eptr;
459 	double d;
460 
461 	digits = eptr = 0;
462 	echr = 'e';
463 	fmt = afmt;
464 	d = va_arg(*args, double);
465 	if(precision < 0) precision = 6;
466 	switch(fmt){
467 	case 'f':
468 		digits = _dtoa(d, 3, precision, &exponent, &sign, &edigits);
469 		break;
470 	case 'E':
471 		echr = 'E';
472 		fmt = 'e';
473 		/* fall through */
474 	case 'e':
475 		digits = _dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
476 		break;
477 	case 'G':
478 		echr = 'E';
479 		/* fall through */
480 	case 'g':
481 		if (precision > 0)
482 			digits = _dtoa(d, 2, precision, &exponent, &sign, &edigits);
483 		else {
484 			digits = _dtoa(d, 0, precision, &exponent, &sign, &edigits);
485 			precision = edigits - digits;
486 			if (exponent > precision && exponent <= precision + 4)
487 				precision = exponent;
488 			}
489 		if(exponent >= -3 && exponent <= precision){
490 			fmt = 'f';
491 			precision -= exponent;
492 		}else{
493 			fmt = 'e';
494 			--precision;
495 		}
496 		break;
497 	}
498 	if (exponent == 9999) {
499 		/* Infinity or Nan */
500 		precision = 0;
501 		exponent = edigits - digits;
502 		fmt = 'f';
503 	}
504 	ndig = edigits-digits;
505 	if(ndig == 0) {
506 		ndig = 1;
507 		digits = "0";
508 	}
509 	if((afmt=='g' || afmt=='G') && !(flags&ALT)){	/* knock off trailing zeros */
510 		if(fmt == 'f'){
511 			if(precision+exponent > ndig) {
512 				precision = ndig - exponent;
513 				if(precision < 0)
514 					precision = 0;
515 			}
516 		}
517 		else{
518 			if(precision > ndig-1) precision = ndig-1;
519 		}
520 	}
521 	nout = precision;				/* digits after decimal point */
522 	if(precision!=0 || flags&ALT) nout++;		/* decimal point */
523 	if(fmt=='f' && exponent>0) nout += exponent;	/* digits before decimal point */
524 	else nout++;					/* there's always at least one */
525 	if(sign || flags&(SPACE|SIGN)) nout++;		/* sign */
526 	if(fmt != 'f'){					/* exponent */
527 		eptr = ebuf;
528 		for(i=exponent<=0?1-exponent:exponent-1; i; i/=10)
529 			*eptr++ = '0' + i%10;
530 		while(eptr<ebuf+2) *eptr++ = '0';
531 		nout += eptr-ebuf+2;			/* e+99 */
532 	}
533 	if(!(flags&ZPAD) && !(flags&LEFT))
534 		while(nout < width){
535 			putc(' ', f);
536 			nout++;
537 		}
538 	if(sign) putc('-', f);
539 	else if(flags&SIGN) putc('+', f);
540 	else if(flags&SPACE) putc(' ', f);
541 	if((flags&ZPAD) && !(flags&LEFT))
542 		while(nout < width){
543 			putc('0', f);
544 			nout++;
545 		}
546 	if(fmt == 'f'){
547 		for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
548 		if(i == 0) putc('0', f);
549 		if(precision>0 || flags&ALT) putc('.', f);
550 		for(i=0; i!=precision; i++)
551 			putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
552 	}
553 	else{
554 		putc(digits[0], f);
555 		if(precision>0 || flags&ALT) putc('.', f);
556 		for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
557 	}
558 	if(fmt != 'f'){
559 		putc(echr, f);
560 		putc(exponent<=0?'-':'+', f);
561 		while(eptr>ebuf) putc(*--eptr, f);
562 	}
563 	while(nout < width){
564 		putc(' ', f);
565 		nout++;
566 	}
567 	return nout;
568 }
569