Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenstreamout.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: GNU GPL, see COPYING in the top level directory 00003 * PROJECT: ReactOS crt library 00004 * FILE: lib/sdk/crt/printf/streamout.c 00005 * PURPOSE: Implementation of streamout 00006 * PROGRAMMER: Timo Kreuzer 00007 */ 00008 00009 #include <stdio.h> 00010 #include <stdarg.h> 00011 #include <tchar.h> 00012 #include <strings.h> 00013 #include <math.h> 00014 #include <float.h> 00015 00016 #ifdef _UNICODE 00017 # define streamout wstreamout 00018 # define format_float format_floatw 00019 #endif 00020 00021 #define MB_CUR_MAX 10 00022 #define BUFFER_SIZE (32 + 17) 00023 00024 int mbtowc(wchar_t *wchar, const char *mbchar, size_t count); 00025 int wctomb(char *mbchar, wchar_t wchar); 00026 00027 typedef struct _STRING 00028 { 00029 unsigned short Length; 00030 unsigned short MaximumLength; 00031 void *Buffer; 00032 } STRING; 00033 00034 enum 00035 { 00036 /* Formatting flags */ 00037 FLAG_ALIGN_LEFT = 0x01, 00038 FLAG_FORCE_SIGN = 0x02, 00039 FLAG_FORCE_SIGNSP = 0x04, 00040 FLAG_PAD_ZERO = 0x08, 00041 FLAG_SPECIAL = 0x10, 00042 00043 /* Data format flags */ 00044 FLAG_SHORT = 0x100, 00045 FLAG_LONG = 0x200, 00046 FLAG_WIDECHAR = FLAG_LONG, 00047 FLAG_INT64 = 0x400, 00048 #ifdef _WIN64 00049 FLAG_INTPTR = FLAG_INT64, 00050 #else 00051 FLAG_INTPTR = 0, 00052 #endif 00053 FLAG_LONGDOUBLE = 0x800, 00054 }; 00055 00056 #define va_arg_f(argptr, flags) \ 00057 (flags & FLAG_INT64) ? va_arg(argptr, __int64) : \ 00058 (flags & FLAG_SHORT) ? (short)va_arg(argptr, int) : \ 00059 va_arg(argptr, int) 00060 00061 #define va_arg_fu(argptr, flags) \ 00062 (flags & FLAG_INT64) ? va_arg(argptr, unsigned __int64) : \ 00063 (flags & FLAG_SHORT) ? (unsigned short)va_arg(argptr, int) : \ 00064 va_arg(argptr, unsigned int) 00065 00066 #define va_arg_ffp(argptr, flags) \ 00067 (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \ 00068 va_arg(argptr, double) 00069 00070 #define get_exp(f) (int)floor(f == 0 ? 0 : (f >= 0 ? log10(f) : log10(-f))) 00071 #define round(x) floor((x) + 0.5) 00072 00073 #ifndef _USER32_WSPRINTF 00074 00075 void 00076 #ifdef _LIBCNT_ 00077 /* Due to restrictions in kernel mode regarding the use of floating point, 00078 we prevent it from being inlined */ 00079 __declspec(noinline) 00080 #endif 00081 format_float( 00082 TCHAR chr, 00083 unsigned int flags, 00084 int precision, 00085 TCHAR **string, 00086 const TCHAR **prefix, 00087 va_list *argptr) 00088 { 00089 static const TCHAR digits_l[] = _T("0123456789abcdef0x"); 00090 static const TCHAR digits_u[] = _T("0123456789ABCDEF0X"); 00091 static const TCHAR _nan[] = _T("#QNAN"); 00092 static const TCHAR _infinity[] = _T("#INF"); 00093 const TCHAR *digits = digits_l; 00094 int exponent = 0, sign; 00095 long double fpval, fpval2; 00096 int padding = 0, num_digits, val32, base = 10; 00097 00098 /* Normalize the precision */ 00099 if (precision < 0) precision = 6; 00100 else if (precision > 17) 00101 { 00102 padding = precision - 17; 00103 precision = 17; 00104 } 00105 00106 /* Get the float value and calculate the exponent */ 00107 fpval = va_arg_ffp(*argptr, flags); 00108 exponent = get_exp(fpval); 00109 sign = fpval < 0 ? -1 : 1; 00110 00111 switch (chr) 00112 { 00113 case _T('G'): 00114 digits = digits_u; 00115 case _T('g'): 00116 if (precision > 0) precision--; 00117 if (exponent < -4 || exponent >= precision) goto case_e; 00118 00119 /* Shift the decimal point and round */ 00120 fpval2 = round(sign * fpval * pow(10., precision)); 00121 00122 /* Skip trailing zeroes */ 00123 while (precision && (unsigned __int64)fpval2 % 10 == 0) 00124 { 00125 precision--; 00126 fpval2 /= 10; 00127 } 00128 break; 00129 00130 case _T('E'): 00131 digits = digits_u; 00132 case _T('e'): 00133 case_e: 00134 /* Shift the decimal point and round */ 00135 fpval2 = round(sign * fpval * pow(10., precision - exponent)); 00136 00137 /* Compensate for changed exponent through rounding */ 00138 if (fpval2 >= (unsigned __int64)pow(10., precision + 1)) 00139 { 00140 exponent++; 00141 fpval2 = round(sign * fpval * pow(10., precision - exponent)); 00142 } 00143 00144 val32 = exponent >= 0 ? exponent : -exponent; 00145 00146 // FIXME: handle length of exponent field: 00147 // http://msdn.microsoft.com/de-de/library/0fatw238%28VS.80%29.aspx 00148 num_digits = 3; 00149 while (num_digits--) 00150 { 00151 *--(*string) = digits[val32 % 10]; 00152 val32 /= 10; 00153 } 00154 00155 /* Sign for the exponent */ 00156 *--(*string) = exponent >= 0 ? _T('+') : _T('-'); 00157 00158 /* Add 'e' or 'E' separator */ 00159 *--(*string) = digits[0xe]; 00160 break; 00161 00162 case _T('A'): 00163 digits = digits_u; 00164 case _T('a'): 00165 // base = 16; 00166 // FIXME: TODO 00167 00168 case _T('f'): 00169 default: 00170 /* Shift the decimal point and round */ 00171 fpval2 = round(sign * fpval * pow(10., precision)); 00172 break; 00173 } 00174 00175 /* Handle sign */ 00176 if (fpval < 0) 00177 { 00178 *prefix = _T("-"); 00179 } 00180 else if (flags & FLAG_FORCE_SIGN) 00181 *prefix = _T("+"); 00182 else if (flags & FLAG_FORCE_SIGNSP) 00183 *prefix = _T(" "); 00184 00185 /* Handle special cases first */ 00186 if (_isnan(fpval)) 00187 { 00188 (*string) -= sizeof(_nan) / sizeof(TCHAR) - 1; 00189 _tcscpy((*string), _nan); 00190 fpval2 = 1; 00191 } 00192 else if (!_finite(fpval)) 00193 { 00194 (*string) -= sizeof(_infinity) / sizeof(TCHAR) - 1; 00195 _tcscpy((*string), _infinity); 00196 fpval2 = 1; 00197 } 00198 else 00199 { 00200 /* Zero padding */ 00201 while (padding-- > 0) *--(*string) = _T('0'); 00202 00203 /* Digits after the decimal point */ 00204 num_digits = precision; 00205 while (num_digits-- > 0) 00206 { 00207 *--(*string) = digits[(unsigned __int64)fpval2 % 10]; 00208 fpval2 /= base; 00209 } 00210 } 00211 00212 if (precision > 0 || flags & FLAG_SPECIAL) 00213 *--(*string) = _T('.'); 00214 00215 /* Digits before the decimal point */ 00216 do 00217 { 00218 *--(*string) = digits[(unsigned __int64)fpval2 % base]; 00219 fpval2 /= base; 00220 } 00221 while ((unsigned __int64)fpval2); 00222 00223 } 00224 #endif 00225 00226 static 00227 int 00228 streamout_char(FILE *stream, int chr) 00229 { 00230 #if defined(_USER32_WSPRINTF) || defined(_LIBCNT_) 00231 /* Check if the buffer is full */ 00232 if (stream->_cnt < sizeof(TCHAR)) 00233 return 0; 00234 00235 *(TCHAR*)stream->_ptr = chr; 00236 stream->_ptr += sizeof(TCHAR); 00237 stream->_cnt -= sizeof(TCHAR); 00238 00239 return 1; 00240 #else 00241 return _fputtc((TCHAR)chr, stream) != _TEOF; 00242 #endif 00243 } 00244 00245 static 00246 int 00247 streamout_astring(FILE *stream, const char *string, size_t count) 00248 { 00249 TCHAR chr; 00250 int written = 0; 00251 00252 while (count--) 00253 { 00254 #ifdef _UNICODE 00255 int len; 00256 if ((len = mbtowc(&chr, string, MB_CUR_MAX)) < 1) break; 00257 string += len; 00258 #else 00259 chr = *string++; 00260 #endif 00261 if (streamout_char(stream, chr) == 0) return -1; 00262 written++; 00263 } 00264 00265 return written; 00266 } 00267 00268 static 00269 int 00270 streamout_wstring(FILE *stream, const wchar_t *string, size_t count) 00271 { 00272 wchar_t chr; 00273 int written = 0; 00274 00275 while (count--) 00276 { 00277 #ifndef _UNICODE 00278 char mbchar[MB_CUR_MAX], *ptr = mbchar; 00279 int mblen; 00280 00281 mblen = wctomb(mbchar, *string++); 00282 if (mblen <= 0) return written; 00283 00284 while (chr = *ptr++, mblen--) 00285 #else 00286 chr = *string++; 00287 #endif 00288 { 00289 if (streamout_char(stream, chr) == 0) return -1; 00290 written++; 00291 } 00292 } 00293 00294 return written; 00295 } 00296 00297 #ifdef _UNICODE 00298 #define streamout_string streamout_wstring 00299 #else 00300 #define streamout_string streamout_astring 00301 #endif 00302 00303 #ifdef _USER32_WSPRINTF 00304 # define USE_MULTISIZE 0 00305 #else 00306 # define USE_MULTISIZE 1 00307 #endif 00308 00309 int 00310 _cdecl 00311 streamout(FILE *stream, const TCHAR *format, va_list argptr) 00312 { 00313 static const TCHAR digits_l[] = _T("0123456789abcdef0x"); 00314 static const TCHAR digits_u[] = _T("0123456789ABCDEF0X"); 00315 static const char *_nullstring = "(null)"; 00316 TCHAR buffer[BUFFER_SIZE + 1]; 00317 TCHAR chr, *string; 00318 STRING *nt_string; 00319 const TCHAR *digits, *prefix; 00320 int base, fieldwidth, precision, padding; 00321 size_t prefixlen, len; 00322 int written = 1, written_all = 0; 00323 unsigned int flags; 00324 unsigned __int64 val64; 00325 00326 buffer[BUFFER_SIZE] = '\0'; 00327 00328 while (written >= 0) 00329 { 00330 chr = *format++; 00331 00332 /* Check for end of format string */ 00333 if (chr == _T('\0')) break; 00334 00335 /* Check for 'normal' character or double % */ 00336 if ((chr != _T('%')) || 00337 (chr = *format++) == _T('%')) 00338 { 00339 /* Write the character to the stream */ 00340 if ((written = streamout_char(stream, chr)) == 0) return -1; 00341 written_all += written; 00342 continue; 00343 } 00344 00345 /* Handle flags */ 00346 flags = 0; 00347 while (1) 00348 { 00349 if (chr == _T('-')) flags |= FLAG_ALIGN_LEFT; 00350 else if (chr == _T('+')) flags |= FLAG_FORCE_SIGN; 00351 else if (chr == _T(' ')) flags |= FLAG_FORCE_SIGNSP; 00352 else if (chr == _T('0')) flags |= FLAG_PAD_ZERO; 00353 else if (chr == _T('#')) flags |= FLAG_SPECIAL; 00354 else break; 00355 chr = *format++; 00356 } 00357 00358 /* Handle field width modifier */ 00359 if (chr == _T('*')) 00360 { 00361 fieldwidth = va_arg(argptr, int); 00362 if (fieldwidth < 0) 00363 { 00364 flags |= FLAG_ALIGN_LEFT; 00365 fieldwidth = -fieldwidth; 00366 } 00367 chr = *format++; 00368 } 00369 else 00370 { 00371 fieldwidth = 0; 00372 while (chr >= _T('0') && chr <= _T('9')) 00373 { 00374 fieldwidth = fieldwidth * 10 + (chr - _T('0')); 00375 chr = *format++; 00376 } 00377 } 00378 00379 /* Handle precision modifier */ 00380 if (chr == '.') 00381 { 00382 chr = *format++; 00383 00384 if (chr == _T('*')) 00385 { 00386 precision = va_arg(argptr, int); 00387 chr = *format++; 00388 } 00389 else 00390 { 00391 precision = 0; 00392 while (chr >= _T('0') && chr <= _T('9')) 00393 { 00394 precision = precision * 10 + (chr - _T('0')); 00395 chr = *format++; 00396 } 00397 } 00398 } 00399 else precision = -1; 00400 00401 /* Handle argument size prefix */ 00402 do 00403 { 00404 if (chr == _T('h')) flags |= FLAG_SHORT; 00405 else if (chr == _T('w')) flags |= FLAG_WIDECHAR; 00406 else if (chr == _T('L')) flags |= 0; // FIXME: long double 00407 else if (chr == _T('F')) flags |= 0; // FIXME: what is that? 00408 else if (chr == _T('l')) 00409 { 00410 /* Check if this is the 2nd 'l' in a row */ 00411 if (format[-2] == 'l') flags |= FLAG_INT64; 00412 else flags |= FLAG_LONG; 00413 } 00414 else if (chr == _T('I')) 00415 { 00416 if (format[0] == _T('3') && format[1] == _T('2')) 00417 { 00418 format += 2; 00419 } 00420 else if (format[0] == _T('6') && format[1] == _T('4')) 00421 { 00422 format += 2; 00423 flags |= FLAG_INT64; 00424 } 00425 else if (format[0] == _T('x') || format[0] == _T('X') || 00426 format[0] == _T('d') || format[0] == _T('i') || 00427 format[0] == _T('u') || format[0] == _T('o')) 00428 { 00429 flags |= FLAG_INTPTR; 00430 } 00431 else break; 00432 } 00433 else break; 00434 chr = *format++; 00435 } 00436 while (USE_MULTISIZE); 00437 00438 /* Handle the format specifier */ 00439 digits = digits_l; 00440 string = &buffer[BUFFER_SIZE]; 00441 base = 10; 00442 prefix = 0; 00443 switch (chr) 00444 { 00445 case _T('n'): 00446 if (flags & FLAG_INT64) 00447 *va_arg(argptr, __int64*) = written_all; 00448 else if (flags & FLAG_SHORT) 00449 *va_arg(argptr, short*) = written_all; 00450 else 00451 *va_arg(argptr, int*) = written_all; 00452 continue; 00453 00454 case _T('C'): 00455 #ifndef _UNICODE 00456 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 00457 #endif 00458 goto case_char; 00459 00460 case _T('c'): 00461 #ifdef _UNICODE 00462 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 00463 #endif 00464 case_char: 00465 string = buffer; 00466 len = 1; 00467 if (flags & FLAG_WIDECHAR) 00468 { 00469 ((wchar_t*)string)[0] = va_arg(argptr, int); 00470 ((wchar_t*)string)[1] = _T('\0'); 00471 } 00472 else 00473 { 00474 ((char*)string)[0] = va_arg(argptr, int); 00475 ((char*)string)[1] = _T('\0'); 00476 } 00477 break; 00478 00479 case _T('Z'): 00480 nt_string = va_arg(argptr, void*); 00481 if (nt_string && (string = nt_string->Buffer)) 00482 { 00483 len = nt_string->Length; 00484 if (flags & FLAG_WIDECHAR) len /= sizeof(wchar_t); 00485 break; 00486 } 00487 string = 0; 00488 goto case_string; 00489 00490 case _T('S'): 00491 string = va_arg(argptr, void*); 00492 #ifndef _UNICODE 00493 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 00494 #endif 00495 goto case_string; 00496 00497 case _T('s'): 00498 string = va_arg(argptr, void*); 00499 #ifdef _UNICODE 00500 if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR; 00501 #endif 00502 00503 case_string: 00504 if (!string) 00505 { 00506 string = (TCHAR*)_nullstring; 00507 flags &= ~FLAG_WIDECHAR; 00508 } 00509 00510 if (flags & FLAG_WIDECHAR) 00511 len = wcslen((wchar_t*)string); 00512 else 00513 len = strlen((char*)string); 00514 if (precision >= 0 && len > (unsigned)precision) len = precision; 00515 precision = 0; 00516 break; 00517 00518 #ifndef _USER32_WSPRINTF 00519 case _T('G'): 00520 case _T('E'): 00521 case _T('A'): 00522 case _T('g'): 00523 case _T('e'): 00524 case _T('a'): 00525 case _T('f'): 00526 #ifdef _UNICODE 00527 flags |= FLAG_WIDECHAR; 00528 #else 00529 flags &= ~FLAG_WIDECHAR; 00530 #endif 00531 /* Use external function, one for kernel one for user mode */ 00532 format_float(chr, flags, precision, &string, &prefix, &argptr); 00533 len = _tcslen(string); 00534 precision = 0; 00535 break; 00536 #endif 00537 00538 case _T('d'): 00539 case _T('i'): 00540 val64 = (__int64)va_arg_f(argptr, flags); 00541 00542 if ((__int64)val64 < 0) 00543 { 00544 val64 = -(__int64)val64; 00545 prefix = _T("-"); 00546 } 00547 else if (flags & FLAG_FORCE_SIGN) 00548 prefix = _T("+"); 00549 else if (flags & FLAG_FORCE_SIGNSP) 00550 prefix = _T(" "); 00551 00552 goto case_number; 00553 00554 case _T('o'): 00555 base = 8; 00556 if (flags & FLAG_SPECIAL) 00557 { 00558 prefix = _T("0"); 00559 if (precision > 0) precision--; 00560 } 00561 goto case_unsigned; 00562 00563 case _T('p'): 00564 precision = 2 * sizeof(void*); 00565 flags &= ~FLAG_PAD_ZERO; 00566 flags |= FLAG_INTPTR; 00567 /* Fall through */ 00568 00569 case _T('X'): 00570 digits = digits_u; 00571 /* Fall through */ 00572 00573 case _T('x'): 00574 base = 16; 00575 if (flags & FLAG_SPECIAL) 00576 { 00577 prefix = &digits[16]; 00578 #ifdef _USER32_WSPRINTF 00579 fieldwidth += 2; 00580 #endif 00581 } 00582 00583 case _T('u'): 00584 case_unsigned: 00585 val64 = va_arg_fu(argptr, flags); 00586 00587 case_number: 00588 #ifdef _UNICODE 00589 flags |= FLAG_WIDECHAR; 00590 #else 00591 flags &= ~FLAG_WIDECHAR; 00592 #endif 00593 if (precision < 0) precision = 1; 00594 00595 /* Gather digits in reverse order */ 00596 while (val64) 00597 { 00598 *--string = digits[val64 % base]; 00599 val64 /= base; 00600 precision--; 00601 } 00602 00603 len = _tcslen(string); 00604 break; 00605 00606 default: 00607 /* Treat anything else as a new character */ 00608 format--; 00609 continue; 00610 } 00611 00612 /* Calculate padding */ 00613 prefixlen = prefix ? _tcslen(prefix) : 0; 00614 if (precision < 0) precision = 0; 00615 padding = (int)(fieldwidth - len - prefixlen - precision); 00616 if (padding < 0) padding = 0; 00617 00618 /* Optional left space padding */ 00619 if ((flags & (FLAG_ALIGN_LEFT | FLAG_PAD_ZERO)) == 0) 00620 { 00621 for (; padding > 0; padding--) 00622 { 00623 if ((written = streamout_char(stream, _T(' '))) == 0) return -1; 00624 written_all += written; 00625 } 00626 } 00627 00628 /* Optional prefix */ 00629 if (prefix) 00630 { 00631 written = streamout_string(stream, prefix, prefixlen); 00632 if (written == -1) return -1; 00633 written_all += written; 00634 } 00635 00636 /* Optional left '0' padding */ 00637 if ((flags & FLAG_ALIGN_LEFT) == 0) precision += padding; 00638 while (precision-- > 0) 00639 { 00640 if ((written = streamout_char(stream, _T('0'))) == 0) return -1; 00641 written_all += written; 00642 } 00643 00644 /* Output the string */ 00645 if (flags & FLAG_WIDECHAR) 00646 written = streamout_wstring(stream, (wchar_t*)string, len); 00647 else 00648 written = streamout_astring(stream, (char*)string, len); 00649 if (written == -1) return -1; 00650 written_all += written; 00651 00652 #if 0 && SUPPORT_FLOAT 00653 /* Optional right '0' padding */ 00654 while (precision-- > 0) 00655 { 00656 if ((written = streamout_char(stream, _T('0'))) == 0) return -1; 00657 written_all += written; 00658 len++; 00659 } 00660 #endif 00661 00662 /* Optional right padding */ 00663 if (flags & FLAG_ALIGN_LEFT) 00664 { 00665 while (padding-- > 0) 00666 { 00667 if ((written = streamout_char(stream, _T(' '))) == 0) return -1; 00668 written_all += written; 00669 } 00670 } 00671 00672 } 00673 00674 if (written == -1) return -1; 00675 00676 return written_all; 00677 } 00678 Generated on Sat May 26 2012 04:35:28 for ReactOS by
1.7.6.1
|