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