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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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