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