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