twinapi.appcore: Implement Windows.System.Profile.AnalyticsInfo_get_DeviceFamily.
[wine.git] / dlls / msvcrt / printf.h
blobbe30d6ae7192c3e0930df9976fbbc03a987c5a4f
1 /*
2 * Copyright 2011 Piotr Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "bnum.h"
21 #ifdef PRINTF_WIDE
22 #define APICHAR wchar_t
23 #define CONVCHAR char
24 #define FUNC_NAME(func) func ## _w
25 #else
26 #define APICHAR char
27 #define CONVCHAR wchar_t
28 #define FUNC_NAME(func) func ## _a
29 #endif
31 struct FUNC_NAME(_str_ctx) {
32 size_t len;
33 APICHAR *buf;
36 static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
38 struct FUNC_NAME(_str_ctx) *out = ctx;
40 if(!out->buf)
41 return len;
43 if(out->len < len) {
44 memmove(out->buf, str, out->len*sizeof(APICHAR));
45 out->buf += out->len;
46 out->len = 0;
47 return -1;
50 memmove(out->buf, str, len*sizeof(APICHAR));
51 out->buf += len;
52 out->len -= len;
53 return len;
56 static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
58 *val = 0;
60 while (*fmt >= '0' && *fmt <= '9') {
61 *val *= 10;
62 *val += *fmt++ - '0';
65 return fmt;
68 /* pf_fill: takes care of signs, alignment, zero and field padding */
69 static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
70 int len, pf_flags *flags, BOOL left)
72 int i, r = 0, written;
74 if(flags->Sign && !strchr("diaAeEfFgG", flags->Format))
75 flags->Sign = 0;
77 if(left && flags->Sign) {
78 APICHAR ch = flags->Sign;
79 flags->FieldLength--;
80 if(flags->PadZero)
81 r = pf_puts(puts_ctx, 1, &ch);
83 written = r;
85 if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) {
86 APICHAR ch;
88 if(left && flags->PadZero)
89 ch = '0';
90 else
91 ch = ' ';
93 for(i=0; i<flags->FieldLength-len && r>=0; i++) {
94 r = pf_puts(puts_ctx, 1, &ch);
95 written += r;
100 if(r>=0 && left && flags->Sign && !flags->PadZero) {
101 APICHAR ch = flags->Sign;
102 r = pf_puts(puts_ctx, 1, &ch);
103 written += r;
106 return r>=0 ? written : r;
109 #ifndef PRINTF_HELPERS
110 #define PRINTF_HELPERS
111 static inline int wcstombs_len(char *mbstr, const wchar_t *wcstr,
112 int len, _locale_t locale)
114 char buf[MB_LEN_MAX];
115 int i, r, mblen = 0;
117 for(i=0; i<len; i++) {
118 r = _wctomb_l(mbstr ? mbstr+mblen : buf, wcstr[i], locale);
119 if(r < 0) return r;
120 mblen += r;
122 return mblen;
125 static inline int mbstowcs_len(wchar_t *wcstr, const char *mbstr,
126 int len, _locale_t locale)
128 int i, r, wlen = 0;
129 WCHAR buf;
131 for(i=0; i<len; wlen++) {
132 r = _mbtowc_l(wcstr ? wcstr+wlen : &buf, mbstr+i, len-i, locale);
133 if(r < 0) return r;
134 i += r ? r : 1;
136 return wlen;
139 static inline unsigned int log2i(unsigned int x)
141 ULONG result;
142 _BitScanReverse(&result, x);
143 return result;
146 static inline unsigned int log10i(unsigned int x)
148 unsigned int t = ((log2i(x) + 1) * 1233) / 4096;
149 return t - (x < p10s[t]);
152 #endif
154 static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
155 const wchar_t *str, int len, _locale_t locale)
157 #ifdef PRINTF_WIDE
158 return pf_puts(puts_ctx, len, str);
159 #else
160 LPSTR out;
161 int len_a = wcstombs_len(NULL, str, len, locale);
162 if(len_a < 0)
163 return -1;
165 out = HeapAlloc(GetProcessHeap(), 0, len_a);
166 if(!out)
167 return -1;
169 wcstombs_len(out, str, len, locale);
170 len = pf_puts(puts_ctx, len_a, out);
171 HeapFree(GetProcessHeap(), 0, out);
172 return len;
173 #endif
176 static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
177 const char *str, int len, _locale_t locale)
179 #ifdef PRINTF_WIDE
180 LPWSTR out;
181 int len_w = mbstowcs_len(NULL, str, len, locale);
182 if(len_w < 0)
183 return -1;
185 out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
186 if(!out)
187 return -1;
189 mbstowcs_len(out, str, len, locale);
190 len = pf_puts(puts_ctx, len_w, out);
191 HeapFree(GetProcessHeap(), 0, out);
192 return len;
193 #else
194 return pf_puts(puts_ctx, len, str);
195 #endif
198 static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
199 const wchar_t *str, int len, pf_flags *flags, _locale_t locale)
201 int r, ret;
203 if(len < 0) {
204 /* Do not search past the length specified by the precision. */
205 if(flags->Precision>=0)
206 len = wcsnlen(str, flags->Precision);
207 else
208 len = wcslen(str);
211 if(flags->Precision>=0 && flags->Precision<len)
212 len = flags->Precision;
214 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
215 ret = r;
216 if(r >= 0) {
217 r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locale);
218 ret += r;
220 if(r >= 0) {
221 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
222 ret += r;
225 return r>=0 ? ret : r;
228 static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
229 const char *str, int len, pf_flags *flags, _locale_t locale)
231 int r, ret;
233 if(len < 0) {
234 /* Do not search past the length specified by the precision. */
235 if(flags->Precision>=0)
236 len = strnlen(str, flags->Precision);
237 else
238 len = strlen(str);
241 if(flags->Precision>=0 && flags->Precision<len)
242 len = flags->Precision;
244 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
245 ret = r;
246 if(r >= 0) {
247 r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locale);
248 ret += r;
250 if(r >= 0) {
251 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
252 ret += r;
255 return r>=0 ? ret : r;
258 static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
259 const void *str, int len, pf_flags *flags, _locale_t locale, BOOL legacy_wide)
261 BOOL api_is_wide = sizeof(APICHAR) == sizeof(wchar_t);
262 BOOL complement_is_narrow = legacy_wide ? api_is_wide : FALSE;
263 #ifdef PRINTF_WIDE
265 if(!str)
266 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, L"(null)", 6, flags, locale);
267 #else
268 if(!str)
269 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locale);
270 #endif
272 if((flags->NaturalString && api_is_wide) || flags->WideString || flags->IntegerLength == LEN_LONG)
273 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
274 if((flags->NaturalString && !api_is_wide) || flags->IntegerLength == LEN_SHORT)
275 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
277 if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow)
278 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
279 else
280 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
283 static inline int FUNC_NAME(pf_output_special_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
284 double v, pf_flags *flags, _locale_t locale,
285 BOOL legacy_msvcrt_compat, BOOL three_digit_exp)
287 APICHAR pfx[16], sfx[8], *p;
288 int len = 0, r, frac_len, pfx_len, sfx_len;
290 if(!legacy_msvcrt_compat) {
291 const char *str;
293 if(isinf(v)) {
294 if(strchr("AEFG", flags->Format)) str = "INF";
295 else str = "inf";
296 }else {
297 if(strchr("AEFG", flags->Format)) str = (flags->Sign == '-' ? "NAN(IND)" : "NAN");
298 else str = (flags->Sign == '-' ? "nan(ind)" : "nan");
301 flags->Precision = -1;
302 flags->PadZero = FALSE;
303 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, -1, flags, locale);
306 /* workaround a bug in native implementation */
307 if(flags->Format=='g' || flags->Format=='G')
308 flags->Precision--;
310 p = pfx;
311 if(flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
312 if (flags->Sign) *p++ = flags->Sign;
313 *p++ = '0';
314 *p++ = (flags->Format=='a' ? 'x' : 'X');
315 r = pf_puts(puts_ctx, p-pfx, pfx);
316 if(r < 0) return r;
317 len += r;
319 flags->FieldLength -= p-pfx;
322 p = pfx;
323 if(!flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
324 *p++ = '0';
325 *p++ = (flags->Format=='a' ? 'x' : 'X');
328 *p++ = '1';
329 *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
330 *p++ = '#';
331 frac_len = 1;
333 if(isinf(v)) {
334 *p++ = 'I';
335 *p++ = 'N';
336 *p++ = 'F';
337 frac_len += 3;
338 }else if(flags->Sign == '-') {
339 *p++ = 'I';
340 *p++ = 'N';
341 *p++ = 'D';
342 frac_len += 3;
343 }else {
344 *p++ = 'Q';
345 *p++ = 'N';
346 *p++ = 'A';
347 *p++ = 'N';
348 frac_len += 4;
350 *p = 0;
351 pfx_len = p - pfx;
353 if(len) flags->Sign = 0;
355 if(flags->Precision>=0 && flags->Precision<frac_len)
356 p[flags->Precision - frac_len - 1]++;
358 p = sfx;
359 if(strchr("aAeE", flags->Format)) {
360 if(flags->Format == 'a') *p++ = 'p';
361 else if(flags->Format == 'A') *p++ = 'P';
362 else if(flags->Format == 'e') *p++ = 'e';
363 else *p++ = 'E';
365 *p++ = '+';
366 *p++ = '0';
368 if(flags->Format == 'e' || flags->Format == 'E') {
369 *p++ = '0';
370 if(three_digit_exp) *p++ = '0';
373 *p = 0;
375 if(!flags->Alternate && (flags->Format == 'g' || flags->Format == 'G')) sfx_len = frac_len;
376 else sfx_len = flags->Precision;
378 if(sfx_len == -1) {
379 if(strchr("fFeE", flags->Format)) sfx_len = 6;
380 else if(flags->Format == 'a' || flags->Format == 'A') sfx_len = 13;
382 sfx_len += p - sfx - frac_len;
384 if(sfx_len > 0) flags->FieldLength -= sfx_len;
385 if(flags->Precision >= 0) {
386 if(!flags->Precision) flags->Precision--;
387 flags->Precision += pfx_len - frac_len;
389 #ifdef PRINTF_WIDE
390 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
391 #else
392 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
393 #endif
394 if(r < 0) return r;
395 len += r;
397 flags->FieldLength = sfx_len;
398 flags->PadZero = TRUE;
399 flags->Precision = -1;
400 flags->Sign = 0;
401 #ifdef PRINTF_WIDE
402 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
403 #else
404 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
405 #endif
406 if(r < 0) return r;
407 len += r;
409 return len;
412 static inline int FUNC_NAME(pf_output_hex_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
413 double v, pf_flags *flags, _locale_t locale, BOOL standard_rounding)
415 const APICHAR digits[2][16] = {
416 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
417 { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }
420 APICHAR pfx[4+MANT_BITS/4+1], sfx[8], *p;
421 ULONGLONG mant;
422 int len = 0, sfx_len = 0, r, exp;
424 mant = (*(ULONGLONG*)&v) << 1;
425 exp = (mant >> MANT_BITS);
426 exp -= (1 << (EXP_BITS - 1)) - 1;
427 mant = (mant << EXP_BITS) >> (EXP_BITS+1);
429 p = pfx;
430 if(flags->PadZero) {
431 if(flags->Sign) *p++ = flags->Sign;
432 *p++ = '0';
433 *p++ = (flags->Format=='a' ? 'x' : 'X');
434 r = pf_puts(puts_ctx, p-pfx, pfx);
435 if(r < 0) return r;
436 len += r;
438 flags->FieldLength -= p-pfx;
439 flags->Sign = 0;
440 p = pfx;
441 }else {
442 *p++ = '0';
443 *p++ = (flags->Format=='a' ? 'x' : 'X');
445 if(exp == -(1 << (EXP_BITS-1))+1) {
446 if(!mant) exp = 0;
447 else exp++;
448 *p++ = '0';
449 }else {
450 *p++ = '1';
452 *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
453 for(r=MANT_BITS/4-1; r>=0; r--) {
454 p[r] = digits[flags->Format == 'A'][mant & 15];
455 mant >>= 4;
457 if(!flags->Precision) {
458 if(p[0] >= '8') p[-2]++;
459 if(!flags->Alternate) p--;
460 }else if(flags->Precision>0 && flags->Precision<MANT_BITS/4) {
461 BOOL round_up = FALSE;
463 if(!standard_rounding) round_up = (p[flags->Precision] >= '8');
464 else if(p[flags->Precision] > '8') round_up = TRUE;
465 else if(p[flags->Precision] == '8') {
466 for(r = flags->Precision+1; r<MANT_BITS/4; r++) {
467 if(p[r] != '0') {
468 round_up = TRUE;
469 break;
473 if(!round_up) {
474 if(p[flags->Precision-1] <= '9') round_up = (p[flags->Precision-1] - '0') & 1;
475 else if(p[flags->Precision-1] <= 'F') round_up = (p[flags->Precision-1] - 'A') & 1;
476 else round_up = (p[flags->Precision-1] - 'a') & 1;
480 for(r=flags->Precision-1; r>=0 && round_up; r--) {
481 round_up = FALSE;
482 if(p[r]=='f' || p[r]=='F') {
483 p[r] = '0';
484 round_up = TRUE;
485 }else if(p[r] == '9') {
486 p[r] = (flags->Format == 'a' ? 'a' : 'A');
487 }else {
488 p[r]++;
491 if(round_up) p[-2]++;
492 p += flags->Precision;
493 }else {
494 p += MANT_BITS/4;
495 if(flags->Precision > MANT_BITS/4) sfx_len += flags->Precision - MANT_BITS/4;
497 *p = 0;
499 p = sfx;
500 *p++ = (flags->Format == 'a' ? 'p' : 'P');
501 if(exp < 0) {
502 *p++ = '-';
503 exp = -exp;
504 }else {
505 *p++ = '+';
507 for(r=3; r>=0; r--) {
508 p[r] = exp%10 + '0';
509 exp /= 10;
510 if(!exp) break;
512 for(exp=0; exp<4-r; exp++)
513 p[exp] = p[exp+r];
514 p += exp;
515 *p = 0;
516 sfx_len += p - sfx;
518 flags->FieldLength -= sfx_len;
519 flags->Precision = -1;
520 #ifdef PRINTF_WIDE
521 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
522 #else
523 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
524 #endif
525 if(r < 0) return r;
526 len += r;
528 flags->FieldLength = sfx_len;
529 flags->PadZero = TRUE;
530 flags->Sign = 0;
531 #ifdef PRINTF_WIDE
532 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
533 #else
534 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
535 #endif
536 if(r < 0) return r;
537 len += r;
539 return len;
542 /* pf_integer_conv: prints x to buf, including alternate formats and
543 additional precision digits, but not field characters or the sign */
544 static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, pf_flags *flags, LONGLONG x)
546 unsigned int base;
547 const char *digits;
548 int i, j, k;
550 if(flags->Format == 'o')
551 base = 8;
552 else if(flags->Format=='x' || flags->Format=='X')
553 base = 16;
554 else
555 base = 10;
557 if(flags->Format == 'X')
558 digits = "0123456789ABCDEFX";
559 else
560 digits = "0123456789abcdefx";
562 if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
563 x = -x;
564 flags->Sign = '-';
567 i = 0;
568 if(x == 0) {
569 flags->Alternate = FALSE;
570 if(flags->Precision)
571 buf[i++] = '0';
572 } else {
573 while(x != 0) {
574 j = (ULONGLONG)x%base;
575 x = (ULONGLONG)x/base;
576 buf[i++] = digits[j];
579 k = flags->Precision-i;
580 while(k-- > 0)
581 buf[i++] = '0';
582 if(flags->Alternate) {
583 if(base == 16) {
584 buf[i++] = digits[16];
585 buf[i++] = '0';
586 } else if(base==8 && buf[i-1]!='0')
587 buf[i++] = '0';
590 /* Adjust precision so pf_fill won't truncate the number later */
591 flags->Precision = i;
593 buf[i] = '\0';
594 j = 0;
595 while(--i > j) {
596 APICHAR tmp = buf[j];
597 buf[j] = buf[i];
598 buf[i] = tmp;
599 j++;
603 static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
604 double v, pf_flags *flags, _locale_t locale, BOOL three_digit_exp,
605 BOOL standard_rounding)
607 int e2, e10 = 0, round_pos, round_limb, radix_pos, first_limb_len, i, len, r, ret;
608 BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])];
609 struct bnum *b = (struct bnum*)bnum_data;
610 APICHAR buf[LIMB_DIGITS + 1];
611 BOOL trim_tail = FALSE, round_up = FALSE;
612 pf_flags f;
613 int limb_len, prec;
614 ULONGLONG m;
615 DWORD l;
617 if(flags->Precision == -1)
618 flags->Precision = 6;
620 v = frexp(v, &e2);
621 if(v) {
622 m = (ULONGLONG)1 << (MANT_BITS - 1);
623 m |= (*(ULONGLONG*)&v & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1));
624 b->b = 0;
625 b->e = 2;
626 b->size = BNUM_PREC64;
627 b->data[0] = m % LIMB_MAX;
628 b->data[1] = m / LIMB_MAX;
629 e2 -= MANT_BITS;
631 while(e2 > 0) {
632 int shift = e2 > 29 ? 29 : e2;
633 if(bnum_lshift(b, shift)) e10 += LIMB_DIGITS;
634 e2 -= shift;
636 while(e2 < 0) {
637 int shift = -e2 > 9 ? 9 : -e2;
638 if(bnum_rshift(b, shift)) e10 -= LIMB_DIGITS;
639 e2 += shift;
641 } else {
642 b->b = 0;
643 b->e = 1;
644 b->size = BNUM_PREC64;
645 b->data[0] = 0;
646 e10 = -LIMB_DIGITS;
649 if(!b->data[bnum_idx(b, b->e-1)])
650 first_limb_len = 1;
651 else
652 first_limb_len = log10i(b->data[bnum_idx(b, b->e - 1)]) + 1;
653 radix_pos = first_limb_len + LIMB_DIGITS + e10;
655 round_pos = flags->Precision;
656 if(flags->Format=='f' || flags->Format=='F')
657 round_pos += radix_pos;
658 else if(!flags->Precision || flags->Format=='e' || flags->Format=='E')
659 round_pos++;
660 if (round_pos <= first_limb_len)
661 round_limb = b->e + (first_limb_len - round_pos) / LIMB_DIGITS - 1;
662 else
663 round_limb = b->e - (round_pos - first_limb_len - 1) / LIMB_DIGITS - 2;
665 if (b->b<=round_limb && round_limb<b->e) {
666 if (round_pos <= first_limb_len) {
667 round_pos = first_limb_len - round_pos;
668 } else {
669 round_pos = LIMB_DIGITS - (round_pos - first_limb_len) % LIMB_DIGITS;
670 if (round_pos == LIMB_DIGITS) round_pos = 0;
673 if (round_pos) {
674 l = b->data[bnum_idx(b, round_limb)] % p10s[round_pos];
675 b->data[bnum_idx(b, round_limb)] -= l;
676 if(!standard_rounding) round_up = (2*l >= p10s[round_pos]);
677 else if(2*l > p10s[round_pos]) round_up = TRUE;
678 else if(2*l == p10s[round_pos]) {
679 for(r = round_limb-1; r >= b->b; r--) {
680 if(b->data[bnum_idx(b, r)]) {
681 round_up = TRUE;
682 break;
686 if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] / p10s[round_pos] & 1;
688 } else if(round_limb - 1 >= b->b) {
689 if(!standard_rounding) round_up = (2*b->data[bnum_idx(b, round_limb-1)] >= LIMB_MAX);
690 else if(2*b->data[bnum_idx(b, round_limb-1)] > LIMB_MAX) round_up = TRUE;
691 else if(2*b->data[bnum_idx(b, round_limb-1)] == LIMB_MAX) {
692 for(r = round_limb-2; r >= b->b; r--) {
693 if(b->data[bnum_idx(b, r)]) {
694 round_up = TRUE;
695 break;
699 if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] & 1;
702 b->b = round_limb;
704 if(round_up) {
705 b->data[bnum_idx(b, b->b)] += p10s[round_pos];
706 for(i = b->b; i < b->e; i++) {
707 if(b->data[bnum_idx(b, i)] < LIMB_MAX) break;
709 b->data[bnum_idx(b, i)] -= LIMB_MAX;
710 if(i+1 < b->e) b->data[bnum_idx(b, i+1)]++;
711 else b->data[bnum_idx(b, i+1)] = 1;
713 if(i == b->e-1) {
714 if(!b->data[bnum_idx(b, b->e-1)])
715 i = 1;
716 else
717 i = log10i(b->data[bnum_idx(b, b->e-1)]) + 1;
718 if(i != first_limb_len) {
719 first_limb_len = i;
720 radix_pos++;
722 round_pos++;
723 if (round_pos == LIMB_DIGITS)
725 round_pos = 0;
726 round_limb++;
729 } else if(i == b->e) {
730 first_limb_len = 1;
731 radix_pos++;
732 b->e++;
734 round_pos++;
735 if (round_pos == LIMB_DIGITS)
737 round_pos = 0;
738 round_limb++;
743 else if(b->e <= round_limb) { /* got 0 or 1 after rounding */
744 if(b->e == round_limb) {
745 if(!standard_rounding) round_up = b->data[bnum_idx(b, b->e-1)] >= LIMB_MAX/2;
746 else if(b->data[bnum_idx(b, b->e-1)] > LIMB_MAX/2) round_up = TRUE;
747 else if(b->data[bnum_idx(b, b->e-1)] == LIMB_MAX/2) {
748 for(r = b->e-2; r >= b->b; r--) {
749 if(b->data[bnum_idx(b, r)]) {
750 round_up = TRUE;
751 break;
757 b->data[bnum_idx(b, round_limb)] = round_up;
758 b->b = round_limb;
759 b->e = b->b + 1;
760 first_limb_len = 1;
761 radix_pos++;
764 if(flags->Format=='g' || flags->Format=='G') {
765 trim_tail = TRUE;
767 if(radix_pos>=-3 && radix_pos<=flags->Precision) {
768 flags->Format -= 1;
769 if(!flags->Precision) flags->Precision++;
770 flags->Precision -= radix_pos;
771 } else {
772 flags->Format -= 2;
773 if(flags->Precision > 0) flags->Precision--;
777 if(trim_tail && !flags->Alternate) {
778 for(i=round_limb; flags->Precision>0 && i<b->e; i++) {
779 if(i>=b->b)
780 l = b->data[bnum_idx(b, i)];
781 else
782 l = 0;
784 if(i == round_limb) {
785 if(flags->Format=='f' || flags->Format=='F')
786 r = radix_pos + flags->Precision;
787 else
788 r = flags->Precision + 1;
789 r = first_limb_len + LIMB_DIGITS * (b->e-1 - b->b) - r;
790 r %= LIMB_DIGITS;
791 if(r < 0) r += LIMB_DIGITS;
792 l /= p10s[r];
793 limb_len = LIMB_DIGITS - r;
794 } else {
795 limb_len = LIMB_DIGITS;
798 if(!l) {
799 flags->Precision -= limb_len;
800 } else {
801 while(l % 10 == 0) {
802 flags->Precision--;
803 l /= 10;
807 if(flags->Precision <= 0) {
808 flags->Precision = 0;
809 break;
811 if(l)
812 break;
816 len = flags->Precision;
817 if(flags->Precision || flags->Alternate) len++;
818 if(flags->Format=='f' || flags->Format=='F') {
819 len += (radix_pos > 0 ? radix_pos : 1);
820 } else if(flags->Format=='e' || flags->Format=='E') {
821 radix_pos--;
822 if(!trim_tail || radix_pos) {
823 len += 3; /* strlen("1e+") */
824 if(three_digit_exp || radix_pos<-99 || radix_pos>99) len += 3;
825 else len += 2;
826 } else {
827 len++;
831 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
832 if(r < 0) return r;
833 ret = r;
835 f.Format = 'd';
836 f.PadZero = TRUE;
837 if(flags->Format=='f' || flags->Format=='F') {
838 if(radix_pos <= 0) {
839 buf[0] = '0';
840 r = pf_puts(puts_ctx, 1, buf);
841 if(r < 0) return r;
842 ret += r;
845 limb_len = LIMB_DIGITS;
846 for(i=b->e-1; radix_pos>0 && i>=b->b; i--) {
847 limb_len = (i == b->e-1 ? first_limb_len : LIMB_DIGITS);
848 l = b->data[bnum_idx(b, i)];
849 if(limb_len > radix_pos) {
850 f.Precision = radix_pos;
851 l /= p10s[limb_len - radix_pos];
852 limb_len = limb_len - radix_pos;
853 } else {
854 f.Precision = limb_len;
855 limb_len = LIMB_DIGITS;
857 radix_pos -= f.Precision;
858 FUNC_NAME(pf_integer_conv)(buf, &f, l);
860 r = pf_puts(puts_ctx, f.Precision, buf);
861 if(r < 0) return r;
862 ret += r;
865 buf[0] = '0';
866 for(; radix_pos>0; radix_pos--) {
867 r = pf_puts(puts_ctx, 1, buf);
868 if(r < 0) return r;
869 ret += r;
872 if(flags->Precision || flags->Alternate) {
873 buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
874 r = pf_puts(puts_ctx, 1, buf);
875 if(r < 0) return r;
876 ret += r;
879 prec = flags->Precision;
880 buf[0] = '0';
881 for(; prec>0 && radix_pos+LIMB_DIGITS-first_limb_len<0; radix_pos++, prec--) {
882 r = pf_puts(puts_ctx, 1, buf);
883 if(r < 0) return r;
884 ret += r;
887 for(; prec>0 && i>=b->b; i--) {
888 l = b->data[bnum_idx(b, i)];
889 if(limb_len != LIMB_DIGITS)
890 l %= p10s[limb_len];
891 if(limb_len > prec) {
892 f.Precision = prec;
893 l /= p10s[limb_len - prec];
894 } else {
895 f.Precision = limb_len;
896 limb_len = LIMB_DIGITS;
898 prec -= f.Precision;
899 FUNC_NAME(pf_integer_conv)(buf, &f, l);
901 r = pf_puts(puts_ctx, f.Precision, buf);
902 if(r < 0) return r;
903 ret += r;
906 buf[0] = '0';
907 for(; prec>0; prec--) {
908 r = pf_puts(puts_ctx, 1, buf);
909 if(r < 0) return r;
910 ret += r;
912 } else {
913 l = b->data[bnum_idx(b, b->e - 1)];
914 l /= p10s[first_limb_len - 1];
916 buf[0] = '0' + l;
917 r = pf_puts(puts_ctx, 1, buf);
918 if(r < 0) return r;
919 ret += r;
921 if(flags->Precision || flags->Alternate) {
922 buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
923 r = pf_puts(puts_ctx, 1, buf);
924 if(r < 0) return r;
925 ret += r;
928 prec = flags->Precision;
929 limb_len = LIMB_DIGITS;
930 for(i=b->e-1; prec>0 && i>=b->b; i--) {
931 l = b->data[bnum_idx(b, i)];
932 if(i == b->e-1) {
933 limb_len = first_limb_len - 1;
934 l %= p10s[limb_len];
937 if(limb_len > prec) {
938 f.Precision = prec;
939 l /= p10s[limb_len - prec];
940 } else {
941 f.Precision = limb_len;
942 limb_len = LIMB_DIGITS;
944 prec -= f.Precision;
945 FUNC_NAME(pf_integer_conv)(buf, &f, l);
947 r = pf_puts(puts_ctx, f.Precision, buf);
948 if(r < 0) return r;
949 ret += r;
952 buf[0] = '0';
953 for(; prec>0; prec--) {
954 r = pf_puts(puts_ctx, 1, buf);
955 if(r < 0) return r;
956 ret += r;
959 if(!trim_tail || radix_pos) {
960 buf[0] = flags->Format;
961 buf[1] = radix_pos < 0 ? '-' : '+';
962 r = pf_puts(puts_ctx, 2, buf);
963 if(r < 0) return r;
964 ret += r;
966 f.Precision = three_digit_exp ? 3 : 2;
967 FUNC_NAME(pf_integer_conv)(buf, &f, radix_pos);
968 r = pf_puts(puts_ctx, f.Precision, buf);
969 if(r < 0) return r;
970 ret += r;
974 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
975 if(r < 0) return r;
976 ret += r;
977 return ret;
980 int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
981 _locale_t locale, DWORD options,
982 args_clbk pf_args, void *args_ctx, va_list *valist)
984 const APICHAR *q, *p = fmt;
985 APICHAR buf[32];
986 int written = 0, pos, i;
987 pf_flags flags;
988 BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
989 BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER;
990 #if _MSVCR_VER >= 140
991 BOOL legacy_wide = options & _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS;
992 BOOL legacy_msvcrt_compat = options & _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
993 BOOL three_digit_exp = options & _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS;
994 BOOL standard_rounding = options & _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING;
995 #else
996 BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE;
997 BOOL three_digit_exp = _get_output_format() != _TWO_DIGIT_EXPONENT;
998 BOOL standard_rounding = FALSE;
999 #endif
1001 if (!MSVCRT_CHECK_PMT(fmt != NULL))
1002 return -1;
1004 while(*p) {
1005 /* output characters before '%' */
1006 for(q=p; *q && *q!='%'; q++);
1007 if(p != q) {
1008 i = pf_puts(puts_ctx, q-p, p);
1009 if(i < 0)
1010 return i;
1012 written += i;
1013 p = q;
1014 continue;
1017 /* *p == '%' here */
1018 p++;
1020 /* output a single '%' character */
1021 if(*p == '%') {
1022 i = pf_puts(puts_ctx, 1, p++);
1023 if(i < 0)
1024 return i;
1026 written += i;
1027 continue;
1030 /* check parameter position */
1031 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
1032 p = q+1;
1033 else
1034 pos = -1;
1036 /* parse the flags */
1037 memset(&flags, 0, sizeof(flags));
1038 while(*p) {
1039 if(*p=='+' || *p==' ') {
1040 if(flags.Sign != '+')
1041 flags.Sign = *p;
1042 } else if(*p == '-')
1043 flags.LeftAlign = TRUE;
1044 else if(*p == '0')
1045 flags.PadZero = TRUE;
1046 else if(*p == '#')
1047 flags.Alternate = TRUE;
1048 else
1049 break;
1051 p++;
1054 /* parse the width */
1055 if(*p == '*') {
1056 p++;
1057 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
1058 p = q+1;
1059 else
1060 i = -1;
1062 flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
1063 if(flags.FieldLength < 0) {
1064 flags.LeftAlign = TRUE;
1065 flags.FieldLength = -flags.FieldLength;
1069 #if _MSVCR_VER >= 140
1070 if (*p >= '0' && *p <= '9')
1071 flags.FieldLength = 0;
1072 #endif
1074 while (*p >= '0' && *p <= '9') {
1075 flags.FieldLength *= 10;
1076 flags.FieldLength += *p++ - '0';
1079 /* parse the precision */
1080 flags.Precision = -1;
1081 if(*p == '.') {
1082 flags.Precision = 0;
1083 p++;
1084 if(*p == '*') {
1085 p++;
1086 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
1087 p = q+1;
1088 else
1089 i = -1;
1091 flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
1092 } else while (*p >= '0' && *p <= '9') {
1093 flags.Precision *= 10;
1094 flags.Precision += *p++ - '0';
1098 /* parse argument size modifier */
1099 while(*p) {
1100 if(*p=='l' && *(p+1)=='l') {
1101 flags.IntegerDouble = TRUE;
1102 p++;
1103 } else if(*p=='l') {
1104 flags.IntegerLength = LEN_LONG;
1105 } else if(*p == 'h') {
1106 flags.IntegerLength = LEN_SHORT;
1107 } else if(*p == 'I') {
1108 if(*(p+1)=='6' && *(p+2)=='4') {
1109 flags.IntegerDouble = TRUE;
1110 p += 2;
1111 } else if(*(p+1)=='3' && *(p+2)=='2')
1112 p += 2;
1113 else if(p[1] && strchr("diouxX", p[1]))
1114 flags.IntegerNative = TRUE;
1115 else
1116 break;
1117 } else if(*p == 'w')
1118 flags.WideString = TRUE;
1119 #if _MSVCR_VER == 0 || _MSVCR_VER >= 140
1120 else if((*p == 'z' || *p == 't') && p[1] && strchr("diouxX", p[1]))
1121 flags.IntegerNative = TRUE;
1122 else if(*p == 'j')
1123 flags.IntegerDouble = TRUE;
1124 #endif
1125 #if _MSVCR_VER >= 140
1126 else if(*p == 'T')
1127 flags.NaturalString = TRUE;
1128 #endif
1129 else if(*p != 'L' && ((*p != 'F' && *p != 'N') || !legacy_msvcrt_compat))
1130 break;
1131 p++;
1134 flags.Format = *p;
1136 if(flags.Format == 's' || flags.Format == 'S') {
1137 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
1138 pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
1139 -1, &flags, locale, legacy_wide);
1140 } else if(flags.Format == 'c' || flags.Format == 'C') {
1141 int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
1143 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locale, legacy_wide);
1144 if(i < 0) i = 0; /* ignore conversion error */
1145 } else if(flags.Format == 'p') {
1146 flags.Format = 'X';
1147 flags.PadZero = TRUE;
1148 i = flags.Precision;
1149 flags.Precision = 2*sizeof(void*);
1150 FUNC_NAME(pf_integer_conv)(buf, &flags,
1151 (ULONG_PTR)pf_args(args_ctx, pos, VT_PTR, valist).get_ptr);
1152 flags.PadZero = FALSE;
1153 flags.Precision = i;
1155 #ifdef PRINTF_WIDE
1156 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locale);
1157 #else
1158 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locale);
1159 #endif
1160 } else if(flags.Format == 'n') {
1161 int *used;
1163 if(!n_format_enabled) {
1164 MSVCRT_INVALID_PMT("\'n\' format specifier disabled", EINVAL);
1165 return -1;
1168 used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
1169 *used = written;
1170 i = 0;
1171 } else if(flags.Format && strchr("diouxX", flags.Format)) {
1172 APICHAR *tmp = buf;
1173 int max_len;
1175 /* 0 padding is added after '0x' if Alternate flag is in use */
1176 if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
1177 && !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
1178 flags.Precision = flags.FieldLength - 2;
1180 max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
1181 if(max_len > ARRAY_SIZE(buf))
1182 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
1183 if(!tmp)
1184 return -1;
1186 if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8))
1187 FUNC_NAME(pf_integer_conv)(tmp, &flags, pf_args(args_ctx, pos,
1188 VT_I8, valist).get_longlong);
1189 else if(flags.Format=='d' || flags.Format=='i')
1190 FUNC_NAME(pf_integer_conv)(tmp, &flags,
1191 flags.IntegerLength != LEN_SHORT ?
1192 pf_args(args_ctx, pos, VT_INT, valist).get_int :
1193 (short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
1194 else
1195 FUNC_NAME(pf_integer_conv)(tmp, &flags,
1196 flags.IntegerLength != LEN_SHORT ?
1197 (unsigned)pf_args(args_ctx, pos, VT_INT, valist).get_int :
1198 (unsigned short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
1200 #ifdef PRINTF_WIDE
1201 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
1202 #else
1203 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
1204 #endif
1205 if(tmp != buf)
1206 HeapFree(GetProcessHeap(), 0, tmp);
1207 } else if(flags.Format && strchr("aAeEfFgG", flags.Format)) {
1208 double val = pf_args(args_ctx, pos, VT_R8, valist).get_double;
1210 if(signbit(val)) {
1211 flags.Sign = '-';
1212 val = -val;
1215 if(isinf(val) || isnan(val))
1216 i = FUNC_NAME(pf_output_special_fp)(pf_puts, puts_ctx, val, &flags,
1217 locale, legacy_msvcrt_compat, three_digit_exp);
1218 else if(flags.Format=='a' || flags.Format=='A')
1219 i = FUNC_NAME(pf_output_hex_fp)(pf_puts, puts_ctx, val, &flags,
1220 locale, standard_rounding);
1221 else
1222 i = FUNC_NAME(pf_output_fp)(pf_puts, puts_ctx, val, &flags,
1223 locale, three_digit_exp, standard_rounding);
1224 } else {
1225 if(invoke_invalid_param_handler) {
1226 _invalid_parameter(NULL, NULL, NULL, 0, 0);
1227 *_errno() = EINVAL;
1228 return -1;
1231 continue;
1234 if(i < 0)
1235 return i;
1236 written += i;
1237 p++;
1240 return written;
1243 #ifndef PRINTF_WIDE
1244 enum types_clbk_flags {
1245 TYPE_CLBK_VA_LIST = 1,
1246 TYPE_CLBK_POSITIONAL = 2,
1247 TYPE_CLBK_ERROR_POS = 4,
1248 TYPE_CLBK_ERROR_TYPE = 8
1251 /* This functions stores types of arguments. It uses args[0] internally */
1252 static printf_arg arg_clbk_type(void *ctx, int pos, int type, va_list *valist)
1254 static const printf_arg ret;
1255 printf_arg *args = ctx;
1257 if(pos == -1) {
1258 args[0].get_int |= TYPE_CLBK_VA_LIST;
1259 return ret;
1260 } else
1261 args[0].get_int |= TYPE_CLBK_POSITIONAL;
1263 if(pos<1 || pos>_ARGMAX)
1264 args[0].get_int |= TYPE_CLBK_ERROR_POS;
1265 else if(args[pos].get_int && args[pos].get_int!=type)
1266 args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
1267 else
1268 args[pos].get_int = type;
1270 return ret;
1272 #endif
1274 int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, va_list valist)
1276 struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
1277 printf_arg *args = args_ctx;
1278 int i, j;
1280 i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL,
1281 MSVCRT_PRINTF_POSITIONAL_PARAMS, arg_clbk_type, args_ctx, NULL);
1282 if(i < 0)
1283 return i;
1285 if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
1286 return 0;
1287 if(args[0].get_int != TYPE_CLBK_POSITIONAL)
1288 return -1;
1290 for(i=_ARGMAX; i>0; i--)
1291 if(args[i].get_int)
1292 break;
1294 for(j=1; j<=i; j++) {
1295 switch(args[j].get_int) {
1296 case VT_I8:
1297 args[j].get_longlong = va_arg(valist, LONGLONG);
1298 break;
1299 case VT_INT:
1300 args[j].get_int = va_arg(valist, int);
1301 break;
1302 case VT_R8:
1303 args[j].get_double = va_arg(valist, double);
1304 break;
1305 case VT_PTR:
1306 args[j].get_ptr = va_arg(valist, void*);
1307 break;
1308 default:
1309 return -1;
1313 return j;
1316 #undef APICHAR
1317 #undef CONVCHAR
1318 #undef FUNC_NAME