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