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 int 147 vfprintf(FILE *f, const char *s, va_list args) 148 { 149 int flags, width, precision; 150 151 nprint = 0; 152 while(*s){ 153 if(*s != '%'){ 154 putc(*s++, f); 155 nprint++; 156 continue; 157 } 158 s++; 159 flags = 0; 160 while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK]; 161 if(*s == '*'){ 162 width = va_arg(args, int); 163 s++; 164 if(width<0){ 165 flags |= LEFT; 166 width = -width; 167 } 168 } 169 else{ 170 width = 0; 171 while('0'<=*s && *s<='9') width = width*10 + *s++ - '0'; 172 } 173 if(*s == '.'){ 174 s++; 175 if(*s == '*'){ 176 precision = va_arg(args, int); 177 s++; 178 } 179 else{ 180 precision = 0; 181 while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0'; 182 } 183 } 184 else 185 precision = -1; 186 while(tflag[*s&_IO_CHMASK]) flags |= tflag[*s++&_IO_CHMASK]; 187 if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision); 188 else if(*s){ 189 putc(*s++, f); 190 nprint++; 191 } 192 } 193 return ferror(f)? -1: nprint;; 194 } 195 196 static int 197 ocvt_c(FILE *f, va_list *args, int flags, int width, int precision) 198 { 199 #pragma ref precision 200 int i; 201 202 if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f); 203 putc((unsigned char)va_arg(*args, int), f); 204 if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f); 205 return width<1 ? 1 : width; 206 } 207 208 static int 209 ocvt_s(FILE *f, va_list *args, int flags, int width, int precision) 210 { 211 int i, n = 0; 212 char *s; 213 214 s = va_arg(*args, char *); 215 if(!(flags&LEFT)){ 216 if(precision >= 0) 217 for(i=0; i!=precision && s[i]; i++); 218 else 219 for(i=0; s[i]; i++); 220 for(; i<width; i++){ 221 putc(' ', f); 222 n++; 223 } 224 } 225 if(precision >= 0){ 226 for(i=0; i!=precision && *s; i++){ 227 putc(*s++, f); 228 n++; 229 } 230 } else{ 231 for(i=0;*s;i++){ 232 putc(*s++, f); 233 n++; 234 } 235 } 236 if(flags&LEFT){ 237 for(; i<width; i++){ 238 putc(' ', f); 239 n++; 240 } 241 } 242 return n; 243 } 244 245 static int 246 ocvt_n(FILE *f, va_list *args, int flags, int width, int precision) 247 { 248 #pragma ref f 249 #pragma ref width 250 #pragma ref precision 251 if(flags&SHORT) 252 *va_arg(*args, short *) = nprint; 253 else if(flags&LONG) 254 *va_arg(*args, long *) = nprint; 255 else 256 *va_arg(*args, int *) = nprint; 257 return 0; 258 } 259 260 /* 261 * Generic fixed-point conversion 262 * f is the output FILE *; 263 * args is the va_list * from which to get the number; 264 * flags, width and precision are the results of printf-cracking; 265 * radix is the number base to print in; 266 * alphabet is the set of digits to use; 267 * prefix is the prefix to print before non-zero numbers when 268 * using ``alternate form.'' 269 */ 270 static int 271 ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision, 272 int radix, int sgned, char alphabet[], char *prefix) 273 { 274 char digits[128]; /* no reasonable machine will ever overflow this */ 275 char *sign; 276 char *dp; 277 long snum; 278 unsigned long num; 279 int nout, npad, nlzero; 280 281 if(sgned){ 282 if(flags&PTR) snum = (long)va_arg(*args, void *); 283 else if(flags&SHORT) snum = va_arg(*args, short); 284 else if(flags&LONG) snum = va_arg(*args, long); 285 else snum = va_arg(*args, int); 286 if(snum < 0){ 287 sign = "-"; 288 num = -snum; 289 } else{ 290 if(flags&SIGN) sign = "+"; 291 else if(flags&SPACE) sign = " "; 292 else sign = ""; 293 num = snum; 294 } 295 } else { 296 sign = ""; 297 if(flags&PTR) num = (long)va_arg(*args, void *); 298 else if(flags&SHORT) num = va_arg(*args, unsigned short); 299 else if(flags&LONG) num = va_arg(*args, unsigned long); 300 else num = va_arg(*args, unsigned int); 301 } 302 if(num == 0) prefix = ""; 303 dp = digits; 304 do{ 305 *dp++ = alphabet[num%radix]; 306 num /= radix; 307 }while(num); 308 if(precision==0 && dp-digits==1 && dp[-1]=='0') 309 dp--; 310 nlzero = precision-(dp-digits); 311 if(nlzero < 0) nlzero = 0; 312 if(flags&ALT){ 313 if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = ""; 314 } 315 else prefix = ""; 316 nout = dp-digits+nlzero+strlen(prefix)+strlen(sign); 317 npad = width-nout; 318 if(npad < 0) npad = 0; 319 nout += npad; 320 if(!(flags&LEFT)){ 321 if(flags&ZPAD && precision <= 0){ 322 fputs(sign, f); 323 fputs(prefix, f); 324 while(npad){ 325 putc('0', f); 326 --npad; 327 } 328 } else{ 329 while(npad){ 330 putc(' ', f); 331 --npad; 332 } 333 fputs(sign, f); 334 fputs(prefix, f); 335 } 336 while(nlzero){ 337 putc('0', f); 338 --nlzero; 339 } 340 while(dp!=digits) putc(*--dp, f); 341 } 342 else{ 343 fputs(sign, f); 344 fputs(prefix, f); 345 while(nlzero){ 346 putc('0', f); 347 --nlzero; 348 } 349 while(dp != digits) putc(*--dp, f); 350 while(npad){ 351 putc(' ', f); 352 --npad; 353 } 354 } 355 return nout; 356 } 357 358 static int 359 ocvt_X(FILE *f, va_list *args, int flags, int width, int precision) 360 { 361 return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X"); 362 } 363 364 static int 365 ocvt_d(FILE *f, va_list *args, int flags, int width, int precision) 366 { 367 return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", ""); 368 } 369 370 static int 371 ocvt_o(FILE *f, va_list *args, int flags, int width, int precision) 372 { 373 return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0"); 374 } 375 376 static int 377 ocvt_p(FILE *f, va_list *args, int flags, int width, int precision) 378 { 379 return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0, 380 "0123456789ABCDEF", "0X"); 381 } 382 383 static int 384 ocvt_u(FILE *f, va_list *args, int flags, int width, int precision) 385 { 386 return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", ""); 387 } 388 389 static int 390 ocvt_x(FILE *f, va_list *args, int flags, int width, int precision) 391 { 392 return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x"); 393 } 394 395 static int ocvt_flt(FILE *, va_list *, int, int, int, char); 396 397 static int 398 ocvt_E(FILE *f, va_list *args, int flags, int width, int precision) 399 { 400 return ocvt_flt(f, args, flags, width, precision, 'E'); 401 } 402 403 static int 404 ocvt_G(FILE *f, va_list *args, int flags, int width, int precision) 405 { 406 return ocvt_flt(f, args, flags, width, precision, 'G'); 407 } 408 409 static int 410 ocvt_e(FILE *f, va_list *args, int flags, int width, int precision) 411 { 412 return ocvt_flt(f, args, flags, width, precision, 'e'); 413 } 414 415 static int 416 ocvt_f(FILE *f, va_list *args, int flags, int width, int precision) 417 { 418 return ocvt_flt(f, args, flags, width, precision, 'f'); 419 } 420 421 static int 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 428 ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt) 429 { 430 extern char *_dtoa(double, int, int, int*, int*, char **); 431 int echr; 432 char *digits, *edigits; 433 int exponent; 434 char fmt; 435 int sign; 436 int ndig; 437 int nout, i; 438 char ebuf[20]; /* no sensible machine will overflow this */ 439 char *eptr; 440 double d; 441 442 echr = 'e'; 443 fmt = afmt; 444 d = va_arg(*args, double); 445 if(precision < 0) precision = 6; 446 switch(fmt){ 447 case 'f': 448 digits = _dtoa(d, 3, precision, &exponent, &sign, &edigits); 449 break; 450 case 'E': 451 echr = 'E'; 452 fmt = 'e'; 453 /* fall through */ 454 case 'e': 455 digits = _dtoa(d, 2, 1+precision, &exponent, &sign, &edigits); 456 break; 457 case 'G': 458 echr = 'E'; 459 /* fall through */ 460 case 'g': 461 if (precision > 0) 462 digits = _dtoa(d, 2, precision, &exponent, &sign, &edigits); 463 else { 464 digits = _dtoa(d, 0, precision, &exponent, &sign, &edigits); 465 precision = edigits - digits; 466 if (exponent > precision && exponent <= precision + 4) 467 precision = exponent; 468 } 469 if(exponent >= -3 && exponent <= precision){ 470 fmt = 'f'; 471 precision -= exponent; 472 }else{ 473 fmt = 'e'; 474 --precision; 475 } 476 break; 477 } 478 if (exponent == 9999) { 479 /* Infinity or Nan */ 480 precision = 0; 481 exponent = edigits - digits; 482 fmt = 'f'; 483 } 484 ndig = edigits-digits; 485 if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */ 486 if(fmt == 'f'){ 487 if(precision+exponent > ndig) { 488 precision = ndig - exponent; 489 if(precision < 0) 490 precision = 0; 491 } 492 } 493 else{ 494 if(precision > ndig-1) precision = ndig-1; 495 } 496 } 497 nout = precision; /* digits after decimal point */ 498 if(precision!=0 || flags&ALT) nout++; /* decimal point */ 499 if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */ 500 else nout++; /* there's always at least one */ 501 if(sign || flags&(SPACE|SIGN)) nout++; /* sign */ 502 if(fmt != 'f'){ /* exponent */ 503 eptr = ebuf; 504 for(i=exponent<=0?1-exponent:exponent-1; i; i/=10) 505 *eptr++ = '0' + i%10; 506 while(eptr<ebuf+2) *eptr++ = '0'; 507 nout += eptr-ebuf+2; /* e+99 */ 508 } 509 if(!(flags&ZPAD) && !(flags&LEFT)) 510 while(nout < width){ 511 putc(' ', f); 512 nout++; 513 } 514 if(sign) putc('-', f); 515 else if(flags&SIGN) putc('+', f); 516 else if(flags&SPACE) putc(' ', f); 517 if(flags&ZPAD) 518 while(nout < width){ 519 putc('0', f); 520 nout++; 521 } 522 if(fmt == 'f'){ 523 for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f); 524 if(i == 0) putc('0', f); 525 if(precision>0 || flags&ALT) putc('.', f); 526 for(i=0; i!=precision; i++) 527 putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f); 528 } 529 else{ 530 putc(digits[0], f); 531 if(precision>0 || flags&ALT) putc('.', f); 532 for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f); 533 } 534 if(fmt != 'f'){ 535 putc(echr, f); 536 putc(exponent<=0?'-':'+', f); 537 while(eptr>ebuf) putc(*--eptr, f); 538 } 539 while(nout < width){ 540 putc(' ', f); 541 nout++; 542 } 543 return nout; 544 } 545