msvcrt: Use the msvcrt ctype functions internally.
[wine.git] / dlls / msvcrt / printf.h
blobf8b200c7e54f74b53807b091670fd69471e7c219
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 MSVCRT_wchar_t
23 #define CONVCHAR char
24 #define FUNC_NAME(func) func ## _w
25 #else
26 #define APICHAR char
27 #define CONVCHAR MSVCRT_wchar_t
28 #define FUNC_NAME(func) func ## _a
29 #endif
31 struct FUNC_NAME(_str_ctx) {
32 MSVCRT_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 MSVCRT_wchar_t *wcstr,
112 int len, MSVCRT__locale_t locale)
114 char buf[MSVCRT_MB_LEN_MAX];
115 int i, r, mblen = 0;
117 for(i=0; i<len; i++) {
118 r = MSVCRT__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(MSVCRT_wchar_t *wcstr, const char *mbstr,
126 int len, MSVCRT__locale_t locale)
128 int i, r, wlen = 0;
129 WCHAR buf;
131 for(i=0; i<len; wlen++) {
132 r = MSVCRT_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;
138 #endif
140 static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
141 const MSVCRT_wchar_t *str, int len, MSVCRT__locale_t locale)
143 #ifdef PRINTF_WIDE
144 return pf_puts(puts_ctx, len, str);
145 #else
146 LPSTR out;
147 int len_a = wcstombs_len(NULL, str, len, locale);
148 if(len_a < 0)
149 return -1;
151 out = HeapAlloc(GetProcessHeap(), 0, len_a);
152 if(!out)
153 return -1;
155 wcstombs_len(out, str, len, locale);
156 len = pf_puts(puts_ctx, len_a, out);
157 HeapFree(GetProcessHeap(), 0, out);
158 return len;
159 #endif
162 static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
163 const char *str, int len, MSVCRT__locale_t locale)
165 #ifdef PRINTF_WIDE
166 LPWSTR out;
167 int len_w = mbstowcs_len(NULL, str, len, locale);
168 if(len_w < 0)
169 return -1;
171 out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
172 if(!out)
173 return -1;
175 mbstowcs_len(out, str, len, locale);
176 len = pf_puts(puts_ctx, len_w, out);
177 HeapFree(GetProcessHeap(), 0, out);
178 return len;
179 #else
180 return pf_puts(puts_ctx, len, str);
181 #endif
184 static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
185 const MSVCRT_wchar_t *str, int len, pf_flags *flags, MSVCRT__locale_t locale)
187 int r, ret;
189 if(len < 0) {
190 /* Do not search past the length specified by the precision. */
191 if(flags->Precision>=0)
192 len = MSVCRT_wcsnlen(str, flags->Precision);
193 else
194 len = MSVCRT_wcslen(str);
197 if(flags->Precision>=0 && flags->Precision<len)
198 len = flags->Precision;
200 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
201 ret = r;
202 if(r >= 0) {
203 r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locale);
204 ret += r;
206 if(r >= 0) {
207 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
208 ret += r;
211 return r>=0 ? ret : r;
214 static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
215 const char *str, int len, pf_flags *flags, MSVCRT__locale_t locale)
217 int r, ret;
219 if(len < 0) {
220 /* Do not search past the length specified by the precision. */
221 if(flags->Precision>=0)
222 len = MSVCRT_strnlen(str, flags->Precision);
223 else
224 len = strlen(str);
227 if(flags->Precision>=0 && flags->Precision<len)
228 len = flags->Precision;
230 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
231 ret = r;
232 if(r >= 0) {
233 r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locale);
234 ret += r;
236 if(r >= 0) {
237 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
238 ret += r;
241 return r>=0 ? ret : r;
244 static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
245 const void *str, int len, pf_flags *flags, MSVCRT__locale_t locale, BOOL legacy_wide)
247 BOOL api_is_wide = sizeof(APICHAR) == sizeof(MSVCRT_wchar_t);
248 BOOL complement_is_narrow = legacy_wide ? api_is_wide : FALSE;
249 #ifdef PRINTF_WIDE
250 static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
252 if(!str)
253 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, nullW, 6, flags, locale);
254 #else
255 if(!str)
256 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locale);
257 #endif
259 if((flags->NaturalString && api_is_wide) || flags->WideString || flags->IntegerLength == LEN_LONG)
260 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
261 if((flags->NaturalString && !api_is_wide) || flags->IntegerLength == LEN_SHORT)
262 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
264 if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow)
265 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
266 else
267 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
270 static inline int FUNC_NAME(pf_output_special_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
271 double v, pf_flags *flags, MSVCRT__locale_t locale,
272 BOOL legacy_msvcrt_compat, BOOL three_digit_exp)
274 APICHAR pfx[16], sfx[8], *p;
275 int len = 0, r, frac_len, pfx_len, sfx_len;
277 if(!legacy_msvcrt_compat) {
278 const char *str;
280 if(isinf(v)) {
281 if(strchr("AEFG", flags->Format)) str = "INF";
282 else str = "inf";
283 }else {
284 if(strchr("AEFG", flags->Format)) str = (flags->Sign == '-' ? "NAN(IND)" : "NAN");
285 else str = (flags->Sign == '-' ? "nan(ind)" : "nan");
288 flags->Precision = -1;
289 flags->PadZero = FALSE;
290 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, -1, flags, locale);
293 /* workaround a bug in native implementation */
294 if(flags->Format=='g' || flags->Format=='G')
295 flags->Precision--;
297 p = pfx;
298 if(flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
299 if (flags->Sign) *p++ = flags->Sign;
300 *p++ = '0';
301 *p++ = (flags->Format=='a' ? 'x' : 'X');
302 r = pf_puts(puts_ctx, p-pfx, pfx);
303 if(r < 0) return r;
304 len += r;
306 flags->FieldLength -= p-pfx;
309 p = pfx;
310 if(!flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
311 *p++ = '0';
312 *p++ = (flags->Format=='a' ? 'x' : 'X');
315 *p++ = '1';
316 *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
317 *p++ = '#';
318 frac_len = 1;
320 if(isinf(v)) {
321 *p++ = 'I';
322 *p++ = 'N';
323 *p++ = 'F';
324 frac_len += 3;
325 }else if(flags->Sign == '-') {
326 *p++ = 'I';
327 *p++ = 'N';
328 *p++ = 'D';
329 frac_len += 3;
330 }else {
331 *p++ = 'Q';
332 *p++ = 'N';
333 *p++ = 'A';
334 *p++ = 'N';
335 frac_len += 4;
337 *p = 0;
338 pfx_len = p - pfx;
340 if(len) flags->Sign = 0;
342 if(flags->Precision>=0 && flags->Precision<frac_len)
343 p[flags->Precision - frac_len - 1]++;
345 p = sfx;
346 if(strchr("aAeE", flags->Format)) {
347 if(flags->Format == 'a') *p++ = 'p';
348 else if(flags->Format == 'A') *p++ = 'P';
349 else if(flags->Format == 'e') *p++ = 'e';
350 else *p++ = 'E';
352 *p++ = '+';
353 *p++ = '0';
355 if(flags->Format == 'e' || flags->Format == 'E') {
356 *p++ = '0';
357 if(three_digit_exp) *p++ = '0';
360 *p = 0;
362 if(!flags->Alternate && (flags->Format == 'g' || flags->Format == 'G')) sfx_len = frac_len;
363 else sfx_len = flags->Precision;
365 if(sfx_len == -1) {
366 if(strchr("fFeE", flags->Format)) sfx_len = 6;
367 else if(flags->Format == 'a' || flags->Format == 'A') sfx_len = 13;
369 sfx_len += p - sfx - frac_len;
371 if(sfx_len > 0) flags->FieldLength -= sfx_len;
372 if(flags->Precision >= 0) {
373 if(!flags->Precision) flags->Precision--;
374 flags->Precision += pfx_len - frac_len;
376 #ifdef PRINTF_WIDE
377 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
378 #else
379 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
380 #endif
381 if(r < 0) return r;
382 len += r;
384 flags->FieldLength = sfx_len;
385 flags->PadZero = TRUE;
386 flags->Precision = -1;
387 flags->Sign = 0;
388 #ifdef PRINTF_WIDE
389 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
390 #else
391 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
392 #endif
393 if(r < 0) return r;
394 len += r;
396 return len;
399 static inline int FUNC_NAME(pf_output_hex_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
400 double v, pf_flags *flags, MSVCRT__locale_t locale, BOOL standard_rounding)
402 const APICHAR digits[2][16] = {
403 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
404 { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }
407 APICHAR pfx[4+MANT_BITS/4+1], sfx[8], *p;
408 ULONGLONG mant;
409 int len = 0, sfx_len = 0, r, exp;
411 mant = (*(ULONGLONG*)&v) << 1;
412 exp = (mant >> MANT_BITS);
413 exp -= (1 << (EXP_BITS - 1)) - 1;
414 mant = (mant << EXP_BITS) >> (EXP_BITS+1);
416 p = pfx;
417 if(flags->PadZero) {
418 if(flags->Sign) *p++ = flags->Sign;
419 *p++ = '0';
420 *p++ = (flags->Format=='a' ? 'x' : 'X');
421 r = pf_puts(puts_ctx, p-pfx, pfx);
422 if(r < 0) return r;
423 len += r;
425 flags->FieldLength -= p-pfx;
426 flags->Sign = 0;
427 p = pfx;
428 }else {
429 *p++ = '0';
430 *p++ = (flags->Format=='a' ? 'x' : 'X');
432 if(exp == -(1 << (EXP_BITS-1))+1) {
433 if(!mant) exp = 0;
434 else exp++;
435 *p++ = '0';
436 }else {
437 *p++ = '1';
439 *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
440 for(r=MANT_BITS/4-1; r>=0; r--) {
441 p[r] = digits[flags->Format == 'A'][mant & 15];
442 mant >>= 4;
444 if(!flags->Precision) {
445 if(p[0] >= '8') p[-2]++;
446 if(!flags->Alternate) p--;
447 }else if(flags->Precision>0 && flags->Precision<MANT_BITS/4) {
448 BOOL round_up = FALSE;
450 if(!standard_rounding) round_up = (p[flags->Precision] >= '8');
451 else if(p[flags->Precision] > '8') round_up = TRUE;
452 else if(p[flags->Precision] == '8') {
453 for(r = flags->Precision+1; r<MANT_BITS/4; r++) {
454 if(p[r] != '0') {
455 round_up = TRUE;
456 break;
460 if(!round_up) {
461 if(p[flags->Precision-1] <= '9') round_up = (p[flags->Precision-1] - '0') & 1;
462 else if(p[flags->Precision-1] <= 'F') round_up = (p[flags->Precision-1] - 'A') & 1;
463 else round_up = (p[flags->Precision-1] - 'a') & 1;
467 for(r=flags->Precision-1; r>=0 && round_up; r--) {
468 round_up = FALSE;
469 if(p[r]=='f' || p[r]=='F') {
470 p[r] = '0';
471 round_up = TRUE;
472 }else if(p[r] == '9') {
473 p[r] = (flags->Format == 'a' ? 'a' : 'A');
474 }else {
475 p[r]++;
478 if(round_up) p[-2]++;
479 p += flags->Precision;
480 }else {
481 p += MANT_BITS/4;
482 if(flags->Precision > MANT_BITS/4) sfx_len += flags->Precision - MANT_BITS/4;
484 *p = 0;
486 p = sfx;
487 *p++ = (flags->Format == 'a' ? 'p' : 'P');
488 if(exp < 0) {
489 *p++ = '-';
490 exp = -exp;
491 }else {
492 *p++ = '+';
494 for(r=3; r>=0; r--) {
495 p[r] = exp%10 + '0';
496 exp /= 10;
497 if(!exp) break;
499 for(exp=0; exp<4-r; exp++)
500 p[exp] = p[exp+r];
501 p += exp;
502 *p = 0;
503 sfx_len += p - sfx;
505 flags->FieldLength -= sfx_len;
506 flags->Precision = -1;
507 #ifdef PRINTF_WIDE
508 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
509 #else
510 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
511 #endif
512 if(r < 0) return r;
513 len += r;
515 flags->FieldLength = sfx_len;
516 flags->PadZero = TRUE;
517 flags->Sign = 0;
518 #ifdef PRINTF_WIDE
519 r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
520 #else
521 r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
522 #endif
523 if(r < 0) return r;
524 len += r;
526 return len;
529 /* pf_integer_conv: prints x to buf, including alternate formats and
530 additional precision digits, but not field characters or the sign */
531 static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, pf_flags *flags, LONGLONG x)
533 unsigned int base;
534 const char *digits;
535 int i, j, k;
537 if(flags->Format == 'o')
538 base = 8;
539 else if(flags->Format=='x' || flags->Format=='X')
540 base = 16;
541 else
542 base = 10;
544 if(flags->Format == 'X')
545 digits = "0123456789ABCDEFX";
546 else
547 digits = "0123456789abcdefx";
549 if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
550 x = -x;
551 flags->Sign = '-';
554 i = 0;
555 if(x == 0) {
556 flags->Alternate = FALSE;
557 if(flags->Precision)
558 buf[i++] = '0';
559 } else {
560 while(x != 0) {
561 j = (ULONGLONG)x%base;
562 x = (ULONGLONG)x/base;
563 buf[i++] = digits[j];
566 k = flags->Precision-i;
567 while(k-- > 0)
568 buf[i++] = '0';
569 if(flags->Alternate) {
570 if(base == 16) {
571 buf[i++] = digits[16];
572 buf[i++] = '0';
573 } else if(base==8 && buf[i-1]!='0')
574 buf[i++] = '0';
577 /* Adjust precision so pf_fill won't truncate the number later */
578 flags->Precision = i;
580 buf[i] = '\0';
581 j = 0;
582 while(--i > j) {
583 APICHAR tmp = buf[j];
584 buf[j] = buf[i];
585 buf[i] = tmp;
586 j++;
590 static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
591 double v, pf_flags *flags, MSVCRT__locale_t locale, BOOL three_digit_exp,
592 BOOL standard_rounding)
594 int e2, e10 = 0, round_pos, round_limb, radix_pos, first_limb_len, i, len, r, ret;
595 BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])];
596 struct bnum *b = (struct bnum*)bnum_data;
597 APICHAR buf[LIMB_DIGITS + 1];
598 BOOL trim_tail = FALSE, round_up = FALSE;
599 pf_flags f;
600 int limb_len, prec;
601 ULONGLONG m;
602 DWORD l;
604 TRACE("floating point argument: %.16le\n", v);
606 if(flags->Precision == -1)
607 flags->Precision = 6;
609 v = MSVCRT_frexp(v, &e2);
610 if(v) {
611 m = (ULONGLONG)1 << (MANT_BITS - 1);
612 m |= (*(ULONGLONG*)&v & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1));
613 b->b = 0;
614 b->e = 2;
615 b->size = BNUM_PREC64;
616 b->data[0] = m % LIMB_MAX;
617 b->data[1] = m / LIMB_MAX;
618 e2 -= MANT_BITS;
620 while(e2 > 0) {
621 int shift = e2 > 29 ? 29 : e2;
622 if(bnum_lshift(b, shift)) e10 += LIMB_DIGITS;
623 e2 -= shift;
625 while(e2 < 0) {
626 int shift = -e2 > 9 ? 9 : -e2;
627 if(bnum_rshift(b, shift)) e10 -= LIMB_DIGITS;
628 e2 += shift;
630 } else {
631 b->b = 0;
632 b->e = 1;
633 b->size = BNUM_PREC64;
634 b->data[0] = 0;
635 e10 = -LIMB_DIGITS;
638 if(!b->data[bnum_idx(b, b->e-1)])
639 first_limb_len = 1;
640 else
641 first_limb_len = MSVCRT_floor(MSVCRT_log10(b->data[bnum_idx(b, b->e - 1)])) + 1;
642 radix_pos = first_limb_len + LIMB_DIGITS + e10;
644 round_pos = flags->Precision;
645 if(flags->Format=='f' || flags->Format=='F')
646 round_pos += radix_pos;
647 else if(!flags->Precision || flags->Format=='e' || flags->Format=='E')
648 round_pos++;
649 if (round_pos <= first_limb_len)
650 round_limb = b->e + (first_limb_len - round_pos) / LIMB_DIGITS - 1;
651 else
652 round_limb = b->e - (round_pos - first_limb_len - 1) / LIMB_DIGITS - 2;
654 if (b->b<=round_limb && round_limb<b->e) {
655 if (round_pos <= first_limb_len) {
656 round_pos = first_limb_len - round_pos;
657 } else {
658 round_pos = LIMB_DIGITS - (round_pos - first_limb_len) % LIMB_DIGITS;
659 if (round_pos == LIMB_DIGITS) round_pos = 0;
662 if (round_pos) {
663 l = b->data[bnum_idx(b, round_limb)] % p10s[round_pos];
664 b->data[bnum_idx(b, round_limb)] -= l;
665 if(!standard_rounding) round_up = (2*l >= p10s[round_pos]);
666 else if(2*l > p10s[round_pos]) round_up = TRUE;
667 else if(2*l == p10s[round_pos]) {
668 for(r = round_limb-1; r >= b->b; r--) {
669 if(b->data[bnum_idx(b, r)]) {
670 round_up = TRUE;
671 break;
675 if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] / p10s[round_pos] & 1;
677 } else if(round_limb - 1 >= b->b) {
678 if(!standard_rounding) round_up = (2*b->data[bnum_idx(b, round_limb-1)] >= LIMB_MAX);
679 else if(2*b->data[bnum_idx(b, round_limb-1)] > LIMB_MAX) round_up = TRUE;
680 else if(2*b->data[bnum_idx(b, round_limb-1)] == LIMB_MAX) {
681 for(r = round_limb-2; r >= b->b; r--) {
682 if(b->data[bnum_idx(b, r)]) {
683 round_up = TRUE;
684 break;
688 if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] & 1;
691 b->b = round_limb;
693 if(round_up) {
694 b->data[bnum_idx(b, b->b)] += p10s[round_pos];
695 for(i = b->b; i < b->e; i++) {
696 if(b->data[bnum_idx(b, i)] < LIMB_MAX) break;
698 b->data[bnum_idx(b, i)] -= LIMB_MAX;
699 if(i+1 < b->e) b->data[bnum_idx(b, i+1)]++;
700 else b->data[bnum_idx(b, i+1)] = 1;
702 if(i == b->e-1) {
703 if(!b->data[bnum_idx(b, b->e-1)])
704 i = 1;
705 else
706 i = MSVCRT_floor(MSVCRT_log10(b->data[bnum_idx(b, b->e-1)])) + 1;
707 if(i != first_limb_len) {
708 first_limb_len = i;
709 radix_pos++;
711 } else if(i == b->e) {
712 first_limb_len = 1;
713 radix_pos++;
714 b->e++;
718 else if(b->e <= round_limb) { /* got 0 or 1 after rounding */
719 if(b->e == round_limb) {
720 if(!standard_rounding) round_up = b->data[bnum_idx(b, b->e-1)] >= LIMB_MAX/2;
721 else if(b->data[bnum_idx(b, b->e-1)] > LIMB_MAX/2) round_up = TRUE;
722 else if(b->data[bnum_idx(b, b->e-1)] == LIMB_MAX/2) {
723 for(r = b->e-2; r >= b->b; r--) {
724 if(b->data[bnum_idx(b, r)]) {
725 round_up = TRUE;
726 break;
732 b->data[bnum_idx(b, round_limb)] = round_up;
733 b->b = round_limb;
734 b->e = b->b + 1;
735 first_limb_len = 1;
736 radix_pos++;
739 if(flags->Format=='g' || flags->Format=='G') {
740 trim_tail = TRUE;
742 if(radix_pos>=-3 && radix_pos<=flags->Precision) {
743 flags->Format -= 1;
744 if(!flags->Precision) flags->Precision++;
745 flags->Precision -= radix_pos;
746 } else {
747 flags->Format -= 2;
748 if(flags->Precision > 0) flags->Precision--;
752 if(trim_tail && !flags->Alternate) {
753 for(i=round_limb; flags->Precision>0 && i<b->e; i++) {
754 if(i>=b->b)
755 l = b->data[bnum_idx(b, i)];
756 else
757 l = 0;
759 if(i == round_limb) {
760 if(flags->Format=='f' || flags->Format=='F')
761 r = radix_pos + flags->Precision;
762 else
763 r = flags->Precision + 1;
764 r = first_limb_len + LIMB_DIGITS * (b->e-1 - b->b) - r;
765 r %= LIMB_DIGITS;
766 if(r < 0) r += LIMB_DIGITS;
767 l /= p10s[r];
768 limb_len = LIMB_DIGITS - r;
769 } else {
770 limb_len = LIMB_DIGITS;
773 if(!l) {
774 flags->Precision -= limb_len;
775 } else {
776 while(l % 10 == 0) {
777 flags->Precision--;
778 l /= 10;
782 if(flags->Precision <= 0) {
783 flags->Precision = 0;
784 break;
786 if(l)
787 break;
791 len = flags->Precision;
792 if(flags->Precision || flags->Alternate) len++;
793 if(flags->Format=='f' || flags->Format=='F') {
794 len += (radix_pos > 0 ? radix_pos : 1);
795 } else if(flags->Format=='e' || flags->Format=='E') {
796 radix_pos--;
797 if(!trim_tail || radix_pos) {
798 len += 3; /* strlen("1e+") */
799 if(three_digit_exp || radix_pos<-99 || radix_pos>99) len += 3;
800 else len += 2;
801 } else {
802 len++;
806 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
807 if(r < 0) return r;
808 ret = r;
810 f.Format = 'd';
811 f.PadZero = TRUE;
812 if(flags->Format=='f' || flags->Format=='F') {
813 if(radix_pos <= 0) {
814 buf[0] = '0';
815 r = pf_puts(puts_ctx, 1, buf);
816 if(r < 0) return r;
817 ret += r;
820 limb_len = LIMB_DIGITS;
821 for(i=b->e-1; radix_pos>0 && i>=b->b; i--) {
822 limb_len = (i == b->e-1 ? first_limb_len : LIMB_DIGITS);
823 l = b->data[bnum_idx(b, i)];
824 if(limb_len > radix_pos) {
825 f.Precision = radix_pos;
826 l /= p10s[limb_len - radix_pos];
827 limb_len = limb_len - radix_pos;
828 } else {
829 f.Precision = limb_len;
830 limb_len = LIMB_DIGITS;
832 radix_pos -= f.Precision;
833 FUNC_NAME(pf_integer_conv)(buf, &f, l);
835 r = pf_puts(puts_ctx, f.Precision, buf);
836 if(r < 0) return r;
837 ret += r;
840 buf[0] = '0';
841 for(; radix_pos>0; radix_pos--) {
842 r = pf_puts(puts_ctx, 1, buf);
843 if(r < 0) return r;
844 ret += r;
847 if(flags->Precision || flags->Alternate) {
848 buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
849 r = pf_puts(puts_ctx, 1, buf);
850 if(r < 0) return r;
851 ret += r;
854 prec = flags->Precision;
855 buf[0] = '0';
856 for(; prec>0 && radix_pos+LIMB_DIGITS-first_limb_len<0; radix_pos++, prec--) {
857 r = pf_puts(puts_ctx, 1, buf);
858 if(r < 0) return r;
859 ret += r;
862 for(; prec>0 && i>=b->b; i--) {
863 l = b->data[bnum_idx(b, i)];
864 if(limb_len != LIMB_DIGITS)
865 l %= p10s[limb_len];
866 if(limb_len > prec) {
867 f.Precision = prec;
868 l /= p10s[limb_len - prec];
869 } else {
870 f.Precision = limb_len;
871 limb_len = LIMB_DIGITS;
873 prec -= f.Precision;
874 FUNC_NAME(pf_integer_conv)(buf, &f, l);
876 r = pf_puts(puts_ctx, f.Precision, buf);
877 if(r < 0) return r;
878 ret += r;
881 buf[0] = '0';
882 for(; prec>0; prec--) {
883 r = pf_puts(puts_ctx, 1, buf);
884 if(r < 0) return r;
885 ret += r;
887 } else {
888 l = b->data[bnum_idx(b, b->e - 1)];
889 l /= p10s[first_limb_len - 1];
891 buf[0] = '0' + l;
892 r = pf_puts(puts_ctx, 1, buf);
893 if(r < 0) return r;
894 ret += r;
896 if(flags->Precision || flags->Alternate) {
897 buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
898 r = pf_puts(puts_ctx, 1, buf);
899 if(r < 0) return r;
900 ret += r;
903 prec = flags->Precision;
904 limb_len = LIMB_DIGITS;
905 for(i=b->e-1; prec>0 && i>=b->b; i--) {
906 l = b->data[bnum_idx(b, i)];
907 if(i == b->e-1) {
908 limb_len = first_limb_len - 1;
909 l %= p10s[limb_len];
912 if(limb_len > prec) {
913 f.Precision = prec;
914 l /= p10s[limb_len - prec];
915 } else {
916 f.Precision = limb_len;
917 limb_len = LIMB_DIGITS;
919 prec -= f.Precision;
920 FUNC_NAME(pf_integer_conv)(buf, &f, l);
922 r = pf_puts(puts_ctx, f.Precision, buf);
923 if(r < 0) return r;
924 ret += r;
927 buf[0] = '0';
928 for(; prec>0; prec--) {
929 r = pf_puts(puts_ctx, 1, buf);
930 if(r < 0) return r;
931 ret += r;
934 if(!trim_tail || radix_pos) {
935 buf[0] = flags->Format;
936 buf[1] = radix_pos < 0 ? '-' : '+';
937 r = pf_puts(puts_ctx, 2, buf);
938 if(r < 0) return r;
939 ret += r;
941 f.Precision = three_digit_exp ? 3 : 2;
942 FUNC_NAME(pf_integer_conv)(buf, &f, radix_pos);
943 r = pf_puts(puts_ctx, f.Precision, buf);
944 if(r < 0) return r;
945 ret += r;
949 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
950 if(r < 0) return r;
951 ret += r;
952 return ret;
955 int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
956 MSVCRT__locale_t locale, DWORD options,
957 args_clbk pf_args, void *args_ctx, __ms_va_list *valist)
959 const APICHAR *q, *p = fmt;
960 APICHAR buf[32];
961 int written = 0, pos, i;
962 pf_flags flags;
963 BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
964 BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER;
965 #if _MSVCR_VER >= 140
966 BOOL legacy_wide = options & UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS;
967 BOOL legacy_msvcrt_compat = options & UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
968 BOOL three_digit_exp = options & UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS;
969 BOOL standard_rounding = options & UCRTBASE_PRINTF_STANDARD_ROUNDING;
970 #else
971 BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE;
972 BOOL three_digit_exp = MSVCRT__get_output_format() != MSVCRT__TWO_DIGIT_EXPONENT;
973 BOOL standard_rounding = FALSE;
974 #endif
976 TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
978 if (!MSVCRT_CHECK_PMT(fmt != NULL))
979 return -1;
981 while(*p) {
982 /* output characters before '%' */
983 for(q=p; *q && *q!='%'; q++);
984 if(p != q) {
985 i = pf_puts(puts_ctx, q-p, p);
986 if(i < 0)
987 return i;
989 written += i;
990 p = q;
991 continue;
994 /* *p == '%' here */
995 p++;
997 /* output a single '%' character */
998 if(*p == '%') {
999 i = pf_puts(puts_ctx, 1, p++);
1000 if(i < 0)
1001 return i;
1003 written += i;
1004 continue;
1007 /* check parameter position */
1008 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
1009 p = q+1;
1010 else
1011 pos = -1;
1013 /* parse the flags */
1014 memset(&flags, 0, sizeof(flags));
1015 while(*p) {
1016 if(*p=='+' || *p==' ') {
1017 if(flags.Sign != '+')
1018 flags.Sign = *p;
1019 } else if(*p == '-')
1020 flags.LeftAlign = TRUE;
1021 else if(*p == '0')
1022 flags.PadZero = TRUE;
1023 else if(*p == '#')
1024 flags.Alternate = TRUE;
1025 else
1026 break;
1028 p++;
1031 /* parse the width */
1032 if(*p == '*') {
1033 p++;
1034 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
1035 p = q+1;
1036 else
1037 i = -1;
1039 flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
1040 if(flags.FieldLength < 0) {
1041 flags.LeftAlign = TRUE;
1042 flags.FieldLength = -flags.FieldLength;
1044 } else while (*p >= '0' && *p <= '9') {
1045 flags.FieldLength *= 10;
1046 flags.FieldLength += *p++ - '0';
1049 /* parse the precision */
1050 flags.Precision = -1;
1051 if(*p == '.') {
1052 flags.Precision = 0;
1053 p++;
1054 if(*p == '*') {
1055 p++;
1056 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
1057 p = q+1;
1058 else
1059 i = -1;
1061 flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
1062 } else while (*p >= '0' && *p <= '9') {
1063 flags.Precision *= 10;
1064 flags.Precision += *p++ - '0';
1068 /* parse argument size modifier */
1069 while(*p) {
1070 if(*p=='l' && *(p+1)=='l') {
1071 flags.IntegerDouble = TRUE;
1072 p++;
1073 } else if(*p=='l') {
1074 flags.IntegerLength = LEN_LONG;
1075 } else if(*p == 'h') {
1076 flags.IntegerLength = LEN_SHORT;
1077 } else if(*p == 'I') {
1078 if(*(p+1)=='6' && *(p+2)=='4') {
1079 flags.IntegerDouble = TRUE;
1080 p += 2;
1081 } else if(*(p+1)=='3' && *(p+2)=='2')
1082 p += 2;
1083 else if(p[1] && strchr("diouxX", p[1]))
1084 flags.IntegerNative = TRUE;
1085 else
1086 break;
1087 } else if(*p == 'w')
1088 flags.WideString = TRUE;
1089 #if _MSVCR_VER == 0 || _MSVCR_VER >= 140
1090 else if((*p == 'z' || *p == 't') && p[1] && strchr("diouxX", p[1]))
1091 flags.IntegerNative = TRUE;
1092 else if(*p == 'j')
1093 flags.IntegerDouble = TRUE;
1094 #endif
1095 #if _MSVCR_VER >= 140
1096 else if(*p == 'T')
1097 flags.NaturalString = TRUE;
1098 #endif
1099 else if(*p != 'L' && ((*p != 'F' && *p != 'N') || !legacy_msvcrt_compat))
1100 break;
1101 p++;
1104 flags.Format = *p;
1106 if(flags.Format == 's' || flags.Format == 'S') {
1107 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
1108 pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
1109 -1, &flags, locale, legacy_wide);
1110 } else if(flags.Format == 'c' || flags.Format == 'C') {
1111 int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
1113 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locale, legacy_wide);
1114 if(i < 0) i = 0; /* ignore conversion error */
1115 } else if(flags.Format == 'p') {
1116 flags.Format = 'X';
1117 flags.PadZero = TRUE;
1118 i = flags.Precision;
1119 flags.Precision = 2*sizeof(void*);
1120 FUNC_NAME(pf_integer_conv)(buf, &flags,
1121 (ULONG_PTR)pf_args(args_ctx, pos, VT_PTR, valist).get_ptr);
1122 flags.PadZero = FALSE;
1123 flags.Precision = i;
1125 #ifdef PRINTF_WIDE
1126 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locale);
1127 #else
1128 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locale);
1129 #endif
1130 } else if(flags.Format == 'n') {
1131 int *used;
1133 if(!n_format_enabled) {
1134 MSVCRT_INVALID_PMT("\'n\' format specifier disabled", MSVCRT_EINVAL);
1135 return -1;
1138 used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
1139 *used = written;
1140 i = 0;
1141 } else if(flags.Format && strchr("diouxX", flags.Format)) {
1142 APICHAR *tmp = buf;
1143 int max_len;
1145 /* 0 padding is added after '0x' if Alternate flag is in use */
1146 if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
1147 && !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
1148 flags.Precision = flags.FieldLength - 2;
1150 max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
1151 if(max_len > ARRAY_SIZE(buf))
1152 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
1153 if(!tmp)
1154 return -1;
1156 if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8))
1157 FUNC_NAME(pf_integer_conv)(tmp, &flags, pf_args(args_ctx, pos,
1158 VT_I8, valist).get_longlong);
1159 else if(flags.Format=='d' || flags.Format=='i')
1160 FUNC_NAME(pf_integer_conv)(tmp, &flags,
1161 flags.IntegerLength != LEN_SHORT ?
1162 pf_args(args_ctx, pos, VT_INT, valist).get_int :
1163 (short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
1164 else
1165 FUNC_NAME(pf_integer_conv)(tmp, &flags,
1166 flags.IntegerLength != LEN_SHORT ?
1167 (unsigned)pf_args(args_ctx, pos, VT_INT, valist).get_int :
1168 (unsigned short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
1170 #ifdef PRINTF_WIDE
1171 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
1172 #else
1173 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
1174 #endif
1175 if(tmp != buf)
1176 HeapFree(GetProcessHeap(), 0, tmp);
1177 } else if(flags.Format && strchr("aAeEfFgG", flags.Format)) {
1178 double val = pf_args(args_ctx, pos, VT_R8, valist).get_double;
1180 if(signbit(val)) {
1181 flags.Sign = '-';
1182 val = -val;
1185 if(isinf(val) || isnan(val))
1186 i = FUNC_NAME(pf_output_special_fp)(pf_puts, puts_ctx, val, &flags,
1187 locale, legacy_msvcrt_compat, three_digit_exp);
1188 else if(flags.Format=='a' || flags.Format=='A')
1189 i = FUNC_NAME(pf_output_hex_fp)(pf_puts, puts_ctx, val, &flags,
1190 locale, standard_rounding);
1191 else
1192 i = FUNC_NAME(pf_output_fp)(pf_puts, puts_ctx, val, &flags,
1193 locale, three_digit_exp, standard_rounding);
1194 } else {
1195 if(invoke_invalid_param_handler) {
1196 MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
1197 *MSVCRT__errno() = MSVCRT_EINVAL;
1198 return -1;
1201 continue;
1204 if(i < 0)
1205 return i;
1206 written += i;
1207 p++;
1210 return written;
1213 #ifndef PRINTF_WIDE
1214 enum types_clbk_flags {
1215 TYPE_CLBK_VA_LIST = 1,
1216 TYPE_CLBK_POSITIONAL = 2,
1217 TYPE_CLBK_ERROR_POS = 4,
1218 TYPE_CLBK_ERROR_TYPE = 8
1221 /* This functions stores types of arguments. It uses args[0] internally */
1222 static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
1224 static const printf_arg ret;
1225 printf_arg *args = ctx;
1227 if(pos == -1) {
1228 args[0].get_int |= TYPE_CLBK_VA_LIST;
1229 return ret;
1230 } else
1231 args[0].get_int |= TYPE_CLBK_POSITIONAL;
1233 if(pos<1 || pos>MSVCRT__ARGMAX)
1234 args[0].get_int |= TYPE_CLBK_ERROR_POS;
1235 else if(args[pos].get_int && args[pos].get_int!=type)
1236 args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
1237 else
1238 args[pos].get_int = type;
1240 return ret;
1242 #endif
1244 int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
1246 struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
1247 printf_arg *args = args_ctx;
1248 int i, j;
1250 i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL,
1251 MSVCRT_PRINTF_POSITIONAL_PARAMS, arg_clbk_type, args_ctx, NULL);
1252 if(i < 0)
1253 return i;
1255 if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
1256 return 0;
1257 if(args[0].get_int != TYPE_CLBK_POSITIONAL)
1258 return -1;
1260 for(i=MSVCRT__ARGMAX; i>0; i--)
1261 if(args[i].get_int)
1262 break;
1264 for(j=1; j<=i; j++) {
1265 switch(args[j].get_int) {
1266 case VT_I8:
1267 args[j].get_longlong = va_arg(valist, LONGLONG);
1268 break;
1269 case VT_INT:
1270 args[j].get_int = va_arg(valist, int);
1271 break;
1272 case VT_R8:
1273 args[j].get_double = va_arg(valist, double);
1274 break;
1275 case VT_PTR:
1276 args[j].get_ptr = va_arg(valist, void*);
1277 break;
1278 default:
1279 return -1;
1283 return j;
1286 #undef APICHAR
1287 #undef CONVCHAR
1288 #undef FUNC_NAME