1 /* 2 * COPYRIGHT: GNU GPL, see COPYING in the top level directory 3 * PROJECT: ReactOS crt library 4 * FILE: lib/sdk/crt/printf/streamout.c 5 * PURPOSE: Implementation of streamout 6 * PROGRAMMERS: Timo Kreuzer 7 * Katayama Hirofumi MZ 8 */ 9 10 #include <stdio.h> 11 #include <stdarg.h> 12 #include <tchar.h> 13 #include <strings.h> 14 #include <math.h> 15 #include <float.h> 16 17 #ifdef _UNICODE 18 # define streamout wstreamout 19 # define format_float format_floatw 20 #endif 21 22 #define MB_CUR_MAX 10 23 #define BUFFER_SIZE (32 + 17) 24 25 int mbtowc(wchar_t *wchar, const char *mbchar, size_t count); 26 int wctomb(char *mbchar, wchar_t wchar); 27 28 typedef struct _STRING 29 { 30 unsigned short Length; 31 unsigned short MaximumLength; 32 void *Buffer; 33 } STRING; 34 35 enum 36 { 37 /* Formatting flags */ 38 FLAG_ALIGN_LEFT = 0x01, 39 FLAG_FORCE_SIGN = 0x02, 40 FLAG_FORCE_SIGNSP = 0x04, 41 FLAG_PAD_ZERO = 0x08, 42 FLAG_SPECIAL = 0x10, 43 44 /* Data format flags */ 45 FLAG_SHORT = 0x100, 46 FLAG_LONG = 0x200, 47 FLAG_WIDECHAR = FLAG_LONG, 48 FLAG_INT64 = 0x400, 49 #ifdef _WIN64 50 FLAG_INTPTR = FLAG_INT64, 51 #else 52 FLAG_INTPTR = 0, 53 #endif 54 FLAG_LONGDOUBLE = 0x800, 55 }; 56 57 #define va_arg_f(argptr, flags) \ 58 (flags & FLAG_INT64) ? va_arg(argptr, __int64) : \ 59 (flags & FLAG_SHORT) ? (short)va_arg(argptr, int) : \ 60 va_arg(argptr, int) 61 62 #define va_arg_fu(argptr, flags) \ 63 (flags & FLAG_INT64) ? va_arg(argptr, unsigned __int64) : \ 64 (flags & FLAG_SHORT) ? (unsigned short)va_arg(argptr, int) : \ 65 va_arg(argptr, unsigned int) 66 67 #define va_arg_ffp(argptr, flags) \ 68 (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \ 69 va_arg(argptr, double) 70 71 #define get_exp(f) (int)floor(f == 0 ? 0 : (f >= 0 ? log10(f) : log10(-f))) 72 #define round(x) floor((x) + 0.5) 73 74 #ifndef _USER32_WSPRINTF 75 76 void 77 #ifdef _LIBCNT_ 78 /* Due to restrictions in kernel mode regarding the use of floating point, 79 we prevent it from being inlined */ 80 __declspec(noinline) 81 #endif 82 format_float( 83 TCHAR chr, 84 unsigned int flags, 85 int precision, 86 TCHAR **string, 87 const TCHAR **prefix, 88 va_list *argptr) 89 { 90 static const TCHAR digits_l[] = _T("0123456789abcdef0x"); 91 static const TCHAR digits_u[] = _T("0123456789ABCDEF0X"); 92 static const TCHAR _nan[] = _T("#QNAN"); 93 static const TCHAR _infinity[] = _T("#INF"); 94 const TCHAR *digits = digits_l; 95 int exponent = 0, sign; 96 long double fpval, fpval2; 97 int padding = 0, num_digits, val32, base = 10; 98 99 /* Normalize the precision */ 100 if (precision < 0) precision = 6; 101 else if (precision > 17) 102 { 103 padding = precision - 17; 104 precision = 17; 105 } 106 107 /* Get the float value and calculate the exponent */ 108 fpval = va_arg_ffp(*argptr, flags); 109 exponent = get_exp(fpval); 110 sign = fpval < 0 ? -1 : 1; 111 112 switch (chr) 113 { 114 case _T('G'): 115 digits = digits_u; 116 case _T('g'): 117 if (precision > 0) precision--; 118 if (exponent < -4 || exponent >= precision) goto case_e; 119 120 /* Shift the decimal point and round */ 121 fpval2 = round(sign * fpval * pow(10., precision)); 122 123 /* Skip trailing zeroes */ 124 while (precision && (unsigned __int64)fpval2 % 10 == 0) 125 { 126 precision--; 127 fpval2 /= 10; 128 } 129 break; 130 131 case _T('E'): 132 digits = digits_u; 133 case _T('e'): 134 case_e: 135 /* Shift the decimal point and round */ 136 fpval2 = round(sign * fpval * pow(10., precision - exponent)); 137 138 /* Compensate for changed exponent through rounding */ 139 if (fpval2 >= (unsigned __int64)pow(10., precision + 1)) 140 { 141 exponent++; 142 fpval2 = round(sign * fpval * pow(10., precision - exponent)); 143 } 144 145 val32 = exponent >= 0 ? exponent : -exponent; 146 147 // FIXME: handle length of exponent field: 148 // http://msdn.microsoft.com/de-de/library/0fatw238%28VS.80%29.aspx 149 num_digits = 3; 150 while (num_digits--) 151 { 152 *--(*string) = digits[val32 % 10]; 153 val32 /= 10; 154 } 155 156 /* Sign for the exponent */ 157 *--(*string) = exponent >= 0 ? _T('+') : _T('-'); 158 159 /* Add 'e' or 'E' separator */ 160 *--(*string) = digits[0xe]; 161 break; 162 163 case _T('A'): 164 digits = digits_u; 165 case _T('a'): 166 // base = 16; 167 // FIXME: TODO 168 169 case _T('f'): 170 default: 171 /* Shift the decimal point and round */ 172 fpval2 = round(sign * fpval * pow(10., precision)); 173 break; 174 } 175 176 /* Handle sign */ 177 if (fpval < 0) 178 { 179 *prefix = _T("-"); 180 } 181 else if (flags & FLAG_FORCE_SIGN) 182 *prefix = _T("+"); 183 else if (flags & FLAG_FORCE_SIGNSP) 184 *prefix = _T(" "); 185 186 /* Handle special cases first */ 187 if (_isnan(fpval)) 188 { 189 (*string) -= sizeof(_nan) / sizeof(TCHAR) - 1; 190 _tcscpy((*string), _nan); 191 fpval2 = 1; 192 } 193 else if (!_finite(fpval)) 194 { 195 (*string) -= sizeof(_infinity) / sizeof(TCHAR) - 1; 196 _tcscpy((*string), _infinity); 197 fpval2 = 1; 198 } 199 else 200 { 201 /* Zero padding */ 202 while (padding-- > 0) *--(*string) = _T('0'); 203 204 /* Digits after the decimal point */ 205 num_digits = precision; 206 while (num_digits-- > 0) 207 { 208 *--(*string) = digits[(unsigned __int64)fpval2 % 10]; 209 fpval2 /= base; 210 } 211 } 212 213 if (precision > 0 || flags & FLAG_SPECIAL) 214 *--(*string) = _T('.'); 215 216 /* Digits before the decimal point */ 217 do 218 { 219 *--(*string) = digits[(unsigned __int64)fpval2 % base]; 220 fpval2 /= base; 221 } 222 while ((unsigned __int64)fpval2); 223 224 } 225 #endif 226 227 static 228 int 229 streamout_char(FILE *stream, int chr) 230 { 231 #if !defined(_USER32_WSPRINTF) 232 if ((stream->_flag & _IOSTRG) && (stream->_base == NULL)) 233 return 1; 234 #endif 235 #if defined(_USER32_WSPRINTF) || defined(_LIBCNT_) 236 /* Check if the buffer is full */ 237 if (stream->_cnt < sizeof(TCHAR)) 238 return 0; 239 240 *(TCHAR*)stream->_ptr = chr; 241 stream->_ptr += sizeof(TCHAR); 242 stream->_cnt -= sizeof(TCHAR); 243 244 return 1; 245 #else 246 return _fputtc((TCHAR)chr, stream) != _TEOF; 247 #endif 248 } 249 250 static 251 int 252 streamout_astring(FILE *stream, const char *string, size_t count) 253 { 254 TCHAR chr; 255 int written = 0; 256 257 #if !defined(_USER32_WSPRINTF) 258 if ((stream->_flag & _IOSTRG) && (stream->_base == NULL)) 259 return count; 260 #endif 261 262 while (count--) 263 { 264 #ifdef _UNICODE 265 int len; 266 if ((len = mbtowc(&chr, string, MB_CUR_MAX)) < 1) break; 267 string += len; 268 #else 269 chr = *string++; 270 #endif 271 if (streamout_char(stream, chr) == 0) return -1; 272 written++; 273 } 274 275 return written; 276 } 277 278 static 279 int 280 streamout_wstring(FILE *stream, const wchar_t *string, size_t count) 281 { 282 wchar_t chr; 283 int written = 0; 284 285 #if defined(_UNICODE) && !defined(_USER32_WSPRINTF) 286 if ((stream->_flag & _IOSTRG) && (stream->_base == NULL)) 287 return count; 288 #endif 289 290 while (count--) 291 { 292 #ifndef _UNICODE 293 char mbchar[MB_CUR_MAX], *ptr = mbchar; 294 int mblen; 295 296 mblen = wctomb(mbchar, *string++); 297 if (mblen <= 0) return written; 298 299 while (chr = *ptr++, mblen--) 300 #else 301 chr = *string++; 302 #endif 303 { 304 if (streamout_char(stream, chr) == 0) return -1; 305 written++; 306 } 307 } 308 309 return written; 310 } 311 312 #ifdef _UNICODE 313 #define streamout_string streamout_wstring 314 #else 315 #define streamout_string streamout_astring 316 #endif 317 318 #ifdef _USER32_WSPRINTF 319 # define USE_MULTISIZE 0 320 #else 321 # define USE_MULTISIZE 1 322 #endif 323 324 int 325 __cdecl 326 streamout(FILE *stream, const TCHAR *format, va_list argptr) 327 { 328 static const TCHAR digits_l[] = _T("0123456789abcdef0x"); 329 static const TCHAR digits_u[] = _T("0123456789ABCDEF0X"); 330 static const char *_nullstring = "(null)"; 331 TCHAR buffer[BUFFER_SIZE + 1]; 332 TCHAR chr, *string; 333 STRING *nt_string; 334 const TCHAR *digits, *prefix; 335 int base, fieldwidth, precision, padding; 336 size_t prefixlen, len; 337 int written = 1, written_all = 0; 338 unsigned int flags; 339 unsigned __int64 val64; 340 341 buffer[BUFFER_SIZE] = '\0'; 342 343 while (written >= 0) 344 { 345 chr = *format++; 346 347 /* Check for end of format string */ 348 if (chr == _T('\0')) break; 349 350 /* Check for 'normal' character or double % */ 351 if ((chr != _T('%')) || 352 (chr = *format++) == _T('%')) 353 { 354 /* Write the character to the stream */ 355 if ((written = streamout_char(stream, chr)) == 0) return -1; 356 written_all += written; 357 continue; 358 } 359 360 /* Handle flags */ 361 flags = 0; 362 while (1) 363 { 364 if (chr == _T('-')) flags |= FLAG_ALIGN_LEFT; 365 else if (chr == _T('+')) flags |= FLAG_FORCE_SIGN; 366 else if (chr == _T(' ')) flags |= FLAG_FORCE_SIGNSP; 367 else if (chr == _T('0')) flags |= FLAG_PAD_ZERO; 368 else if (chr == _T('#')) flags |= FLAG_SPECIAL; 369 else break; 370 chr = *format++; 371 } 372 373 /* Handle field width modifier */ 374 if (chr == _T('*')) 375 { 376 #ifdef _USER32_WSPRINTF 377 if ((written = streamout_char(stream, chr)) == 0) return -1; 378 written_all += written; 379 continue; 380 #else 381 fieldwidth = va_arg(argptr, int); 382 if (fieldwidth < 0) 383 { 384 flags |= FLAG_ALIGN_LEFT; 385 fieldwidth = -fieldwidth; 386 } 387 chr = *format++; 388 #endif 389 } 390 else 391 { 392 fieldwidth = 0; 393 while (chr >= _T('0') && chr <= _T('9')) 394 { 395 fieldwidth = fieldwidth * 10 + (chr - _T('0')); 396 chr = *format++; 397 } 398 } 399 400 /* Handle precision modifier */ 401 if (chr == '.') 402 { 403 chr = *format++; 404 405 if (chr == _T('*')) 406 { 407 #ifdef _USER32_WSPRINTF 408 if ((written = streamout_char(stream, chr)) == 0) return -1; 409 written_all += written; 410 continue; 411 #else 412 precision = va_arg(argptr, int); 413 chr = *format++; 414 #endif 415 } 416 else 417 { 418 precision = 0; 419 while (chr >= _T('0') && chr <= _T('9')) 420 { 421 precision = precision * 10 + (chr - _T('0')); 422 chr = *format++; 423 } 424 } 425 } 426 else precision = -1; 427 428 /* Handle argument size prefix */ 429 do 430 { 431 if (chr == _T('h')) flags |= FLAG_SHORT; 432 else if (chr == _T('w')) flags |= FLAG_WIDECHAR; 433 else if (chr == _T('L')) flags |= 0; // FIXME: long double 434 else if (chr == _T('F')) flags |= 0; // FIXME: what is that? 435 else if (chr == _T('z') && *format && strchr("udxXion", *format)) 436 { 437 flags |= FLAG_INTPTR; 438 } 439 else if (chr == _T('l')) 440 { 441 /* Check if this is the 2nd 'l' in a row */ 442 if (format[-2] == 'l') flags |= FLAG_INT64; 443 else flags |= FLAG_LONG; 444 } 445 else if (chr == _T('I')) 446 { 447 if (format[0] == _T('3') && format[1] == _T('2')) 448 { 449 format += 2; 450 } 451 else if (format[0] == _T('6') && format[1] == _T('4')) 452 { 453 format += 2; 454 flags |= FLAG_INT64; 455 } 456 else if (format[0] == _T('x') || format[0] == _T('X') || 457 format[0] == _T('d') || format[0] == _T('i') || 458 format[0] == _T('u') || format[0] == _T('o')) 459 { 460 flags |= FLAG_INTPTR; 461 } 462 else break; 463 } 464 else break; 465 chr = *format++; 466 } 467 while (USE_MULTISIZE); 468 469 /* Handle the format specifier */ 470 digits = digits_l; 471 string = &buffer[BUFFER_SIZE]; 472 base = 10; 473 prefix = 0; 474 switch (chr) 475 { 476 case _T('n'): 477 if (flags & FLAG_INT64) 478 *va_arg(argptr, __int64*) = written_all; 479 else if (flags & FLAG_SHORT) 480 *va_arg(argptr, short*) = written_all; 481 else 482 *va_arg(argptr, int*) = written_all; 483 continue; 484 485 case _T('C'): 486 #ifndef _UNICODE 487 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 488 #endif 489 goto case_char; 490 491 case _T('c'): 492 #ifdef _UNICODE 493 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 494 #endif 495 case_char: 496 string = buffer; 497 len = 1; 498 if (flags & FLAG_WIDECHAR) 499 { 500 ((wchar_t*)string)[0] = va_arg(argptr, int); 501 ((wchar_t*)string)[1] = _T('\0'); 502 } 503 else 504 { 505 ((char*)string)[0] = va_arg(argptr, int); 506 ((char*)string)[1] = _T('\0'); 507 } 508 break; 509 510 case _T('Z'): 511 nt_string = va_arg(argptr, void*); 512 if (nt_string && (string = nt_string->Buffer)) 513 { 514 len = nt_string->Length; 515 if (flags & FLAG_WIDECHAR) len /= sizeof(wchar_t); 516 break; 517 } 518 string = 0; 519 goto case_string; 520 521 case _T('S'): 522 string = va_arg(argptr, void*); 523 #ifndef _UNICODE 524 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 525 #endif 526 goto case_string; 527 528 case _T('s'): 529 string = va_arg(argptr, void*); 530 #ifdef _UNICODE 531 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 532 #endif 533 534 case_string: 535 if (!string) 536 { 537 string = (TCHAR*)_nullstring; 538 flags &= ~FLAG_WIDECHAR; 539 } 540 541 if (flags & FLAG_WIDECHAR) 542 len = wcsnlen((wchar_t*)string, (unsigned)precision); 543 else 544 len = strnlen((char*)string, (unsigned)precision); 545 precision = 0; 546 break; 547 548 #ifndef _USER32_WSPRINTF 549 case _T('G'): 550 case _T('E'): 551 case _T('A'): 552 case _T('g'): 553 case _T('e'): 554 case _T('a'): 555 case _T('f'): 556 #ifdef _UNICODE 557 flags |= FLAG_WIDECHAR; 558 #else 559 flags &= ~FLAG_WIDECHAR; 560 #endif 561 /* Use external function, one for kernel one for user mode */ 562 format_float(chr, flags, precision, &string, &prefix, &argptr); 563 len = _tcslen(string); 564 precision = 0; 565 break; 566 #endif 567 568 case _T('d'): 569 case _T('i'): 570 val64 = (__int64)va_arg_f(argptr, flags); 571 572 if ((__int64)val64 < 0) 573 { 574 val64 = -(__int64)val64; 575 prefix = _T("-"); 576 } 577 else if (flags & FLAG_FORCE_SIGN) 578 prefix = _T("+"); 579 else if (flags & FLAG_FORCE_SIGNSP) 580 prefix = _T(" "); 581 582 goto case_number; 583 584 case _T('o'): 585 base = 8; 586 if (flags & FLAG_SPECIAL) 587 { 588 prefix = _T("0"); 589 if (precision > 0) precision--; 590 } 591 goto case_unsigned; 592 593 case _T('p'): 594 precision = 2 * sizeof(void*); 595 flags &= ~FLAG_PAD_ZERO; 596 flags |= FLAG_INTPTR; 597 /* Fall through */ 598 599 case _T('X'): 600 digits = digits_u; 601 /* Fall through */ 602 603 case _T('x'): 604 base = 16; 605 if (flags & FLAG_SPECIAL) 606 { 607 prefix = &digits[16]; 608 #ifdef _USER32_WSPRINTF 609 fieldwidth += 2; 610 #endif 611 } 612 613 case _T('u'): 614 case_unsigned: 615 val64 = va_arg_fu(argptr, flags); 616 617 case_number: 618 #ifdef _UNICODE 619 flags |= FLAG_WIDECHAR; 620 #else 621 flags &= ~FLAG_WIDECHAR; 622 #endif 623 if (precision < 0) precision = 1; 624 625 /* Gather digits in reverse order */ 626 while (val64) 627 { 628 *--string = digits[val64 % base]; 629 val64 /= base; 630 precision--; 631 } 632 633 len = _tcslen(string); 634 break; 635 636 default: 637 /* Treat anything else as a new character */ 638 format--; 639 continue; 640 } 641 642 /* Calculate padding */ 643 prefixlen = prefix ? _tcslen(prefix) : 0; 644 if (precision < 0) precision = 0; 645 padding = (int)(fieldwidth - len - prefixlen - precision); 646 if (padding < 0) padding = 0; 647 648 /* Optional left space padding */ 649 if ((flags & (FLAG_ALIGN_LEFT | FLAG_PAD_ZERO)) == 0) 650 { 651 for (; padding > 0; padding--) 652 { 653 if ((written = streamout_char(stream, _T(' '))) == 0) return -1; 654 written_all += written; 655 } 656 } 657 658 /* Optional prefix */ 659 if (prefix) 660 { 661 written = streamout_string(stream, prefix, prefixlen); 662 if (written == -1) return -1; 663 written_all += written; 664 } 665 666 /* Optional left '0' padding */ 667 if ((flags & FLAG_ALIGN_LEFT) == 0) precision += padding; 668 while (precision-- > 0) 669 { 670 if ((written = streamout_char(stream, _T('0'))) == 0) return -1; 671 written_all += written; 672 } 673 674 /* Output the string */ 675 if (flags & FLAG_WIDECHAR) 676 written = streamout_wstring(stream, (wchar_t*)string, len); 677 else 678 written = streamout_astring(stream, (char*)string, len); 679 if (written == -1) return -1; 680 written_all += written; 681 682 #if 0 && SUPPORT_FLOAT 683 /* Optional right '0' padding */ 684 while (precision-- > 0) 685 { 686 if ((written = streamout_char(stream, _T('0'))) == 0) return -1; 687 written_all += written; 688 len++; 689 } 690 #endif 691 692 /* Optional right padding */ 693 if (flags & FLAG_ALIGN_LEFT) 694 { 695 while (padding-- > 0) 696 { 697 if ((written = streamout_char(stream, _T(' '))) == 0) return -1; 698 written_all += written; 699 } 700 } 701 702 } 703 704 if (written == -1) return -1; 705 706 return written_all; 707 } 708 709