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 * s,va_list args)149 vfprintf(FILE *f, const char *s, va_list args)
150 {
151 int flags, width, precision;
152
153 qlock(&_stdiolk);
154
155 nprint = 0;
156 while(*s){
157 if(*s != '%'){
158 putc(*s++, f);
159 nprint++;
160 continue;
161 }
162 s++;
163 flags = 0;
164 while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK];
165 if(*s == '*'){
166 width = va_arg(args, int);
167 s++;
168 if(width<0){
169 flags |= LEFT;
170 width = -width;
171 }
172 }
173 else{
174 width = 0;
175 while('0'<=*s && *s<='9') width = width*10 + *s++ - '0';
176 }
177 if(*s == '.'){
178 s++;
179 if(*s == '*'){
180 precision = va_arg(args, int);
181 s++;
182 }
183 else{
184 precision = 0;
185 while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0';
186 }
187 }
188 else
189 precision = -1;
190 while(tflag[*s&_IO_CHMASK]) flags |= tflag[*s++&_IO_CHMASK];
191 if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision);
192 else if(*s){
193 putc(*s++, f);
194 nprint++;
195 }
196 }
197
198 qunlock(&_stdiolk);
199
200 if(ferror(f)){
201 if((f->flags&STRING) && f->wp==f->rp && f->wp>f->buf){
202 *(f->wp-1) = '\0';
203 return nprint;
204 }
205 return -1;
206 }
207 return nprint;
208 }
209
210 static int
ocvt_c(FILE * f,va_list * args,int flags,int width,int precision)211 ocvt_c(FILE *f, va_list *args, int flags, int width, int precision)
212 {
213 #pragma ref precision
214 int i;
215
216 if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f);
217 putc((unsigned char)va_arg(*args, int), f);
218 if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
219 return width<1 ? 1 : width;
220 }
221
222 static int
ocvt_s(FILE * f,va_list * args,int flags,int width,int precision)223 ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
224 {
225 int i, n = 0;
226 char *s;
227
228 s = va_arg(*args, char *);
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
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 = (long)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 = (long)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 switch(fmt){
460 case 'f':
461 digits = dtoa(d, 3, precision, &exponent, &sign, &edigits);
462 break;
463 case 'E':
464 echr = 'E';
465 fmt = 'e';
466 /* fall through */
467 case 'e':
468 digits = dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
469 break;
470 case 'G':
471 echr = 'E';
472 /* fall through */
473 case 'g':
474 if (precision > 0)
475 digits = dtoa(d, 2, precision, &exponent, &sign, &edigits);
476 else {
477 digits = dtoa(d, 0, precision, &exponent, &sign, &edigits);
478 precision = edigits - digits;
479 if (exponent > precision && exponent <= precision + 4)
480 precision = exponent;
481 }
482 if(exponent >= -3 && exponent <= precision){
483 fmt = 'f';
484 precision -= exponent;
485 }else{
486 fmt = 'e';
487 --precision;
488 }
489 break;
490 }
491 if (exponent == 9999) {
492 /* Infinity or Nan */
493 precision = 0;
494 exponent = edigits - digits;
495 fmt = 'f';
496 }
497 ndig = edigits-digits;
498 if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */
499 if(fmt == 'f'){
500 if(precision+exponent > ndig) {
501 precision = ndig - exponent;
502 if(precision < 0)
503 precision = 0;
504 }
505 }
506 else{
507 if(precision > ndig-1) precision = ndig-1;
508 }
509 }
510 nout = precision; /* digits after decimal point */
511 if(precision!=0 || flags&ALT) nout++; /* decimal point */
512 if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */
513 else nout++; /* there's always at least one */
514 if(sign || flags&(SPACE|SIGN)) nout++; /* sign */
515 if(fmt != 'f'){ /* exponent */
516 eptr = ebuf;
517 for(i=exponent<=0?1-exponent:exponent-1; i; i/=10)
518 *eptr++ = '0' + i%10;
519 while(eptr<ebuf+2) *eptr++ = '0';
520 nout += eptr-ebuf+2; /* e+99 */
521 }
522 if(!(flags&ZPAD) && !(flags&LEFT))
523 while(nout < width){
524 putc(' ', f);
525 nout++;
526 }
527 if(sign) putc('-', f);
528 else if(flags&SIGN) putc('+', f);
529 else if(flags&SPACE) putc(' ', f);
530 if(flags&ZPAD)
531 while(nout < width){
532 putc('0', f);
533 nout++;
534 }
535 if(fmt == 'f'){
536 for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
537 if(i == 0) putc('0', f);
538 if(precision>0 || flags&ALT) putc('.', f);
539 for(i=0; i!=precision; i++)
540 putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
541 }
542 else{
543 putc(digits[0], f);
544 if(precision>0 || flags&ALT) putc('.', f);
545 for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
546 }
547 if(fmt != 'f'){
548 putc(echr, f);
549 putc(exponent<=0?'-':'+', f);
550 while(eptr>ebuf) putc(*--eptr, f);
551 }
552 while(nout < width){
553 putc(' ', f);
554 nout++;
555 }
556 freedtoa(digits);
557 return nout;
558 }
559