Files
st/libs/c/src/wprintf.c
T
Yanis 69ef244371 Merge headers from nitro decomp (#69)
* merge headers from nitro decomp

* cleanup: use inlines from g2.h and gx.h when changing registers

* cleanup: remove externs declarations in source files
2026-05-25 02:55:16 +02:00

1408 lines
38 KiB
C

#include <ansi_fp.h>
#include <ctype.h>
#include <locale.h>
#include <math.h>
#include <mbstring.h>
#include <mem.h>
#include <secure_error.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <wstring.h>
#define MAX_SIG_DIG 32
#define LDBL_MANT_DIG 24
#define LDBL_MAX_EXP 128
#define TARGET_FLOAT_BITS 32
#define TARGET_FLOAT_BYTES (TARGET_FLOAT_BITS / 8)
#define TARGET_FLOAT_MAX_EXP LDBL_MAX_EXP
#define TARGET_FLOAT_MANT_DIG LDBL_MANT_DIG
#define TARGET_FLOAT_IMPLICIT_J_BIT 1
#define TARGET_FLOAT_MANT_BITS (TARGET_FLOAT_MANT_DIG - TARGET_FLOAT_IMPLICIT_J_BIT)
#define TARGET_FLOAT_EXP_BITS (TARGET_FLOAT_BITS - TARGET_FLOAT_MANT_BITS - 1)
typedef long long intmax_t;
#define PTRDIFF __typeof__((char *) 0 - (char *) 0)
typedef PTRDIFF ptrdiff_t;
int vswprintf(wchar_t *s, size_t n, const wchar_t *format, va_list arg);
enum justification_options {
left_justification,
right_justification,
zero_fill
};
enum sign_options {
only_minus,
sign_always,
space_holder
};
enum argument_options {
// normal_argument,
// char_argument,
// short_argument,
// long_argument,
// long_long_argument,
// wchar_argument,
// intmax_argument,
// size_t_argument,
// ptrdiff_argument,
// long_double_argument
normal_argument,
char_argument,
short_argument,
long_argument,
long_long_argument,
wchar_argument,
intmax_argument,
size_t_argument,
ptrdiff_argument,
long_double_argument
};
typedef struct {
unsigned char justification_options;
unsigned char sign_options;
unsigned char precision_specified;
unsigned char alternate_form;
unsigned char argument_options;
wchar_t conversion_char;
int field_width;
int precision;
} print_format;
typedef struct {
char *CharStr;
size_t MaxCharCount;
size_t CharsWritten;
} __OutStrCtrl;
#define conversion_buff_size 512
#define conversion_max 509
#define bad_conversion 0xFFFF
static const wchar_t *parse_format(const wchar_t *format_string, va_list *arg, print_format *format) {
print_format f;
const wchar_t *s = format_string;
int c;
int flag_found;
f.justification_options = right_justification;
f.sign_options = only_minus;
f.precision_specified = 0;
f.alternate_form = 0;
f.argument_options = normal_argument;
f.field_width = 0;
f.precision = 0;
if ((c = *++s) == L'%') {
f.conversion_char = c;
*format = f;
return ((const wchar_t *) s + 1);
}
while (true) {
flag_found = 1;
switch (c) {
case L'-':
f.justification_options = left_justification;
break;
case L'+':
f.sign_options = sign_always;
break;
case L' ':
if (f.sign_options != sign_always) {
f.sign_options = space_holder;
}
break;
case L'#':
f.alternate_form = 1;
break;
case L'0':
if (f.justification_options != left_justification) {
f.justification_options = zero_fill;
}
break;
default:
flag_found = 0;
break;
}
if (flag_found) {
c = *++s;
} else {
break;
}
}
if (c == L'*') {
if ((f.field_width = va_arg(*arg, int)) < 0) {
f.justification_options = left_justification;
f.field_width = -f.field_width;
}
c = *++s;
} else {
while (isdigit(c)) {
f.field_width = (f.field_width * 10) + (c - L'0');
c = *++s;
}
}
if (f.field_width > conversion_max) {
f.conversion_char = bad_conversion;
*format = f;
return ((const wchar_t *) s + 1);
}
if (c == L'.') {
f.precision_specified = 1;
if ((c = *++s) == L'*') {
if ((f.precision = va_arg(*arg, int)) < 0) {
f.precision_specified = 0;
}
c = *++s;
} else {
while (isdigit(c)) {
f.precision = (f.precision * 10) + (c - L'0');
c = *++s;
}
}
}
flag_found = 1;
switch (c) {
case L'h':
f.argument_options = short_argument;
if (s[1] == L'h') {
f.argument_options = char_argument;
c = *++s;
}
break;
case L'l':
f.argument_options = long_argument;
if (s[1] == L'l') {
f.argument_options = long_long_argument;
c = *++s;
}
break;
case L'L':
f.argument_options = long_double_argument;
break;
case L'j':
f.argument_options = intmax_argument;
break;
case L't':
f.argument_options = ptrdiff_argument;
break;
case L'z':
f.argument_options = size_t_argument;
break;
default:
flag_found = 0;
break;
}
if (flag_found) {
c = *++s;
}
f.conversion_char = c;
switch (c) {
case L'd':
case L'i':
case L'u':
case L'o':
case L'x':
case L'X':
if (f.argument_options == long_double_argument) {
f.argument_options = long_long_argument;
}
if (!f.precision_specified) {
f.precision = 1;
} else if (f.justification_options == zero_fill) {
f.justification_options = right_justification;
}
break;
case L'f':
case L'F':
if (f.argument_options == short_argument || f.argument_options == intmax_argument ||
f.argument_options == size_t_argument || f.argument_options == ptrdiff_argument ||
f.argument_options == long_long_argument) {
f.conversion_char = bad_conversion;
break;
}
if (!f.precision_specified) {
f.precision = 6;
}
break;
case L'a':
case L'A':
if (!f.precision_specified) {
f.precision = 0x0D;
}
if (f.argument_options == short_argument || f.argument_options == intmax_argument ||
f.argument_options == size_t_argument || f.argument_options == ptrdiff_argument ||
f.argument_options == long_long_argument || f.argument_options == char_argument) {
f.conversion_char = bad_conversion;
}
break;
case L'g':
case L'G':
if (!f.precision) {
f.precision = 1;
}
case L'e':
case L'E':
if (f.argument_options == short_argument || f.argument_options == char_argument ||
f.argument_options == intmax_argument || f.argument_options == size_t_argument ||
f.argument_options == ptrdiff_argument || f.argument_options == long_long_argument) {
f.conversion_char = bad_conversion;
break;
}
if (!f.precision_specified) {
f.precision = 6;
}
break;
case L'p':
f.argument_options = long_argument;
f.alternate_form = 1;
f.conversion_char = L'x';
f.precision = 8;
break;
case L'c':
if (f.argument_options == long_argument) {
f.argument_options = wchar_argument;
} else if (f.precision_specified || f.argument_options != normal_argument) {
f.conversion_char = bad_conversion;
}
break;
case L's':
if (f.argument_options == long_argument) {
f.argument_options = wchar_argument;
} else if (f.argument_options != normal_argument) {
f.conversion_char = bad_conversion;
}
break;
case L'n':
if (f.argument_options == long_double_argument) {
f.argument_options = long_long_argument;
}
break;
default:
f.conversion_char = bad_conversion;
break;
}
*format = f;
return ((const wchar_t *) s + 1);
}
wchar_t *long2str(long num, wchar_t *buff, print_format format) {
unsigned long unsigned_num, base;
wchar_t *p;
int n, digits;
int minus = 0;
unsigned_num = num;
minus = 0;
p = buff;
*--p = 0;
digits = 0;
if (!num && !format.precision && !(format.alternate_form && format.conversion_char == L'o')) {
return p;
}
switch (format.conversion_char) {
case L'd':
case L'i':
base = 10;
if (num < 0) {
if (num != 0x80000000) {
unsigned_num = -num;
}
minus = 1;
}
break;
case L'o':
base = 8;
format.sign_options = only_minus;
break;
case L'u':
base = 10;
format.sign_options = only_minus;
break;
case L'x':
case L'X':
base = 16;
format.sign_options = only_minus;
break;
}
do {
n = unsigned_num % base;
unsigned_num /= base;
if (n < 10) {
n += L'0';
} else {
n -= 10;
if (format.conversion_char == L'x') {
n += L'a';
} else {
n += L'A';
}
}
*--p = n;
++digits;
} while (unsigned_num != 0);
if (base == 8 && format.alternate_form && *p != L'0') {
*--p = L'0';
++digits;
}
if (format.justification_options == zero_fill) {
format.precision = format.field_width;
if (minus || format.sign_options != only_minus) {
--format.precision;
}
if (base == 16 && format.alternate_form) {
format.precision -= 2;
}
}
if (buff - p + format.precision > conversion_max) {
return 0;
}
while (digits < format.precision) {
*--p = L'0';
++digits;
}
if (base == 16 && format.alternate_form) {
*--p = format.conversion_char;
*--p = L'0';
}
if (minus) {
*--p = L'-';
} else if (format.sign_options == sign_always) {
*--p = L'+';
} else if (format.sign_options == space_holder) {
*--p = L' ';
}
return p;
}
wchar_t *longlong2str(long long num, wchar_t *pBuf, print_format fmt) {
unsigned long long unsigned_num, base;
wchar_t *p;
int n, digits;
int minus = 0;
unsigned_num = num;
minus = 0;
p = pBuf;
*--p = 0;
digits = 0;
if (!num && !fmt.precision && !(fmt.alternate_form && fmt.conversion_char == L'o')) {
return p;
}
switch (fmt.conversion_char) {
case L'd':
case L'i':
base = 10;
if (num < 0) {
if (num != 0x8000000000000000) {
unsigned_num = -num;
}
minus = 1;
}
break;
case L'o':
base = 8;
fmt.sign_options = only_minus;
break;
case L'u':
base = 10;
fmt.sign_options = only_minus;
break;
case L'x':
case L'X':
base = 16;
fmt.sign_options = only_minus;
break;
}
do {
n = unsigned_num % base;
unsigned_num /= base;
if (n < 10) {
n += L'0';
} else {
n -= 10;
if (fmt.conversion_char == L'x') {
n += L'a';
} else {
n += L'A';
}
}
*--p = n;
++digits;
} while (unsigned_num != 0);
if (base == 8 && fmt.alternate_form && *p != L'0') {
*--p = L'0';
++digits;
}
if (fmt.justification_options == zero_fill) {
fmt.precision = fmt.field_width;
if (minus || fmt.sign_options != only_minus) {
--fmt.precision;
}
if (base == 16 && fmt.alternate_form) {
fmt.precision -= 2;
}
}
if (pBuf - p + fmt.precision > conversion_max) {
return 0;
}
while (digits < fmt.precision) {
*--p = L'0';
++digits;
}
if (base == 16 && fmt.alternate_form) {
*--p = fmt.conversion_char;
*--p = L'0';
}
if (minus) {
*--p = L'-';
} else if (fmt.sign_options == sign_always) {
*--p = L'+';
} else if (fmt.sign_options == space_holder) {
*--p = L' ';
}
return p;
}
static wchar_t *double2hex(long double num, wchar_t *buff, print_format format) {
wchar_t *p;
unsigned char *q;
unsigned char working_byte;
long double ld;
int expbits, expmask;
unsigned snum;
long exp;
print_format exp_format;
int hex_precision;
int mantissa_bit;
decform form;
decimal dec;
int radix_marker;
radix_marker = '.';
p = buff;
ld = num;
if (format.precision > 509) {
return 0;
}
form.style = (char) 0;
form.digits = 0x20;
__num2dec(&form, num, &dec);
switch (*dec.sig.text) {
case '0':
dec.exp = 0;
if (dec.sgn) {
p = buff - 5;
if (format.conversion_char == L'A') {
wcscpy(p, L"-0X0");
} else {
wcscpy(p, L"-0x0");
}
} else {
p = buff - 4;
if (format.conversion_char == L'A') {
wcscpy(p, L"0X0");
} else {
wcscpy(p, L"0x0");
}
}
return p;
case 'I':
if (dec.sgn) {
p = buff - 5;
if (format.conversion_char == L'A') {
wcscpy(p, L"-INF");
} else {
wcscpy(p, L"-inf");
}
} else {
p = buff - 4;
if (format.conversion_char == L'A') {
wcscpy(p, L"INF");
} else {
wcscpy(p, L"inf");
}
}
return p;
case 'N':
if (dec.sgn) {
p = buff - 5;
if (format.conversion_char == L'A') {
wcscpy(p, L"-NAN");
} else {
wcscpy(p, L"-nan");
}
} else {
p = buff - 4;
if (format.conversion_char == L'A') {
wcscpy(p, L"NAN");
} else {
wcscpy(p, L"nan");
}
}
return p;
}
exp_format.justification_options = right_justification;
exp_format.sign_options = sign_always;
exp_format.precision_specified = 0;
exp_format.alternate_form = 0;
exp_format.argument_options = normal_argument;
exp_format.field_width = 0;
exp_format.precision = 1;
exp_format.conversion_char = L'd';
int i;
unsigned char *nump = (unsigned char *) &num;
for (i = 0; i < TARGET_FLOAT_BYTES; i++) {
unsigned char x = nump[i];
nump[i] = nump[TARGET_FLOAT_BYTES - i + 3];
nump[TARGET_FLOAT_BYTES - i + 3] = x;
}
expbits = TARGET_FLOAT_EXP_BITS;
expmask = 0x7FF;
snum = ((unsigned char *) &num)[0] << 25;
if (TARGET_FLOAT_EXP_BITS > 7) {
snum |= ((unsigned char *) &num)[1] << 17;
}
if (TARGET_FLOAT_EXP_BITS > 15) {
snum |= ((unsigned char *) &num)[2] << 9;
}
if (TARGET_FLOAT_EXP_BITS > 23) {
snum |= ((unsigned char *) &num)[3] << 1;
}
snum = (snum >> (29 - expbits)) & expmask;
if (snum != 0) {
exp = snum - 0x3FF;
} else {
exp = 0;
}
p = long2str(exp, buff, exp_format);
if (format.conversion_char == L'a') {
*--p = L'p';
} else {
*--p = L'P';
}
q = (unsigned char *) &num;
if (TARGET_FLOAT_IMPLICIT_J_BIT) {
mantissa_bit = (4 + expbits + format.precision * 4) - 1;
} else {
mantissa_bit = (1 + expbits + format.precision * 4) - 4;
}
for (hex_precision = format.precision; hex_precision >= 1; hex_precision--) {
if (mantissa_bit < 64) {
int mantissa_byte;
mantissa_byte = mantissa_bit >> 3;
working_byte = (*(q + mantissa_byte)) >> (7 - (mantissa_bit & 7));
if ((mantissa_bit & ~7) != ((mantissa_bit - 4) & ~7)) {
working_byte |= ((*(q + (mantissa_byte - 1))) << 8) >> (7 - ((mantissa_bit) & 7));
}
if (!TARGET_FLOAT_IMPLICIT_J_BIT) {
if (mantissa_bit == 1 + expbits) {
*--p = radix_marker;
working_byte &= 0x1;
}
}
if ((working_byte &= 0xF) < 10) {
working_byte += L'0';
} else if (format.conversion_char == L'a') {
working_byte += L'a' - 10;
} else {
working_byte += L'A' - 10;
}
} else {
working_byte = L'0';
}
*--p = working_byte;
mantissa_bit -= 4;
}
if (TARGET_FLOAT_IMPLICIT_J_BIT) {
if (format.precision || format.alternate_form) {
*--p = radix_marker;
}
if (fabsl(ld) != 0.0) {
*--p = '1';
} else {
*--p = '0';
}
}
if (format.conversion_char == L'a') {
*--p = L'x';
} else {
*--p = L'X';
}
*--p = L'0';
if (dec.sgn) {
*--p = L'-';
} else if (format.sign_options == sign_always) {
*--p = L'+';
} else if (format.sign_options == space_holder) {
*--p = L' ';
}
return p;
}
void round_decimal(decimal *dec, int new_length) {
unsigned char c;
unsigned char *p;
int carry;
if (new_length < 0) {
return_zero:
dec->exp = 0;
dec->sig.length = 1;
*dec->sig.text = L'0';
return;
}
if (new_length >= dec->sig.length) {
return;
}
p = (unsigned char *) dec->sig.text + new_length + 1;
c = *--p - L'0';
if (c == 5) {
unsigned char *q = &((unsigned char *) dec->sig.text)[dec->sig.length];
while (--q > p && *q == L'0')
;
carry = (q == p) ? p[-1] & 1 : 1;
} else {
carry = (c > 5);
}
while (new_length != 0) {
c = *--p - L'0' + carry;
if ((carry = (c > 9)) != 0 || c == 0) {
--new_length;
} else {
*p = c + L'0';
break;
}
}
if (carry != 0) {
dec->exp += 1;
dec->sig.length = 1;
*dec->sig.text = L'1';
return;
} else if (new_length == 0) {
goto return_zero;
}
dec->sig.length = new_length;
}
wchar_t *float2str(long double num, wchar_t *wbuff, print_format format) {
decimal dec;
decform form;
wchar_t *pw;
unsigned char *p;
unsigned char buff[512];
unsigned char *q;
int n, digits, sign;
int int_digits, frac_digits;
int radix_marker;
radix_marker = '.';
if (format.precision > conversion_max) {
return 0;
}
form.style = 0;
form.digits = 0x20;
__num2dec(&form, num, &dec);
p = (unsigned char *) dec.sig.text + dec.sig.length;
while (dec.sig.length > 1 && *--p == L'0') {
--dec.sig.length;
++dec.exp;
}
switch (*dec.sig.text) {
case L'0':
dec.exp = 0;
break;
case L'I':
if (num < 0) {
pw = wbuff - 5;
if (isupper(format.conversion_char)) {
wcscpy(pw, L"-INF");
} else {
wcscpy(pw, L"-inf");
}
} else {
pw = wbuff - 4;
if (isupper(format.conversion_char)) {
wcscpy(pw, L"INF");
} else {
wcscpy(pw, L"inf");
}
}
return pw;
case L'N':
if (dec.sgn) {
pw = wbuff - 5;
if (isupper(format.conversion_char)) {
wcscpy(pw, L"-NAN");
} else {
wcscpy(pw, L"-nan");
}
} else {
pw = wbuff - 4;
if (isupper(format.conversion_char)) {
wcscpy(pw, L"NAN");
} else {
wcscpy(pw, L"nan");
}
}
return pw;
}
dec.exp += dec.sig.length - 1;
p = buff + 512;
*--p = 0;
switch (format.conversion_char) {
case L'g':
case L'G':
if (dec.sig.length > format.precision) {
round_decimal(&dec, format.precision);
}
if (dec.exp < -4 || dec.exp >= format.precision) {
if (format.alternate_form) {
--format.precision;
} else {
format.precision = dec.sig.length - 1;
}
if (format.conversion_char == L'g') {
format.conversion_char = L'e';
} else {
format.conversion_char = L'E';
}
goto e_format;
}
if (format.alternate_form) {
format.precision -= dec.exp + 1;
} else {
if ((format.precision = dec.sig.length - (dec.exp + 1)) < 0) {
format.precision = 0;
}
}
goto f_format;
case L'e':
case L'E':
e_format:
if (dec.sig.length > format.precision + 1) {
round_decimal(&dec, format.precision + 1);
}
n = dec.exp;
sign = L'+';
if (n < 0) {
n = -n;
sign = L'-';
}
for (digits = 0; n || digits < 2; ++digits) {
*--p = n % 10 + '0';
n /= 10;
}
*--p = sign;
*--p = format.conversion_char;
if (buff - p + format.precision > conversion_max) {
return 0;
}
if (dec.sig.length < format.precision + 1) {
for (n = format.precision + 1 - dec.sig.length + 1; --n;) {
*--p = L'0';
}
}
for (n = dec.sig.length, q = (unsigned char *) dec.sig.text + dec.sig.length; --n;) {
*--p = *--q;
}
if (format.precision || format.alternate_form) {
*--p = radix_marker;
}
*--p = *dec.sig.text;
if (dec.sgn) {
*--p = L'-';
} else if (format.sign_options == sign_always) {
*--p = L'+';
} else if (format.sign_options == space_holder) {
*--p = L' ';
}
break;
case L'f':
case L'F':
f_format:
if ((frac_digits = -dec.exp + dec.sig.length - 1) < 0) {
frac_digits = 0;
}
if (frac_digits > format.precision) {
round_decimal(&dec, dec.sig.length - (frac_digits - format.precision));
if ((frac_digits = -dec.exp + dec.sig.length - 1) < 0) {
frac_digits = 0;
}
}
if ((int_digits = dec.exp + 1) < 0) {
int_digits = 0;
}
if (int_digits + frac_digits > conversion_max) {
return 0;
}
q = (unsigned char *) dec.sig.text + dec.sig.length;
for (digits = 0; digits < (format.precision - frac_digits); ++digits) {
*--p = L'0';
}
for (digits = 0; digits < frac_digits && digits < dec.sig.length; ++digits) {
*--p = *--q;
}
for (; digits < frac_digits; ++digits) {
*--p = L'0';
}
if (format.precision || format.alternate_form) {
*--p = radix_marker;
}
if (int_digits) {
for (digits = 0; digits < int_digits - dec.sig.length; ++digits) {
*--p = L'0';
}
for (; digits < int_digits; ++digits) {
*--p = *--q;
}
} else {
*--p = L'0';
}
if (dec.sgn) {
*--p = L'-';
} else if (format.sign_options == sign_always) {
*--p = L'+';
} else if (format.sign_options == space_holder) {
*--p = L' ';
}
break;
}
pw = wbuff - strlen(p) - 1;
mbstowcs(pw, p, strlen(p));
return pw;
}
// non-matching: https://decomp.me/scratch/bV2T9
int __wpformatter(void *(*WriteProc)(void *, const wchar_t *, size_t), void *WriteProcArg, const wchar_t *format_str,
va_list arg, bool is_secure) {
int num_chars, chars_written, field_width;
const wchar_t *format_ptr;
const wchar_t *curr_format;
print_format format;
long long_num;
long long long_long_num;
long double long_double_num;
wchar_t buff[512];
wchar_t *buff_ptr;
wchar_t *string_end;
char *cstring_end;
wchar_t fill_char = ' ';
char *strptr;
format_ptr = format_str;
chars_written = 0;
while (*format_ptr) {
if (!(curr_format = wcschr(format_ptr, L'%'))) {
num_chars = wcslen(format_ptr);
chars_written += num_chars;
if (num_chars && !(*WriteProc)(WriteProcArg, format_ptr, num_chars)) {
return -1;
}
break;
}
num_chars = curr_format - format_ptr;
chars_written += num_chars;
if (num_chars && !(*WriteProc)(WriteProcArg, format_ptr, num_chars)) {
return -1;
}
format_ptr = curr_format;
format_ptr = parse_format(format_ptr, &arg, &format);
switch (format.conversion_char) {
case L'd':
case L'i':
if (format.argument_options == long_argument) {
long_num = va_arg(arg, long);
} else if (format.argument_options == long_long_argument) {
long_long_num = va_arg(arg, long long);
} else if (format.argument_options == intmax_argument) {
long_long_num = va_arg(arg, intmax_t);
} else if (format.argument_options == size_t_argument) {
long_num = va_arg(arg, size_t);
} else if (format.argument_options == ptrdiff_argument) {
long_num = va_arg(arg, ptrdiff_t);
} else {
long_num = va_arg(arg, int);
}
if (format.argument_options == short_argument) {
long_num = (short) long_num;
}
if ((format.argument_options == long_long_argument) || (format.argument_options == intmax_argument)) {
if (!(buff_ptr = longlong2str(long_long_num, buff + 512, format))) {
goto conversion_error;
}
} else {
if (!(buff_ptr = long2str(long_num, buff + 512, format))) {
goto conversion_error;
}
}
num_chars = buff + 512 - 1 - buff_ptr;
break;
case L'o':
case L'u':
case L'x':
case L'X':
if (format.argument_options == long_argument) {
long_num = va_arg(arg, unsigned long);
} else if (format.argument_options == long_long_argument) {
long_long_num = va_arg(arg, long long);
} else if (format.argument_options == intmax_argument) {
long_long_num = va_arg(arg, intmax_t);
} else if (format.argument_options == size_t_argument) {
long_num = va_arg(arg, size_t);
} else if (format.argument_options == ptrdiff_argument) {
long_num = va_arg(arg, ptrdiff_t);
} else {
long_num = va_arg(arg, unsigned int);
}
if (format.argument_options == short_argument) {
long_num = (unsigned short) long_num;
}
if ((format.argument_options == long_long_argument) || (format.argument_options == intmax_argument)) {
if (!(buff_ptr = longlong2str(long_long_num, buff + 512, format))) {
goto conversion_error;
}
} else {
if (!(buff_ptr = long2str(long_num, buff + 512, format))) {
goto conversion_error;
}
}
num_chars = buff + 512 - 1 - buff_ptr;
break;
case L'f':
case L'F':
case L'e':
case L'E':
case L'g':
case L'G':
if (format.argument_options == long_double_argument) {
long_double_num = va_arg(arg, long double);
} else {
long_double_num = va_arg(arg, double);
}
if (!(buff_ptr = float2str(long_double_num, buff + 512, format))) {
goto conversion_error;
}
num_chars = buff + 512 - 1 - buff_ptr;
break;
case L'a':
case L'A':
if (format.argument_options == long_double_argument) {
long_double_num = va_arg(arg, long double);
} else {
long_double_num = va_arg(arg, double);
}
if (!(buff_ptr = double2hex(long_double_num, buff + 512, format))) {
goto conversion_error;
}
num_chars = buff + 512 - 1 - buff_ptr;
break;
case L's':
if (format.argument_options == wchar_argument) {
buff_ptr = va_arg(arg, wchar_t *);
if (is_secure && buff_ptr == NULL) {
__msl_runtime_constraint_violation_s(NULL, NULL, -1);
return -1;
}
if (buff_ptr == NULL) {
buff_ptr = L"";
}
if (format.alternate_form) {
num_chars = (unsigned char) *buff_ptr++;
if (format.precision_specified && num_chars > format.precision) {
num_chars = format.precision;
}
} else if (format.precision_specified) {
num_chars = format.precision;
if ((string_end = (wchar_t *) wmemchr(buff_ptr, 0, num_chars)) != 0) {
num_chars = string_end - buff_ptr;
}
} else {
num_chars = wcslen(buff_ptr);
}
} else {
strptr = va_arg(arg, char *);
if (is_secure && strptr == NULL) {
__msl_runtime_constraint_violation_s(0, 0, -1);
return -1;
}
if (strptr == NULL) {
strptr = "";
}
if (format.alternate_form) {
num_chars = (unsigned char) *buff_ptr++;
if (format.precision_specified && num_chars > format.precision) {
num_chars = format.precision;
}
} else if (format.precision_specified) {
num_chars = format.precision;
if ((cstring_end = (char *) memchr(strptr, 0, num_chars)) != 0) {
num_chars = cstring_end - strptr;
}
} else {
num_chars = strlen(strptr);
}
if ((num_chars = mbstowcs(buff, strptr, num_chars)) < 0) {
goto conversion_error;
}
buff_ptr = &buff[0];
}
break;
case L'n':
if (is_secure) {
__msl_runtime_constraint_violation_s(0, 0, -1);
return -1;
}
buff_ptr = va_arg(arg, wchar_t *);
switch (format.argument_options) {
case normal_argument:
*(int *) buff_ptr = chars_written;
break;
case short_argument:
*(short *) buff_ptr = chars_written;
break;
case long_argument:
*(long *) buff_ptr = chars_written;
break;
case intmax_argument:
*(intmax_t *) buff_ptr = chars_written;
break;
case size_t_argument:
*(size_t *) buff_ptr = chars_written;
break;
case ptrdiff_argument:
*(ptrdiff_t *) buff_ptr = chars_written;
break;
case long_long_argument:
*(long long *) buff_ptr = chars_written;
break;
}
continue;
case L'c':
buff_ptr = buff;
if (format.argument_options == wchar_argument) {
*buff_ptr = va_arg(arg, int);
num_chars = 1;
} else {
char chint = va_arg(arg, int);
num_chars = mbtowc(buff, &chint, 1);
}
break;
case L'%':
buff_ptr = buff;
*buff_ptr = L'%';
num_chars = 1;
break;
case L'p':
case 0xFFFF:
default:
conversion_error:
num_chars = wcslen(curr_format);
chars_written += num_chars;
if (num_chars && !(*WriteProc)(WriteProcArg, curr_format, num_chars)) {
return -1;
}
return chars_written;
}
field_width = num_chars;
if (format.justification_options != left_justification) {
fill_char = (format.justification_options == zero_fill) ? '0' : ' ';
if (((*buff_ptr == L'+') || (*buff_ptr == L'-') || (*buff_ptr == L' ')) && (fill_char == '0')) {
if ((*WriteProc)(WriteProcArg, buff_ptr, 1) == 0) {
return -1;
}
++buff_ptr;
num_chars--;
}
if ((format.justification_options == zero_fill) &&
((format.conversion_char == L'a') || (format.conversion_char == L'A'))) {
if (num_chars < 2) {
return -1;
}
if ((*WriteProc)(WriteProcArg, buff_ptr, 2) == 0) {
return -1;
}
num_chars -= 2;
buff_ptr += 2;
}
while (field_width < format.field_width) {
if ((*WriteProc)(WriteProcArg, &fill_char, 1) == 0) {
return -1;
}
++field_width;
}
}
if (num_chars && !(*WriteProc)(WriteProcArg, buff_ptr, num_chars)) {
return -1;
}
if (format.justification_options == left_justification) {
while (field_width < format.field_width) {
wchar_t blank = L' ';
if ((*WriteProc)(WriteProcArg, &blank, 1) == 0) {
return -1;
}
++field_width;
}
}
chars_written += field_width;
}
return chars_written;
}
typedef struct {
wchar_t *wCharStr;
size_t MaxCharCount;
size_t CharsWritten;
} __wOutStrCtrl;
void *__wStringWrite(void *wosc, const wchar_t *buffer, size_t numchars) {
size_t tobewritten;
void *res;
__wOutStrCtrl *wOscp = (__wOutStrCtrl *) wosc;
tobewritten =
((wOscp->CharsWritten + numchars) <= wOscp->MaxCharCount) ? numchars : wOscp->MaxCharCount - wOscp->CharsWritten;
res = (void *) wmemcpy(wOscp->wCharStr + wOscp->CharsWritten, buffer, tobewritten);
wOscp->CharsWritten += tobewritten;
return res;
}
int swprintf(wchar_t *s, size_t n, const wchar_t *format, ...) {
va_list args;
va_start(args, format);
return vswprintf(s, n, format, args);
}
int vswprintf(wchar_t *s, size_t n, const wchar_t *format, va_list arg) {
int count;
__wOutStrCtrl wosc;
wosc.wCharStr = s;
wosc.MaxCharCount = n;
wosc.CharsWritten = 0;
count = __wpformatter(&__wStringWrite, &wosc, format, arg, 0);
if (count >= 0) {
if (count < n) {
s[count] = 0;
} else {
*(s + n - 1) = 0;
count = -1;
}
}
return count;
}