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