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