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 extern char *_dtoa(double, int, int, int*, int*, char **); 438 int echr; 439 char *digits, *edigits; 440 int exponent; 441 char fmt; 442 int sign; 443 int ndig; 444 int nout, i; 445 char ebuf[20]; /* no sensible machine will overflow this */ 446 char *eptr; 447 double d; 448 449 echr = 'e'; 450 fmt = afmt; 451 d = va_arg(*args, double); 452 if(precision < 0) precision = 6; 453 switch(fmt){ 454 case 'f': 455 digits = _dtoa(d, 3, precision, &exponent, &sign, &edigits); 456 break; 457 case 'E': 458 echr = 'E'; 459 fmt = 'e'; 460 /* fall through */ 461 case 'e': 462 digits = _dtoa(d, 2, 1+precision, &exponent, &sign, &edigits); 463 break; 464 case 'G': 465 echr = 'E'; 466 /* fall through */ 467 case 'g': 468 if (precision > 0) 469 digits = _dtoa(d, 2, precision, &exponent, &sign, &edigits); 470 else { 471 digits = _dtoa(d, 0, precision, &exponent, &sign, &edigits); 472 precision = edigits - digits; 473 if (exponent > precision && exponent <= precision + 4) 474 precision = exponent; 475 } 476 if(exponent >= -3 && exponent <= precision){ 477 fmt = 'f'; 478 precision -= exponent; 479 }else{ 480 fmt = 'e'; 481 --precision; 482 } 483 break; 484 } 485 if (exponent == 9999) { 486 /* Infinity or Nan */ 487 precision = 0; 488 exponent = edigits - digits; 489 fmt = 'f'; 490 } 491 ndig = edigits-digits; 492 if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */ 493 if(fmt == 'f'){ 494 if(precision+exponent > ndig) { 495 precision = ndig - exponent; 496 if(precision < 0) 497 precision = 0; 498 } 499 } 500 else{ 501 if(precision > ndig-1) precision = ndig-1; 502 } 503 } 504 nout = precision; /* digits after decimal point */ 505 if(precision!=0 || flags&ALT) nout++; /* decimal point */ 506 if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */ 507 else nout++; /* there's always at least one */ 508 if(sign || flags&(SPACE|SIGN)) nout++; /* sign */ 509 if(fmt != 'f'){ /* exponent */ 510 eptr = ebuf; 511 for(i=exponent<=0?1-exponent:exponent-1; i; i/=10) 512 *eptr++ = '0' + i%10; 513 while(eptr<ebuf+2) *eptr++ = '0'; 514 nout += eptr-ebuf+2; /* e+99 */ 515 } 516 if(!(flags&ZPAD) && !(flags&LEFT)) 517 while(nout < width){ 518 putc(' ', f); 519 nout++; 520 } 521 if(sign) putc('-', f); 522 else if(flags&SIGN) putc('+', f); 523 else if(flags&SPACE) putc(' ', f); 524 if(flags&ZPAD) 525 while(nout < width){ 526 putc('0', f); 527 nout++; 528 } 529 if(fmt == 'f'){ 530 for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f); 531 if(i == 0) putc('0', f); 532 if(precision>0 || flags&ALT) putc('.', f); 533 for(i=0; i!=precision; i++) 534 putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f); 535 } 536 else{ 537 putc(digits[0], f); 538 if(precision>0 || flags&ALT) putc('.', f); 539 for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f); 540 } 541 if(fmt != 'f'){ 542 putc(echr, f); 543 putc(exponent<=0?'-':'+', f); 544 while(eptr>ebuf) putc(*--eptr, f); 545 } 546 while(nout < width){ 547 putc(' ', f); 548 nout++; 549 } 550 return nout; 551 } 552