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