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
20 #define APICHAR MSVCRT_wchar_t
22 #define FUNC_NAME(func) func ## _w
25 #define CONVCHAR MSVCRT_wchar_t
26 #define FUNC_NAME(func) func ## _a
29 typedef struct FUNC_NAME(pf_flags_t)
31 APICHAR Sign, LeftAlign, Alternate, PadZero;
32 int FieldLength, Precision;
33 APICHAR IntegerLength, IntegerDouble;
36 } FUNC_NAME(pf_flags);
38 struct FUNC_NAME(_str_ctx) {
43 static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
45 struct FUNC_NAME(_str_ctx) *out = ctx;
51 memcpy(out->buf, str, out->len);
57 memcpy(out->buf, str, len*sizeof(APICHAR));
62 static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
66 while(isdigit(*fmt)) {
74 /* pf_fill: takes care of signs, alignment, zero and field padding */
75 static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
76 int len, FUNC_NAME(pf_flags) *flags, BOOL left)
78 int i, r = 0, written;
80 if(flags->Sign && !(flags->Format=='d' || flags->Format=='i'))
83 if(left && flags->Sign) {
86 r = pf_puts(puts_ctx, 1, &flags->Sign);
90 if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) {
93 if(left && flags->PadZero)
98 for(i=0; i<flags->FieldLength-len && r>=0; i++) {
99 r = pf_puts(puts_ctx, 1, &ch);
105 if(r>=0 && left && flags->Sign && !flags->PadZero) {
106 r = pf_puts(puts_ctx, 1, &flags->Sign);
110 return r>=0 ? written : r;
113 static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
114 const MSVCRT_wchar_t *str, int len, MSVCRT_pthreadlocinfo locinfo)
117 return pf_puts(puts_ctx, len, str);
120 int len_a = WideCharToMultiByte(locinfo->lc_codepage, 0, str, len, NULL, 0, NULL, NULL);
122 out = HeapAlloc(GetProcessHeap(), 0, len_a);
126 WideCharToMultiByte(locinfo->lc_codepage, 0, str, len, out, len_a, NULL, NULL);
127 len = pf_puts(puts_ctx, len_a, out);
128 HeapFree(GetProcessHeap(), 0, out);
133 static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
134 const char *str, int len, MSVCRT_pthreadlocinfo locinfo)
138 int len_w = MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, NULL, 0);
140 out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
144 MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, out, len_w);
145 len = pf_puts(puts_ctx, len_w, out);
146 HeapFree(GetProcessHeap(), 0, out);
149 return pf_puts(puts_ctx, len, str);
153 static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
154 const MSVCRT_wchar_t *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
161 if(flags->Precision>=0 && flags->Precision<len)
162 len = flags->Precision;
164 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
167 r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locinfo);
171 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
175 return r>=0 ? ret : r;
178 static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
179 const char *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
186 if(flags->Precision>=0 && flags->Precision<len)
187 len = flags->Precision;
189 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
192 r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locinfo);
196 r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
200 return r>=0 ? ret : r;
203 static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
204 const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
207 static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
210 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, nullW, 6, flags, locinfo);
213 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locinfo);
216 if(flags->WideString || flags->IntegerLength=='l')
217 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
218 if(flags->IntegerLength == 'h')
219 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
221 if((flags->Format=='S' || flags->Format=='C') == (sizeof(APICHAR)==sizeof(MSVCRT_wchar_t)))
222 return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
224 return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
227 static inline void FUNC_NAME(pf_rebuild_format_string)(char *p, FUNC_NAME(pf_flags) *flags)
233 *p++ = flags->LeftAlign;
235 *p++ = flags->Alternate;
237 *p++ = flags->PadZero;
238 if(flags->FieldLength) {
239 sprintf(p, "%d", flags->FieldLength);
242 if(flags->Precision >= 0) {
243 sprintf(p, ".%d", flags->Precision);
246 *p++ = flags->Format;
250 /* pf_integer_conv: prints x to buf, including alternate formats and
251 additional precision digits, but not field characters or the sign */
252 static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, int buf_len,
253 FUNC_NAME(pf_flags) *flags, LONGLONG x)
259 if(flags->Format == 'o')
261 else if(flags->Format=='x' || flags->Format=='X')
266 if(flags->Format == 'X')
267 digits = "0123456789ABCDEFX";
269 digits = "0123456789abcdefx";
271 if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
277 if(x==0 && flags->Precision)
281 j = (ULONGLONG)x%base;
282 x = (ULONGLONG)x/base;
283 buf[i++] = digits[j];
286 k = flags->Precision-i;
289 if(flags->Alternate) {
291 buf[i++] = digits[16];
293 } else if(base==8 && buf[i-1]!='0')
297 /* Adjust precision so pf_fill won't truncate the number later */
298 flags->Precision = i;
303 APICHAR tmp = buf[j];
310 static inline void FUNC_NAME(pf_fixup_exponent)(char *buf)
314 while(tmp[0] && toupper(tmp[0])!='E')
317 if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
318 isdigit(tmp[2]) && isdigit(tmp[3])) {
322 return; /* Exponent already 3 digits */
333 memmove(buf, buf + 1, (tmp - buf) + 3);
338 int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
339 MSVCRT__locale_t locale, BOOL positional_params, BOOL invoke_invalid_param_handler,
340 args_clbk pf_args, void *args_ctx, __ms_va_list *valist)
342 MSVCRT_pthreadlocinfo locinfo;
343 const APICHAR *q, *p = fmt;
345 int written = 0, pos, i;
346 FUNC_NAME(pf_flags) flags;
348 TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
351 locinfo = get_locinfo();
353 locinfo = locale->locinfo;
356 /* output characters before '%' */
357 for(q=p; *q && *q!='%'; q++);
359 i = pf_puts(puts_ctx, q-p, p);
371 /* output a single '%' character */
373 i = pf_puts(puts_ctx, 1, p++);
381 /* check parameter position */
382 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
387 /* parse the flags */
388 memset(&flags, 0, sizeof(flags));
390 if(*p=='+' || *p==' ') {
391 if(flags.Sign != '+')
394 flags.LeftAlign = *p;
398 flags.Alternate = *p;
408 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
413 flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
414 if(flags.FieldLength < 0) {
415 flags.LeftAlign = '-';
416 flags.FieldLength = -flags.FieldLength;
418 } else while(isdigit(*p)) {
419 flags.FieldLength *= 10;
420 flags.FieldLength += *p++ - '0';
423 /* parse the precision */
424 flags.Precision = -1;
430 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
435 flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
436 } else while(isdigit(*p)) {
437 flags.Precision *= 10;
438 flags.Precision += *p++ - '0';
442 /* parse argument size modifier */
444 if(*p=='l' && *(p+1)=='l') {
445 flags.IntegerDouble++;
447 } else if(*p=='h' || *p=='l' || *p=='L') {
448 flags.IntegerLength = *p;
450 } else if(*p == 'I') {
451 if(*(p+1)=='6' && *(p+2)=='4') {
452 flags.IntegerDouble++;
454 } else if(*(p+1)=='3' && *(p+2)=='2')
456 else if(isdigit(*(p+1)) || !*(p+1))
461 flags.WideString = *p++;
470 if(flags.Format == 's' || flags.Format == 'S') {
471 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
472 pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
473 -1, &flags, locinfo);
474 } else if(flags.Format == 'c' || flags.Format == 'C') {
475 int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
478 FIXME("multibyte characters printing not supported\n");
480 i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locinfo);
481 } else if(flags.Format == 'p') {
485 flags.Precision = 2*sizeof(void*);
486 FUNC_NAME(pf_integer_conv)(buf, sizeof(buf)/sizeof(APICHAR), &flags,
487 pf_args(args_ctx, pos, VT_INT, valist).get_int);
492 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
494 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
496 } else if(flags.Format == 'n') {
499 if(!n_format_enabled) {
500 MSVCRT_INVALID_PMT("\'n\' format specifier disabled");
501 *MSVCRT__errno() = MSVCRT_EINVAL;
505 used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
508 } else if(flags.Format && strchr("diouxX", flags.Format)) {
510 int max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
512 if(max_len > sizeof(buf)/sizeof(APICHAR))
513 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
517 if(flags.IntegerDouble)
518 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
519 VT_I8, valist).get_longlong);
520 else if(flags.Format=='d' || flags.Format=='i')
521 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
522 VT_INT, valist).get_int);
524 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, (unsigned)pf_args(
525 args_ctx, pos, VT_INT, valist).get_int);
528 i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
530 i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
533 HeapFree(GetProcessHeap(), 0, tmp);
534 } else if(flags.Format && strchr("aeEfgG", flags.Format)) {
535 char fmt[20], buf_a[32], *tmp = buf_a, *decimal_point;
536 int max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
538 if(max_len > sizeof(buf_a))
539 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
543 FUNC_NAME(pf_rebuild_format_string)(fmt, &flags);
545 sprintf(tmp, fmt, pf_args(args_ctx, pos, VT_R8, valist).get_double);
546 if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G')
547 FUNC_NAME(pf_fixup_exponent)(tmp);
549 decimal_point = strchr(tmp, '.');
551 *decimal_point = *locinfo->lconv->decimal_point;
553 i = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, tmp, strlen(tmp), locinfo);
555 HeapFree(GetProcessHeap(), 0, tmp);
557 if(invoke_invalid_param_handler) {
558 MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
559 *MSVCRT__errno() = MSVCRT_EINVAL;
576 enum types_clbk_flags {
577 TYPE_CLBK_VA_LIST = 1,
578 TYPE_CLBK_POSITIONAL = 2,
579 TYPE_CLBK_ERROR_POS = 4,
580 TYPE_CLBK_ERROR_TYPE = 8
583 /* This functions stores types of arguments. It uses args[0] internally */
584 static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
586 static const printf_arg ret;
587 printf_arg *args = ctx;
590 args[0].get_int |= TYPE_CLBK_VA_LIST;
593 args[0].get_int |= TYPE_CLBK_POSITIONAL;
595 if(pos<1 || pos>MSVCRT__ARGMAX)
596 args[0].get_int |= TYPE_CLBK_ERROR_POS;
597 else if(args[pos].get_int && args[pos].get_int!=type)
598 args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
600 args[pos].get_int = type;
606 static int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
608 struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
609 printf_arg *args = args_ctx;
612 i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL, TRUE, FALSE,
613 arg_clbk_type, args_ctx, NULL);
617 if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
619 if(args[0].get_int != TYPE_CLBK_POSITIONAL)
622 for(i=MSVCRT__ARGMAX; i>0; i--)
626 for(j=1; j<=i; j++) {
627 switch(args[j].get_int) {
629 args[j].get_longlong = va_arg(valist, LONGLONG);
632 args[j].get_int = va_arg(valist, int);
635 args[j].get_double = va_arg(valist, double);
638 args[j].get_ptr = va_arg(valist, void*);