ddraw/tests: Rewrite LimitTest().
[wine.git] / dlls / msvcrt / printf.h
blobee8120e1493699597c416c585a2fbecf5de3a63b
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 #ifdef PRINTF_WIDE
20 #define APICHAR MSVCRT_wchar_t
21 #define CONVCHAR char
22 #define FUNC_NAME(func) func ## _w
23 #else
24 #define APICHAR char
25 #define CONVCHAR MSVCRT_wchar_t
26 #define FUNC_NAME(func) func ## _a
27 #endif
29 #ifndef signbit
30 #define signbit(x) ((x) < 0)
31 #endif
33 typedef struct FUNC_NAME(pf_flags_t)
35 APICHAR Sign, LeftAlign, Alternate, PadZero;
36 int FieldLength, Precision;
37 APICHAR IntegerLength, IntegerDouble, IntegerNative;
38 APICHAR WideString, NaturalString;
39 APICHAR Format;
40 } FUNC_NAME(pf_flags);
42 struct FUNC_NAME(_str_ctx) {
43 MSVCRT_size_t len;
44 APICHAR *buf;
47 static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
49 struct FUNC_NAME(_str_ctx) *out = ctx;
51 if(!out->buf)
52 return len;
54 if(out->len < len) {
55 memcpy(out->buf, str, out->len*sizeof(APICHAR));
56 out->buf += out->len;
57 out->len = 0;
58 return -1;
61 memcpy(out->buf, str, len*sizeof(APICHAR));
62 out->buf += len;
63 out->len -= len;
64 return len;
67 static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
69 *val = 0;
71 while(isdigit(*fmt)) {
72 *val *= 10;
73 *val += *fmt++ - '0';
76 return fmt;
79 /* pf_fill: takes care of signs, alignment, zero and field padding */
80 static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
81 int len, FUNC_NAME(pf_flags) *flags, BOOL left)
83 int i, r = 0, written;
85 if(flags->Sign && !strchr("diaeEfFgG", flags->Format))
86 flags->Sign = 0;
88 if(left && flags->Sign) {
89 flags->FieldLength--;
90 if(flags->PadZero)
91 r = pf_puts(puts_ctx, 1, &flags->Sign);
93 written = r;
95 if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) {
96 APICHAR ch;
98 if(left && flags->PadZero)
99 ch = '0';
100 else
101 ch = ' ';
103 for(i=0; i<flags->FieldLength-len && r>=0; i++) {
104 r = pf_puts(puts_ctx, 1, &ch);
105 written += r;
110 if(r>=0 && left && flags->Sign && !flags->PadZero) {
111 r = pf_puts(puts_ctx, 1, &flags->Sign);
112 written += r;
115 return r>=0 ? written : r;
118 static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
119 const MSVCRT_wchar_t *str, int len, MSVCRT_pthreadlocinfo locinfo)
121 #ifdef PRINTF_WIDE
122 return pf_puts(puts_ctx, len, str);
123 #else
124 LPSTR out;
125 BOOL def_char;
126 int len_a = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS,
127 str, len, NULL, 0, NULL, &def_char);
128 if(def_char)
129 return 0;
131 out = HeapAlloc(GetProcessHeap(), 0, len_a);
132 if(!out)
133 return -1;
135 WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS,
136 str, len, out, len_a, NULL, NULL);
137 len = pf_puts(puts_ctx, len_a, out);
138 HeapFree(GetProcessHeap(), 0, out);
139 return len;
140 #endif
143 static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
144 const char *str, int len, MSVCRT_pthreadlocinfo locinfo)
146 #ifdef PRINTF_WIDE
147 LPWSTR out;
148 int len_w = MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, NULL, 0);
150 out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
151 if(!out)
152 return -1;
154 MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, out, len_w);
155 len = pf_puts(puts_ctx, len_w, out);
156 HeapFree(GetProcessHeap(), 0, out);
157 return len;
158 #else
159 return pf_puts(puts_ctx, len, str);
160 #endif
163 static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
164 const MSVCRT_wchar_t *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
166 int r, ret;
168 if(len < 0) {
169 /* Do not search past the length specified by the precision. */
170 if(flags->Precision>=0)
171 len = MSVCRT_wcsnlen(str, flags->Precision);
172 else
173 len = strlenW(str);
176 if(flags->Precision>=0 && flags->Precision<len)
177 len = flags->Precision;
179 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
180 ret = r;
181 if(r >= 0) {
182 r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locinfo);
183 ret += r;
185 if(r >= 0) {
186 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
187 ret += r;
190 return r>=0 ? ret : r;
193 static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
194 const char *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
196 int r, ret;
198 if(len < 0) {
199 /* Do not search past the length specified by the precision. */
200 if(flags->Precision>=0)
201 len = MSVCRT_strnlen(str, flags->Precision);
202 else
203 len = strlen(str);
206 if(flags->Precision>=0 && flags->Precision<len)
207 len = flags->Precision;
209 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
210 ret = r;
211 if(r >= 0) {
212 r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locinfo);
213 ret += r;
215 if(r >= 0) {
216 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
217 ret += r;
220 return r>=0 ? ret : r;
223 static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
224 const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo, BOOL legacy_wide)
226 BOOL api_is_wide = sizeof(APICHAR) == sizeof(MSVCRT_wchar_t);
227 BOOL complement_is_narrow = legacy_wide ? api_is_wide : FALSE;
228 #ifdef PRINTF_WIDE
229 static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
231 if(!str)
232 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, nullW, 6, flags, locinfo);
233 #else
234 if(!str)
235 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locinfo);
236 #endif
238 if((flags->NaturalString && api_is_wide) || flags->WideString || flags->IntegerLength=='l')
239 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
240 if((flags->NaturalString && !api_is_wide) || flags->IntegerLength == 'h')
241 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
243 if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow)
244 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
245 else
246 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
249 static inline void FUNC_NAME(pf_rebuild_format_string)(char *p, FUNC_NAME(pf_flags) *flags)
251 *p++ = '%';
252 if(flags->Alternate)
253 *p++ = flags->Alternate;
254 if(flags->Precision >= 0) {
255 p += sprintf(p, ".%d", flags->Precision);
257 *p++ = flags->Format;
258 *p++ = 0;
261 /* pf_integer_conv: prints x to buf, including alternate formats and
262 additional precision digits, but not field characters or the sign */
263 static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, int buf_len,
264 FUNC_NAME(pf_flags) *flags, LONGLONG x)
266 unsigned int base;
267 const char *digits;
268 int i, j, k;
270 if(flags->Format == 'o')
271 base = 8;
272 else if(flags->Format=='x' || flags->Format=='X')
273 base = 16;
274 else
275 base = 10;
277 if(flags->Format == 'X')
278 digits = "0123456789ABCDEFX";
279 else
280 digits = "0123456789abcdefx";
282 if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
283 x = -x;
284 flags->Sign = '-';
287 i = 0;
288 if(x == 0) {
289 flags->Alternate = 0;
290 if(flags->Precision)
291 buf[i++] = '0';
292 } else {
293 while(x != 0) {
294 j = (ULONGLONG)x%base;
295 x = (ULONGLONG)x/base;
296 buf[i++] = digits[j];
299 k = flags->Precision-i;
300 while(k-- > 0)
301 buf[i++] = '0';
302 if(flags->Alternate) {
303 if(base == 16) {
304 buf[i++] = digits[16];
305 buf[i++] = '0';
306 } else if(base==8 && buf[i-1]!='0')
307 buf[i++] = '0';
310 /* Adjust precision so pf_fill won't truncate the number later */
311 flags->Precision = i;
313 buf[i] = '\0';
314 j = 0;
315 while(--i > j) {
316 APICHAR tmp = buf[j];
317 buf[j] = buf[i];
318 buf[i] = tmp;
319 j++;
323 static inline void FUNC_NAME(pf_fixup_exponent)(char *buf, BOOL three_digit_exp)
325 char* tmp = buf;
327 while(tmp[0] && toupper(tmp[0])!='E')
328 tmp++;
330 if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
331 isdigit(tmp[2]) && isdigit(tmp[3])) {
332 #if _MSVCR_VER >= 140
333 BOOL two_digit_exp = !three_digit_exp;
334 #else
335 BOOL two_digit_exp = (MSVCRT__get_output_format() == MSVCRT__TWO_DIGIT_EXPONENT);
336 #endif
338 tmp += 2;
339 if(isdigit(tmp[2])) {
340 if(two_digit_exp && tmp[0]=='0') {
341 tmp[0] = tmp[1];
342 tmp[1] = tmp[2];
343 tmp[2] = tmp[3];
346 return; /* Exponent already 3 digits */
347 }else if(two_digit_exp) {
348 return;
351 tmp[3] = tmp[2];
352 tmp[2] = tmp[1];
353 tmp[1] = tmp[0];
354 tmp[0] = '0';
358 int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
359 MSVCRT__locale_t locale, DWORD options,
360 args_clbk pf_args, void *args_ctx, __ms_va_list *valist)
362 MSVCRT_pthreadlocinfo locinfo;
363 const APICHAR *q, *p = fmt;
364 APICHAR buf[32];
365 int written = 0, pos, i;
366 FUNC_NAME(pf_flags) flags;
367 BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
368 BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER;
369 #if _MSVCR_VER >= 140
370 BOOL legacy_wide = options & UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS;
371 BOOL legacy_msvcrt_compat = options & UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
372 BOOL three_digit_exp = options & UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS;
373 #else
374 BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE, three_digit_exp = TRUE;
375 #endif
377 TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
379 if (!MSVCRT_CHECK_PMT(fmt != NULL))
380 return -1;
382 if(!locale)
383 locinfo = get_locinfo();
384 else
385 locinfo = locale->locinfo;
387 while(*p) {
388 /* output characters before '%' */
389 for(q=p; *q && *q!='%'; q++);
390 if(p != q) {
391 i = pf_puts(puts_ctx, q-p, p);
392 if(i < 0)
393 return i;
395 written += i;
396 p = q;
397 continue;
400 /* *p == '%' here */
401 p++;
403 /* output a single '%' character */
404 if(*p == '%') {
405 i = pf_puts(puts_ctx, 1, p++);
406 if(i < 0)
407 return i;
409 written += i;
410 continue;
413 /* check parameter position */
414 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
415 p = q+1;
416 else
417 pos = -1;
419 /* parse the flags */
420 memset(&flags, 0, sizeof(flags));
421 while(*p) {
422 if(*p=='+' || *p==' ') {
423 if(flags.Sign != '+')
424 flags.Sign = *p;
425 } else if(*p == '-')
426 flags.LeftAlign = *p;
427 else if(*p == '0')
428 flags.PadZero = *p;
429 else if(*p == '#')
430 flags.Alternate = *p;
431 else
432 break;
434 p++;
437 /* parse the width */
438 if(*p == '*') {
439 p++;
440 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
441 p = q+1;
442 else
443 i = -1;
445 flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
446 if(flags.FieldLength < 0) {
447 flags.LeftAlign = '-';
448 flags.FieldLength = -flags.FieldLength;
450 } else while(isdigit(*p)) {
451 flags.FieldLength *= 10;
452 flags.FieldLength += *p++ - '0';
455 /* parse the precision */
456 flags.Precision = -1;
457 if(*p == '.') {
458 flags.Precision = 0;
459 p++;
460 if(*p == '*') {
461 p++;
462 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
463 p = q+1;
464 else
465 i = -1;
467 flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
468 } else while(isdigit(*p)) {
469 flags.Precision *= 10;
470 flags.Precision += *p++ - '0';
474 /* parse argument size modifier */
475 while(*p) {
476 if(*p=='l' && *(p+1)=='l') {
477 flags.IntegerDouble++;
478 p += 2;
479 } else if(*p=='h' || *p=='l' || *p=='L') {
480 flags.IntegerLength = *p;
481 p++;
482 } else if(*p == 'I') {
483 if(*(p+1)=='6' && *(p+2)=='4') {
484 flags.IntegerDouble++;
485 p += 3;
486 } else if(*(p+1)=='3' && *(p+2)=='2')
487 p += 3;
488 else if(isdigit(*(p+1)) || !*(p+1))
489 break;
490 else
491 flags.IntegerNative = *p++;
492 } else if(*p == 'w')
493 flags.WideString = *p++;
494 #if _MSVCR_VER >= 140
495 else if(*p == 'z')
496 flags.IntegerNative = *p++;
497 else if(*p == 'T')
498 flags.NaturalString = *p++;
499 #endif
500 else if((*p == 'F' || *p == 'N') && legacy_msvcrt_compat)
501 p++; /* ignore */
502 else
503 break;
506 flags.Format = *p;
508 if(flags.Format == 's' || flags.Format == 'S') {
509 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
510 pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
511 -1, &flags, locinfo, legacy_wide);
512 } else if(flags.Format == 'c' || flags.Format == 'C') {
513 int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
515 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locinfo, legacy_wide);
516 } else if(flags.Format == 'p') {
517 flags.Format = 'X';
518 flags.PadZero = '0';
519 i = flags.Precision;
520 flags.Precision = 2*sizeof(void*);
521 FUNC_NAME(pf_integer_conv)(buf, sizeof(buf)/sizeof(APICHAR), &flags,
522 (ULONG_PTR)pf_args(args_ctx, pos, VT_PTR, valist).get_ptr);
523 flags.PadZero = 0;
524 flags.Precision = i;
526 #ifdef PRINTF_WIDE
527 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
528 #else
529 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
530 #endif
531 } else if(flags.Format == 'n') {
532 int *used;
534 if(!n_format_enabled) {
535 MSVCRT_INVALID_PMT("\'n\' format specifier disabled", MSVCRT_EINVAL);
536 return -1;
539 used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
540 *used = written;
541 i = 0;
542 } else if(flags.Format && strchr("diouxX", flags.Format)) {
543 APICHAR *tmp = buf;
544 int max_len;
546 /* 0 padding is added after '0x' if Alternate flag is in use */
547 if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
548 && !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
549 flags.Precision = flags.FieldLength - 2;
551 max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
552 if(max_len > sizeof(buf)/sizeof(APICHAR))
553 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
554 if(!tmp)
555 return -1;
557 if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8))
558 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
559 VT_I8, valist).get_longlong);
560 else if(flags.Format=='d' || flags.Format=='i')
561 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, flags.IntegerLength!='h' ?
562 pf_args(args_ctx, pos, VT_INT, valist).get_int :
563 (short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
564 else
565 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, flags.IntegerLength!='h' ?
566 (unsigned)pf_args(args_ctx, pos, VT_INT, valist).get_int :
567 (unsigned short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
569 #ifdef PRINTF_WIDE
570 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
571 #else
572 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
573 #endif
574 if(tmp != buf)
575 HeapFree(GetProcessHeap(), 0, tmp);
576 } else if(flags.Format && strchr("aeEfFgG", flags.Format)) {
577 char float_fmt[20], buf_a[32], *tmp = buf_a, *decimal_point;
578 int len = flags.Precision + 10;
579 double val = pf_args(args_ctx, pos, VT_R8, valist).get_double;
580 int r;
581 BOOL inf = FALSE, nan = FALSE, ind = FALSE;
583 if(isinf(val))
584 inf = TRUE;
585 else if(isnan(val)) {
586 if(!signbit(val))
587 nan = TRUE;
588 else
589 ind = TRUE;
592 if(inf || nan || ind) {
593 if(ind || val<0)
594 flags.Sign = '-';
596 if(flags.Format=='g' || flags.Format=='G')
597 val = (nan ? 1.12345 : 1.1234); /* fraction will be overwritten with #INF, #IND or #QNAN string */
598 else
599 val = 1;
601 if(flags.Format=='a') {
602 if(flags.Precision==-1)
603 flags.Precision = 6; /* strlen("#INF00") */
607 if(flags.Format=='f' || flags.Format=='F') {
608 if(val>-10.0 && val<10.0)
609 i = 1;
610 else
611 i = 1 + log10(val<0 ? -val : val);
612 /* Default precision is 6, additional space for sign, separator and nullbyte is required */
613 i += (flags.Precision==-1 ? 6 : flags.Precision) + 3;
615 if(i > len)
616 len = i;
619 if(len > sizeof(buf_a))
620 tmp = HeapAlloc(GetProcessHeap(), 0, len);
621 if(!tmp)
622 return -1;
624 FUNC_NAME(pf_rebuild_format_string)(float_fmt, &flags);
625 if(val < 0) {
626 flags.Sign = '-';
627 val = -val;
630 if((inf || nan || ind) && !legacy_msvcrt_compat) {
631 static const char inf_str[] = "inf";
632 static const char ind_str[] = "nan(ind)";
633 static const char nan_str[] = "nan";
634 if(inf)
635 sprintf(tmp, inf_str);
636 else if(ind)
637 sprintf(tmp, ind_str);
638 else
639 sprintf(tmp, nan_str);
640 if (strchr("EFG", flags.Format))
641 for(i=0; tmp[i]; i++)
642 tmp[i] = toupper(tmp[i]);
643 } else {
644 sprintf(tmp, float_fmt, val);
645 if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G')
646 FUNC_NAME(pf_fixup_exponent)(tmp, three_digit_exp);
649 decimal_point = strchr(tmp, '.');
650 if(decimal_point) {
651 *decimal_point = *locinfo->lconv->decimal_point;
653 if(inf || nan || ind) {
654 static const char inf_str[] = "#INF";
655 static const char ind_str[] = "#IND";
656 static const char nan_str[] = "#QNAN";
658 const char *str;
659 int size;
661 if(inf) {
662 str = inf_str;
663 size = sizeof(inf_str);
664 }else if(ind) {
665 str = ind_str;
666 size = sizeof(ind_str);
667 }else {
668 str = nan_str;
669 size = sizeof(nan_str);
672 for(i=1; i<size; i++) {
673 if(decimal_point[i]<'0' || decimal_point[i]>'9')
674 break;
676 decimal_point[i] = str[i-1];
679 if(i!=size && i!=1)
680 decimal_point[i-1]++;
684 len = strlen(tmp);
685 i = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, &flags, TRUE);
686 if(i < 0)
687 return i;
688 r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, tmp, len, locinfo);
689 if(r < 0)
690 return r;
691 i += r;
692 if(tmp != buf_a)
693 HeapFree(GetProcessHeap(), 0, tmp);
694 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, &flags, FALSE);
695 if(r < 0)
696 return r;
697 i += r;
698 } else {
699 if(invoke_invalid_param_handler) {
700 MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
701 *MSVCRT__errno() = MSVCRT_EINVAL;
702 return -1;
705 continue;
708 if(i < 0)
709 return i;
710 written += i;
711 p++;
714 return written;
717 #ifndef PRINTF_WIDE
718 enum types_clbk_flags {
719 TYPE_CLBK_VA_LIST = 1,
720 TYPE_CLBK_POSITIONAL = 2,
721 TYPE_CLBK_ERROR_POS = 4,
722 TYPE_CLBK_ERROR_TYPE = 8
725 /* This functions stores types of arguments. It uses args[0] internally */
726 static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
728 static const printf_arg ret;
729 printf_arg *args = ctx;
731 if(pos == -1) {
732 args[0].get_int |= TYPE_CLBK_VA_LIST;
733 return ret;
734 } else
735 args[0].get_int |= TYPE_CLBK_POSITIONAL;
737 if(pos<1 || pos>MSVCRT__ARGMAX)
738 args[0].get_int |= TYPE_CLBK_ERROR_POS;
739 else if(args[pos].get_int && args[pos].get_int!=type)
740 args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
741 else
742 args[pos].get_int = type;
744 return ret;
746 #endif
748 int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
750 struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
751 printf_arg *args = args_ctx;
752 int i, j;
754 i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL,
755 MSVCRT_PRINTF_POSITIONAL_PARAMS, arg_clbk_type, args_ctx, NULL);
756 if(i < 0)
757 return i;
759 if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
760 return 0;
761 if(args[0].get_int != TYPE_CLBK_POSITIONAL)
762 return -1;
764 for(i=MSVCRT__ARGMAX; i>0; i--)
765 if(args[i].get_int)
766 break;
768 for(j=1; j<=i; j++) {
769 switch(args[j].get_int) {
770 case VT_I8:
771 args[j].get_longlong = va_arg(valist, LONGLONG);
772 break;
773 case VT_INT:
774 args[j].get_int = va_arg(valist, int);
775 break;
776 case VT_R8:
777 args[j].get_double = va_arg(valist, double);
778 break;
779 case VT_PTR:
780 args[j].get_ptr = va_arg(valist, void*);
781 break;
782 default:
783 return -1;
787 return j;
790 #undef APICHAR
791 #undef CONVCHAR
792 #undef FUNC_NAME