wine-wine/dlls/msvcrt/printf.h

1006 lines
30 KiB
C

/*
* Copyright 2011 Piotr Caban for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifdef PRINTF_WIDE
#define APICHAR MSVCRT_wchar_t
#define CONVCHAR char
#define FUNC_NAME(func) func ## _w
#else
#define APICHAR char
#define CONVCHAR MSVCRT_wchar_t
#define FUNC_NAME(func) func ## _a
#endif
#ifndef signbit
#define signbit(x) ((x) < 0)
#endif
typedef struct FUNC_NAME(pf_flags_t)
{
APICHAR Sign, LeftAlign, Alternate, PadZero;
int FieldLength, Precision;
APICHAR IntegerLength, IntegerDouble, IntegerNative;
APICHAR WideString, NaturalString;
APICHAR Format;
} FUNC_NAME(pf_flags);
struct FUNC_NAME(_str_ctx) {
MSVCRT_size_t len;
APICHAR *buf;
};
static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
{
struct FUNC_NAME(_str_ctx) *out = ctx;
if(!out->buf)
return len;
if(out->len < len) {
memmove(out->buf, str, out->len*sizeof(APICHAR));
out->buf += out->len;
out->len = 0;
return -1;
}
memmove(out->buf, str, len*sizeof(APICHAR));
out->buf += len;
out->len -= len;
return len;
}
static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
{
*val = 0;
while(isdigit(*fmt)) {
*val *= 10;
*val += *fmt++ - '0';
}
return fmt;
}
/* pf_fill: takes care of signs, alignment, zero and field padding */
static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
int len, FUNC_NAME(pf_flags) *flags, BOOL left)
{
int i, r = 0, written;
if(flags->Sign && !strchr("diaAeEfFgG", flags->Format))
flags->Sign = 0;
if(left && flags->Sign) {
flags->FieldLength--;
if(flags->PadZero)
r = pf_puts(puts_ctx, 1, &flags->Sign);
}
written = r;
if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) {
APICHAR ch;
if(left && flags->PadZero)
ch = '0';
else
ch = ' ';
for(i=0; i<flags->FieldLength-len && r>=0; i++) {
r = pf_puts(puts_ctx, 1, &ch);
written += r;
}
}
if(r>=0 && left && flags->Sign && !flags->PadZero) {
r = pf_puts(puts_ctx, 1, &flags->Sign);
written += r;
}
return r>=0 ? written : r;
}
#ifndef PRINTF_HELPERS
#define PRINTF_HELPERS
static inline int wcstombs_len(char *mbstr, const MSVCRT_wchar_t *wcstr,
int len, MSVCRT__locale_t locale)
{
char buf[MSVCRT_MB_LEN_MAX];
int i, r, mblen = 0;
for(i=0; i<len; i++) {
r = MSVCRT__wctomb_l(mbstr ? mbstr+mblen : buf, wcstr[i], locale);
if(r < 0) return r;
mblen += r;
}
return mblen;
}
static inline int mbstowcs_len(MSVCRT_wchar_t *wcstr, const char *mbstr,
int len, MSVCRT__locale_t locale)
{
int i, r, wlen = 0;
WCHAR buf;
for(i=0; i<len; wlen++) {
r = MSVCRT_mbtowc_l(wcstr ? wcstr+wlen : &buf, mbstr+i, len-i, locale);
if(r < 0) return r;
i += r ? r : 1;
}
return wlen;
}
#endif
static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const MSVCRT_wchar_t *str, int len, MSVCRT__locale_t locale)
{
#ifdef PRINTF_WIDE
return pf_puts(puts_ctx, len, str);
#else
LPSTR out;
int len_a = wcstombs_len(NULL, str, len, locale);
if(len_a < 0)
return -1;
out = HeapAlloc(GetProcessHeap(), 0, len_a);
if(!out)
return -1;
wcstombs_len(out, str, len, locale);
len = pf_puts(puts_ctx, len_a, out);
HeapFree(GetProcessHeap(), 0, out);
return len;
#endif
}
static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const char *str, int len, MSVCRT__locale_t locale)
{
#ifdef PRINTF_WIDE
LPWSTR out;
int len_w = mbstowcs_len(NULL, str, len, locale);
if(len_w < 0)
return -1;
out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
if(!out)
return -1;
mbstowcs_len(out, str, len, locale);
len = pf_puts(puts_ctx, len_w, out);
HeapFree(GetProcessHeap(), 0, out);
return len;
#else
return pf_puts(puts_ctx, len, str);
#endif
}
static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const MSVCRT_wchar_t *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT__locale_t locale)
{
int r, ret;
if(len < 0) {
/* Do not search past the length specified by the precision. */
if(flags->Precision>=0)
len = MSVCRT_wcsnlen(str, flags->Precision);
else
len = strlenW(str);
}
if(flags->Precision>=0 && flags->Precision<len)
len = flags->Precision;
r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
ret = r;
if(r >= 0) {
r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locale);
ret += r;
}
if(r >= 0) {
r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
ret += r;
}
return r>=0 ? ret : r;
}
static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const char *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT__locale_t locale)
{
int r, ret;
if(len < 0) {
/* Do not search past the length specified by the precision. */
if(flags->Precision>=0)
len = MSVCRT_strnlen(str, flags->Precision);
else
len = strlen(str);
}
if(flags->Precision>=0 && flags->Precision<len)
len = flags->Precision;
r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
ret = r;
if(r >= 0) {
r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locale);
ret += r;
}
if(r >= 0) {
r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
ret += r;
}
return r>=0 ? ret : r;
}
static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT__locale_t locale, BOOL legacy_wide)
{
BOOL api_is_wide = sizeof(APICHAR) == sizeof(MSVCRT_wchar_t);
BOOL complement_is_narrow = legacy_wide ? api_is_wide : FALSE;
#ifdef PRINTF_WIDE
static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
if(!str)
return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, nullW, 6, flags, locale);
#else
if(!str)
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locale);
#endif
if((flags->NaturalString && api_is_wide) || flags->WideString || flags->IntegerLength=='l')
return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
if((flags->NaturalString && !api_is_wide) || flags->IntegerLength == 'h')
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow)
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale);
else
return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale);
}
static inline int FUNC_NAME(pf_output_special_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
double v, FUNC_NAME(pf_flags) *flags, MSVCRT__locale_t locale,
BOOL legacy_msvcrt_compat, BOOL three_digit_exp)
{
APICHAR pfx[16], sfx[8], *p;
int len = 0, r, frac_len, pfx_len, sfx_len;
if(!legacy_msvcrt_compat) {
const char *str;
if(isinf(v)) {
if(strchr("AEFG", flags->Format)) str = "INF";
else str = "inf";
}else {
if(strchr("AEFG", flags->Format)) str = (flags->Sign == '-' ? "NAN(IND)" : "NAN");
else str = (flags->Sign == '-' ? "nan(ind)" : "nan");
}
flags->Precision = -1;
flags->PadZero = 0;
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, -1, flags, locale);
}
/* workaround a bug in native implementation */
if(flags->Format=='g' || flags->Format=='G')
flags->Precision--;
p = pfx;
if(flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
if (flags->Sign) *p++ = flags->Sign;
*p++ = '0';
*p++ = (flags->Format=='a' ? 'x' : 'X');
r = pf_puts(puts_ctx, p-pfx, pfx);
if(r < 0) return r;
len += r;
flags->FieldLength -= p-pfx;
}
p = pfx;
if(!flags->PadZero && (flags->Format=='a' || flags->Format=='A')) {
*p++ = '0';
*p++ = (flags->Format=='a' ? 'x' : 'X');
}
*p++ = '1';
*p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
*p++ = '#';
frac_len = 1;
if(isinf(v)) {
*p++ = 'I';
*p++ = 'N';
*p++ = 'F';
frac_len += 3;
}else if(flags->Sign == '-') {
*p++ = 'I';
*p++ = 'N';
*p++ = 'D';
frac_len += 3;
}else {
*p++ = 'Q';
*p++ = 'N';
*p++ = 'A';
*p++ = 'N';
frac_len += 4;
}
*p = 0;
pfx_len = p - pfx;
if(len) flags->Sign = 0;
if(flags->Precision>=0 && flags->Precision<frac_len)
p[flags->Precision - frac_len - 1]++;
p = sfx;
if(strchr("aAeE", flags->Format)) {
if(flags->Format == 'a') *p++ = 'p';
else if(flags->Format == 'A') *p++ = 'P';
else if(flags->Format == 'e') *p++ = 'e';
else *p++ = 'E';
*p++ = '+';
*p++ = '0';
if(flags->Format == 'e' || flags->Format == 'E') {
*p++ = '0';
if(three_digit_exp) *p++ = '0';
}
}
*p = 0;
if(!flags->Alternate && (flags->Format == 'g' || flags->Format == 'G')) sfx_len = frac_len;
else sfx_len = flags->Precision;
if(sfx_len == -1) {
if(strchr("fFeE", flags->Format)) sfx_len = 6;
else if(flags->Format == 'a' || flags->Format == 'A') sfx_len = 13;
}
sfx_len += p - sfx - frac_len;
if(sfx_len > 0) flags->FieldLength -= sfx_len;
if(flags->Precision >= 0) {
if(!flags->Precision) flags->Precision--;
flags->Precision += pfx_len - frac_len;
}
#ifdef PRINTF_WIDE
r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
#else
r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
#endif
if(r < 0) return r;
len += r;
flags->FieldLength = sfx_len;
flags->PadZero = '0';
flags->Precision = -1;
flags->Sign = 0;
#ifdef PRINTF_WIDE
r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
#else
r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
#endif
if(r < 0) return r;
len += r;
return len;
}
static inline int FUNC_NAME(pf_output_hex_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
double v, FUNC_NAME(pf_flags) *flags, MSVCRT__locale_t locale)
{
#define EXP_BITS 11
#define MANT_BITS 53
const APICHAR digits[2][16] = {
{ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
{ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }
};
APICHAR pfx[16], sfx[8], *p;
ULONGLONG mant;
int len = 0, sfx_len = 0, r, exp;
mant = (*(ULONGLONG*)&v) << 1;
exp = (mant >> MANT_BITS);
exp -= (1 << (EXP_BITS - 1)) - 1;
mant = (mant << EXP_BITS) >> (EXP_BITS+1);
p = pfx;
if(flags->PadZero) {
if(flags->Sign) *p++ = flags->Sign;
*p++ = '0';
*p++ = (flags->Format=='a' ? 'x' : 'X');
r = pf_puts(puts_ctx, p-pfx, pfx);
if(r < 0) return r;
len += r;
flags->FieldLength -= p-pfx;
flags->Sign = 0;
p = pfx;
}else {
*p++ = '0';
*p++ = (flags->Format=='a' ? 'x' : 'X');
}
if(exp == -(1 << (EXP_BITS-1))+1) {
if(!mant) exp = 0;
else exp++;
*p++ = '0';
}else {
*p++ = '1';
}
*p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
for(r=MANT_BITS/4-1; r>=0; r--) {
p[r] = digits[flags->Format == 'A'][mant & 15];
mant >>= 4;
}
if(!flags->Precision) {
if(p[0] > '8') p[-2]++;
if(!flags->Alternate) p--;
}else if(flags->Precision>0 && flags->Precision<MANT_BITS/4) {
BOOL round_up = (p[flags->Precision] > '8');
for(r=flags->Precision-1; r>=0 && round_up; r--) {
round_up = FALSE;
if(p[r]=='f' || p[r]=='F') {
p[r] = '0';
round_up = TRUE;
}else if(p[r] == '9') {
p[r] = (flags->Format == 'a' ? 'a' : 'A');
}else {
p[r]++;
}
}
if(round_up) p[-2]++;
p += flags->Precision;
}else {
p += MANT_BITS/4;
if(flags->Precision > MANT_BITS/4) sfx_len += flags->Precision - MANT_BITS/4;
}
*p = 0;
p = sfx;
*p++ = (flags->Format == 'a' ? 'p' : 'P');
if(exp < 0) {
*p++ = '-';
exp = -exp;
}else {
*p++ = '+';
}
for(r=3; r>=0; r--) {
p[r] = exp%10 + '0';
exp /= 10;
if(!exp) break;
}
for(exp=0; exp<4-r; exp++)
p[exp] = p[exp+r];
p += exp;
*p = 0;
sfx_len += p - sfx;
flags->FieldLength -= sfx_len;
flags->Precision = -1;
#ifdef PRINTF_WIDE
r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale);
#else
r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale);
#endif
if(r < 0) return r;
len += r;
flags->FieldLength = sfx_len;
flags->PadZero = '0';
flags->Sign = 0;
#ifdef PRINTF_WIDE
r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale);
#else
r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale);
#endif
if(r < 0) return r;
len += r;
return len;
}
static inline void FUNC_NAME(pf_rebuild_format_string)(char *p, FUNC_NAME(pf_flags) *flags)
{
*p++ = '%';
if(flags->Alternate)
*p++ = flags->Alternate;
if(flags->Precision >= 0) {
*p++ = '.';
MSVCRT__itoa(flags->Precision, p, 10);
p += strlen(p);
}
*p++ = flags->Format;
*p++ = 0;
}
/* pf_integer_conv: prints x to buf, including alternate formats and
additional precision digits, but not field characters or the sign */
static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, int buf_len,
FUNC_NAME(pf_flags) *flags, LONGLONG x)
{
unsigned int base;
const char *digits;
int i, j, k;
if(flags->Format == 'o')
base = 8;
else if(flags->Format=='x' || flags->Format=='X')
base = 16;
else
base = 10;
if(flags->Format == 'X')
digits = "0123456789ABCDEFX";
else
digits = "0123456789abcdefx";
if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
x = -x;
flags->Sign = '-';
}
i = 0;
if(x == 0) {
flags->Alternate = 0;
if(flags->Precision)
buf[i++] = '0';
} else {
while(x != 0) {
j = (ULONGLONG)x%base;
x = (ULONGLONG)x/base;
buf[i++] = digits[j];
}
}
k = flags->Precision-i;
while(k-- > 0)
buf[i++] = '0';
if(flags->Alternate) {
if(base == 16) {
buf[i++] = digits[16];
buf[i++] = '0';
} else if(base==8 && buf[i-1]!='0')
buf[i++] = '0';
}
/* Adjust precision so pf_fill won't truncate the number later */
flags->Precision = i;
buf[i] = '\0';
j = 0;
while(--i > j) {
APICHAR tmp = buf[j];
buf[j] = buf[i];
buf[i] = tmp;
j++;
}
}
static inline void FUNC_NAME(pf_fixup_exponent)(char *buf, BOOL three_digit_exp)
{
char* tmp = buf;
while(tmp[0] && MSVCRT__toupper_l(tmp[0], NULL)!='E')
tmp++;
if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
isdigit(tmp[2]) && isdigit(tmp[3])) {
#if _MSVCR_VER >= 140
BOOL two_digit_exp = !three_digit_exp;
#else
BOOL two_digit_exp = (MSVCRT__get_output_format() == MSVCRT__TWO_DIGIT_EXPONENT);
#endif
tmp += 2;
if(isdigit(tmp[2])) {
if(two_digit_exp && tmp[0]=='0') {
tmp[0] = tmp[1];
tmp[1] = tmp[2];
tmp[2] = tmp[3];
}
return; /* Exponent already 3 digits */
}else if(two_digit_exp) {
return;
}
tmp[3] = tmp[2];
tmp[2] = tmp[1];
tmp[1] = tmp[0];
tmp[0] = '0';
}
}
int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
MSVCRT__locale_t locale, DWORD options,
args_clbk pf_args, void *args_ctx, __ms_va_list *valist)
{
const APICHAR *q, *p = fmt;
APICHAR buf[32];
int written = 0, pos, i;
FUNC_NAME(pf_flags) flags;
BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER;
#if _MSVCR_VER >= 140
BOOL legacy_wide = options & UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS;
BOOL legacy_msvcrt_compat = options & UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
BOOL three_digit_exp = options & UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS;
#else
BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE, three_digit_exp = TRUE;
#endif
TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
if (!MSVCRT_CHECK_PMT(fmt != NULL))
return -1;
while(*p) {
/* output characters before '%' */
for(q=p; *q && *q!='%'; q++);
if(p != q) {
i = pf_puts(puts_ctx, q-p, p);
if(i < 0)
return i;
written += i;
p = q;
continue;
}
/* *p == '%' here */
p++;
/* output a single '%' character */
if(*p == '%') {
i = pf_puts(puts_ctx, 1, p++);
if(i < 0)
return i;
written += i;
continue;
}
/* check parameter position */
if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
p = q+1;
else
pos = -1;
/* parse the flags */
memset(&flags, 0, sizeof(flags));
while(*p) {
if(*p=='+' || *p==' ') {
if(flags.Sign != '+')
flags.Sign = *p;
} else if(*p == '-')
flags.LeftAlign = *p;
else if(*p == '0')
flags.PadZero = *p;
else if(*p == '#')
flags.Alternate = *p;
else
break;
p++;
}
/* parse the width */
if(*p == '*') {
p++;
if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
p = q+1;
else
i = -1;
flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
if(flags.FieldLength < 0) {
flags.LeftAlign = '-';
flags.FieldLength = -flags.FieldLength;
}
} else while(isdigit(*p)) {
flags.FieldLength *= 10;
flags.FieldLength += *p++ - '0';
}
/* parse the precision */
flags.Precision = -1;
if(*p == '.') {
flags.Precision = 0;
p++;
if(*p == '*') {
p++;
if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
p = q+1;
else
i = -1;
flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
} else while(isdigit(*p)) {
flags.Precision *= 10;
flags.Precision += *p++ - '0';
}
}
/* parse argument size modifier */
while(*p) {
if(*p=='l' && *(p+1)=='l') {
flags.IntegerDouble++;
p += 2;
} else if(*p=='h' || *p=='l' || *p=='L') {
flags.IntegerLength = *p;
p++;
} else if(*p == 'I') {
if(*(p+1)=='6' && *(p+2)=='4') {
flags.IntegerDouble++;
p += 3;
} else if(*(p+1)=='3' && *(p+2)=='2')
p += 3;
else if(p[1] && strchr("diouxX", p[1]))
flags.IntegerNative = *p++;
else
break;
} else if(*p == 'w')
flags.WideString = *p++;
#if _MSVCR_VER == 0 || _MSVCR_VER >= 140
else if((*p == 'z' || *p == 't') && p[1] && strchr("diouxX", p[1]))
flags.IntegerNative = *p++;
else if(*p == 'j') {
flags.IntegerDouble++;
p++;
}
#endif
#if _MSVCR_VER >= 140
else if(*p == 'T')
flags.NaturalString = *p++;
#endif
else if((*p == 'F' || *p == 'N') && legacy_msvcrt_compat)
p++; /* ignore */
else
break;
}
flags.Format = *p;
if(flags.Format == 's' || flags.Format == 'S') {
i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
-1, &flags, locale, legacy_wide);
} else if(flags.Format == 'c' || flags.Format == 'C') {
int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locale, legacy_wide);
if(i < 0) i = 0; /* ignore conversion error */
} else if(flags.Format == 'p') {
flags.Format = 'X';
flags.PadZero = '0';
i = flags.Precision;
flags.Precision = 2*sizeof(void*);
FUNC_NAME(pf_integer_conv)(buf, ARRAY_SIZE(buf), &flags,
(ULONG_PTR)pf_args(args_ctx, pos, VT_PTR, valist).get_ptr);
flags.PadZero = 0;
flags.Precision = i;
#ifdef PRINTF_WIDE
i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locale);
#else
i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locale);
#endif
} else if(flags.Format == 'n') {
int *used;
if(!n_format_enabled) {
MSVCRT_INVALID_PMT("\'n\' format specifier disabled", MSVCRT_EINVAL);
return -1;
}
used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
*used = written;
i = 0;
} else if(flags.Format && strchr("diouxX", flags.Format)) {
APICHAR *tmp = buf;
int max_len;
/* 0 padding is added after '0x' if Alternate flag is in use */
if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
&& !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
flags.Precision = flags.FieldLength - 2;
max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
if(max_len > ARRAY_SIZE(buf))
tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
if(!tmp)
return -1;
if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8))
FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
VT_I8, valist).get_longlong);
else if(flags.Format=='d' || flags.Format=='i')
FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, flags.IntegerLength!='h' ?
pf_args(args_ctx, pos, VT_INT, valist).get_int :
(short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
else
FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, flags.IntegerLength!='h' ?
(unsigned)pf_args(args_ctx, pos, VT_INT, valist).get_int :
(unsigned short)pf_args(args_ctx, pos, VT_INT, valist).get_int);
#ifdef PRINTF_WIDE
i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
#else
i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locale);
#endif
if(tmp != buf)
HeapFree(GetProcessHeap(), 0, tmp);
} else if(flags.Format && strchr("aAeEfFgG", flags.Format)) {
char float_fmt[20], buf_a[32], *tmp = buf_a, *decimal_point;
int len = flags.Precision + 10;
double val = pf_args(args_ctx, pos, VT_R8, valist).get_double;
int r;
if(signbit(val)) {
flags.Sign = '-';
val = -val;
}
if(isinf(val) || isnan(val))
i = FUNC_NAME(pf_output_special_fp)(pf_puts, puts_ctx, val, &flags,
locale, legacy_msvcrt_compat, three_digit_exp);
else if(flags.Format=='a' || flags.Format=='A')
i = FUNC_NAME(pf_output_hex_fp)(pf_puts, puts_ctx, val, &flags, locale);
else {
if(flags.Format=='f' || flags.Format=='F') {
if(val<10.0)
i = 1;
else
i = 1 + log10(val);
/* Default precision is 6, additional space for sign, separator and nullbyte is required */
i += (flags.Precision==-1 ? 6 : flags.Precision) + 3;
if(i > len)
len = i;
}
if(len > sizeof(buf_a))
tmp = HeapAlloc(GetProcessHeap(), 0, len);
if(!tmp)
return -1;
FUNC_NAME(pf_rebuild_format_string)(float_fmt, &flags);
sprintf(tmp, float_fmt, val);
if(MSVCRT__toupper_l(flags.Format, NULL)=='E' || MSVCRT__toupper_l(flags.Format, NULL)=='G')
FUNC_NAME(pf_fixup_exponent)(tmp, three_digit_exp);
decimal_point = strchr(tmp, '.');
if(decimal_point)
*decimal_point = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point;
len = strlen(tmp);
i = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, &flags, TRUE);
if(i < 0)
return i;
r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, tmp, len, locale);
if(r < 0)
return r;
i += r;
if(tmp != buf_a)
HeapFree(GetProcessHeap(), 0, tmp);
r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, &flags, FALSE);
if(r < 0)
return r;
i += r;
}
} else {
if(invoke_invalid_param_handler) {
MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
*MSVCRT__errno() = MSVCRT_EINVAL;
return -1;
}
continue;
}
if(i < 0)
return i;
written += i;
p++;
}
return written;
}
#ifndef PRINTF_WIDE
enum types_clbk_flags {
TYPE_CLBK_VA_LIST = 1,
TYPE_CLBK_POSITIONAL = 2,
TYPE_CLBK_ERROR_POS = 4,
TYPE_CLBK_ERROR_TYPE = 8
};
/* This functions stores types of arguments. It uses args[0] internally */
static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
{
static const printf_arg ret;
printf_arg *args = ctx;
if(pos == -1) {
args[0].get_int |= TYPE_CLBK_VA_LIST;
return ret;
} else
args[0].get_int |= TYPE_CLBK_POSITIONAL;
if(pos<1 || pos>MSVCRT__ARGMAX)
args[0].get_int |= TYPE_CLBK_ERROR_POS;
else if(args[pos].get_int && args[pos].get_int!=type)
args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
else
args[pos].get_int = type;
return ret;
}
#endif
int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
{
struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
printf_arg *args = args_ctx;
int i, j;
i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL,
MSVCRT_PRINTF_POSITIONAL_PARAMS, arg_clbk_type, args_ctx, NULL);
if(i < 0)
return i;
if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
return 0;
if(args[0].get_int != TYPE_CLBK_POSITIONAL)
return -1;
for(i=MSVCRT__ARGMAX; i>0; i--)
if(args[i].get_int)
break;
for(j=1; j<=i; j++) {
switch(args[j].get_int) {
case VT_I8:
args[j].get_longlong = va_arg(valist, LONGLONG);
break;
case VT_INT:
args[j].get_int = va_arg(valist, int);
break;
case VT_R8:
args[j].get_double = va_arg(valist, double);
break;
case VT_PTR:
args[j].get_ptr = va_arg(valist, void*);
break;
default:
return -1;
}
}
return j;
}
#undef APICHAR
#undef CONVCHAR
#undef FUNC_NAME