ReactOS  0.4.14-dev-358-gbef841c
streamout.c
Go to the documentation of this file.
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 */
41  FLAG_PAD_ZERO = 0x08,
42  FLAG_SPECIAL = 0x10,
43 
44  /* Data format flags */
45  FLAG_SHORT = 0x100,
46  FLAG_LONG = 0x200,
48  FLAG_INT64 = 0x400,
49 #ifdef _WIN64
51 #else
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 */
81 #endif
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
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
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  {
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('l'))
436  {
437  /* Check if this is the 2nd 'l' in a row */
438  if (format[-2] == 'l') flags |= FLAG_INT64;
439  else flags |= FLAG_LONG;
440  }
441  else if (chr == _T('I'))
442  {
443  if (format[0] == _T('3') && format[1] == _T('2'))
444  {
445  format += 2;
446  }
447  else if (format[0] == _T('6') && format[1] == _T('4'))
448  {
449  format += 2;
450  flags |= FLAG_INT64;
451  }
452  else if (format[0] == _T('x') || format[0] == _T('X') ||
453  format[0] == _T('d') || format[0] == _T('i') ||
454  format[0] == _T('u') || format[0] == _T('o'))
455  {
456  flags |= FLAG_INTPTR;
457  }
458  else break;
459  }
460  else break;
461  chr = *format++;
462  }
463  while (USE_MULTISIZE);
464 
465  /* Handle the format specifier */
466  digits = digits_l;
467  string = &buffer[BUFFER_SIZE];
468  base = 10;
469  prefix = 0;
470  switch (chr)
471  {
472  case _T('n'):
473  if (flags & FLAG_INT64)
474  *va_arg(argptr, __int64*) = written_all;
475  else if (flags & FLAG_SHORT)
476  *va_arg(argptr, short*) = written_all;
477  else
478  *va_arg(argptr, int*) = written_all;
479  continue;
480 
481  case _T('C'):
482 #ifndef _UNICODE
483  if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR;
484 #endif
485  goto case_char;
486 
487  case _T('c'):
488 #ifdef _UNICODE
489  if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR;
490 #endif
491  case_char:
492  string = buffer;
493  len = 1;
494  if (flags & FLAG_WIDECHAR)
495  {
496  ((wchar_t*)string)[0] = va_arg(argptr, int);
497  ((wchar_t*)string)[1] = _T('\0');
498  }
499  else
500  {
501  ((char*)string)[0] = va_arg(argptr, int);
502  ((char*)string)[1] = _T('\0');
503  }
504  break;
505 
506  case _T('Z'):
507  nt_string = va_arg(argptr, void*);
508  if (nt_string && (string = nt_string->Buffer))
509  {
510  len = nt_string->Length;
511  if (flags & FLAG_WIDECHAR) len /= sizeof(wchar_t);
512  break;
513  }
514  string = 0;
515  goto case_string;
516 
517  case _T('S'):
518  string = va_arg(argptr, void*);
519 #ifndef _UNICODE
520  if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR;
521 #endif
522  goto case_string;
523 
524  case _T('s'):
525  string = va_arg(argptr, void*);
526 #ifdef _UNICODE
527  if (!(flags & FLAG_SHORT)) flags |= FLAG_WIDECHAR;
528 #endif
529 
530  case_string:
531  if (!string)
532  {
533  string = (TCHAR*)_nullstring;
534  flags &= ~FLAG_WIDECHAR;
535  }
536 
537  if (flags & FLAG_WIDECHAR)
538  len = wcsnlen((wchar_t*)string, (unsigned)precision);
539  else
540  len = strnlen((char*)string, (unsigned)precision);
541  precision = 0;
542  break;
543 
544 #ifndef _USER32_WSPRINTF
545  case _T('G'):
546  case _T('E'):
547  case _T('A'):
548  case _T('g'):
549  case _T('e'):
550  case _T('a'):
551  case _T('f'):
552 #ifdef _UNICODE
553  flags |= FLAG_WIDECHAR;
554 #else
555  flags &= ~FLAG_WIDECHAR;
556 #endif
557  /* Use external function, one for kernel one for user mode */
558  format_float(chr, flags, precision, &string, &prefix, &argptr);
559  len = _tcslen(string);
560  precision = 0;
561  break;
562 #endif
563 
564  case _T('d'):
565  case _T('i'):
566  val64 = (__int64)va_arg_f(argptr, flags);
567 
568  if ((__int64)val64 < 0)
569  {
570  val64 = -(__int64)val64;
571  prefix = _T("-");
572  }
573  else if (flags & FLAG_FORCE_SIGN)
574  prefix = _T("+");
575  else if (flags & FLAG_FORCE_SIGNSP)
576  prefix = _T(" ");
577 
578  goto case_number;
579 
580  case _T('o'):
581  base = 8;
582  if (flags & FLAG_SPECIAL)
583  {
584  prefix = _T("0");
585  if (precision > 0) precision--;
586  }
587  goto case_unsigned;
588 
589  case _T('p'):
590  precision = 2 * sizeof(void*);
591  flags &= ~FLAG_PAD_ZERO;
592  flags |= FLAG_INTPTR;
593  /* Fall through */
594 
595  case _T('X'):
596  digits = digits_u;
597  /* Fall through */
598 
599  case _T('x'):
600  base = 16;
601  if (flags & FLAG_SPECIAL)
602  {
603  prefix = &digits[16];
604 #ifdef _USER32_WSPRINTF
605  fieldwidth += 2;
606 #endif
607  }
608 
609  case _T('u'):
610  case_unsigned:
611  val64 = va_arg_fu(argptr, flags);
612 
613  case_number:
614 #ifdef _UNICODE
615  flags |= FLAG_WIDECHAR;
616 #else
617  flags &= ~FLAG_WIDECHAR;
618 #endif
619  if (precision < 0) precision = 1;
620 
621  /* Gather digits in reverse order */
622  while (val64)
623  {
624  *--string = digits[val64 % base];
625  val64 /= base;
626  precision--;
627  }
628 
629  len = _tcslen(string);
630  break;
631 
632  default:
633  /* Treat anything else as a new character */
634  format--;
635  continue;
636  }
637 
638  /* Calculate padding */
639  prefixlen = prefix ? _tcslen(prefix) : 0;
640  if (precision < 0) precision = 0;
641  padding = (int)(fieldwidth - len - prefixlen - precision);
642  if (padding < 0) padding = 0;
643 
644  /* Optional left space padding */
645  if ((flags & (FLAG_ALIGN_LEFT | FLAG_PAD_ZERO)) == 0)
646  {
647  for (; padding > 0; padding--)
648  {
649  if ((written = streamout_char(stream, _T(' '))) == 0) return -1;
650  written_all += written;
651  }
652  }
653 
654  /* Optional prefix */
655  if (prefix)
656  {
657  written = streamout_string(stream, prefix, prefixlen);
658  if (written == -1) return -1;
659  written_all += written;
660  }
661 
662  /* Optional left '0' padding */
663  if ((flags & FLAG_ALIGN_LEFT) == 0) precision += padding;
664  while (precision-- > 0)
665  {
666  if ((written = streamout_char(stream, _T('0'))) == 0) return -1;
667  written_all += written;
668  }
669 
670  /* Output the string */
671  if (flags & FLAG_WIDECHAR)
672  written = streamout_wstring(stream, (wchar_t*)string, len);
673  else
674  written = streamout_astring(stream, (char*)string, len);
675  if (written == -1) return -1;
676  written_all += written;
677 
678 #if 0 && SUPPORT_FLOAT
679  /* Optional right '0' padding */
680  while (precision-- > 0)
681  {
682  if ((written = streamout_char(stream, _T('0'))) == 0) return -1;
683  written_all += written;
684  len++;
685  }
686 #endif
687 
688  /* Optional right padding */
689  if (flags & FLAG_ALIGN_LEFT)
690  {
691  while (padding-- > 0)
692  {
693  if ((written = streamout_char(stream, _T(' '))) == 0) return -1;
694  written_all += written;
695  }
696  }
697 
698  }
699 
700  if (written == -1) return -1;
701 
702  return written_all;
703 }
704 
#define _nan()
Definition: mingw_math.h:465
static size_t double int int int * sign
Definition: printf.c:69
#define va_arg_fu(argptr, flags)
Definition: streamout.c:62
_Check_return_ __MINGW_NOTHROW _CRTIMP int __cdecl _isnan(_In_ double)
#define __cdecl
Definition: accygwin.h:79
void format_float(TCHAR chr, unsigned int flags, int precision, TCHAR **string, const TCHAR **prefix, va_list *argptr)
Definition: streamout.c:82
GLuint GLuint GLsizei count
Definition: gl.h:1545
#define round(x)
Definition: streamout.c:72
_TCHAR * _tcscpy(_TCHAR *to, const _TCHAR *from)
Definition: tcscpy.h:8
unsigned short Length
Definition: sprintf.c:451
GLuint buffer
Definition: glext.h:5915
struct _EXCEPTION_POINTERS *_CRTIMP __declspec(noreturn) void __cdecl terminate(void)
Definition: eh.h:27
#define USE_MULTISIZE
Definition: streamout.c:321
#define _IOSTRG
Definition: stdio.h:135
#define va_arg_f(argptr, flags)
Definition: streamout.c:57
int wctomb(char *mbchar, wchar_t wchar)
Definition: freeldr.c:84
#define va_arg_ffp(argptr, flags)
Definition: streamout.c:67
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
#define noinline
Definition: types.h:60
void * Buffer
Definition: sprintf.c:453
GLuint base
Definition: 3dtext.c:35
float pow(float __x, int __y)
Definition: _cmath.h:458
static PVOID ptr
Definition: dispmode.c:27
size_t __cdecl _tcslen(const _TCHAR *str)
Definition: tcslen.h:9
int __cdecl streamout(FILE *stream, const TCHAR *format, va_list argptr)
Definition: streamout.c:326
smooth NULL
Definition: ftsmooth.c:416
char * va_list
Definition: acmsvcex.h:78
GLenum GLint GLint * precision
Definition: glext.h:7539
char TCHAR
Definition: xmlstorage.h:189
#define streamout_string
Definition: streamout.c:315
static int streamout_wstring(FILE *stream, const wchar_t *string, size_t count)
Definition: streamout.c:280
#define _T(x)
Definition: vfdio.h:22
unsigned int padding
Definition: isohybrid.c:50
_Check_return_ __MINGW_NOTHROW _CRTIMP int __cdecl _finite(_In_ double)
#define _fputtc
Definition: tchar.h:566
#define BUFFER_SIZE
Definition: streamout.c:23
GLbitfield flags
Definition: glext.h:7161
std::wstring STRING
Definition: fontsub.cpp:33
Definition: parse.h:22
GLenum GLsizei len
Definition: glext.h:6722
static int streamout_astring(FILE *stream, const char *string, size_t count)
Definition: streamout.c:252
int chr(char *serport)
Definition: gdblib.c:152
char string[160]
Definition: util.h:11
#define va_arg(ap, T)
Definition: acmsvcex.h:89
_Check_return_ int __cdecl mblen(_In_reads_bytes_opt_(_MaxCount) _Pre_opt_z_ const char *_Ch, _In_ size_t _MaxCount)
#define _TEOF
Definition: regproc.c:117
int mbtowc(wchar_t *wchar, const char *mbchar, size_t count)
Definition: freeldr.c:90
static int streamout_char(FILE *stream, int chr)
Definition: streamout.c:229
#define wchar_t
Definition: wchar.h:102
unsigned short MaximumLength
Definition: sprintf.c:452
#define MB_CUR_MAX
Definition: streamout.c:22
struct _STRING STRING
static const int digits[]
Definition: decode.c:71
#define __int64
Definition: basetyps.h:16
#define get_exp(f)
Definition: streamout.c:71
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31