marioparty4/src/MSL_C.PPCEABI.bare.H/printf.c

1139 lines
28 KiB
C

#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/printf.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/ansi_fp.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/direct_io.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/ctype.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/scanf.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/stdio.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/stdlib.h"
#include "string.h"
#include "PowerPC_EABI_Support/Msl/MSL_C/MSL_Common/wchar_io.h"
#include "stdarg.h"
extern void __num2dec(const decform*, double, decimal*);
#define TARGET_FLOAT_BITS 64
#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)
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,
long_double_argument,
wchar_argument
};
typedef struct {
unsigned char justification_options;
unsigned char sign_options;
unsigned char precision_specified;
unsigned char alternate_form;
unsigned char argument_options;
unsigned char conversion_char;
int field_width;
int precision;
} print_format;
static const char* parse_format(const char *format_string, va_list *arg, print_format *format) {
print_format f;
const char* 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) == '%') {
f.conversion_char = c;
*format = f;
return ((const char*)s + 1);
}
for (;;) {
flag_found = 1;
switch (c) {
case '-':
f.justification_options = left_justification;
break;
case '+':
f.sign_options = sign_always;
break;
case ' ':
if (f.sign_options != sign_always) {
f.sign_options = space_holder;
}
break;
case '#':
f.alternate_form = 1;
break;
case '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 == '*') {
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 - '0');
c = *++s;
}
}
if (f.field_width > 509) {
f.conversion_char = 0xFF;
*format = f;
return ((const char*)s + 1);
}
if (c == '.') {
f.precision_specified = 1;
if ((c = *++s) == '*') {
if ((f.precision = va_arg(*arg, int)) < 0) {
f.precision_specified = 0;
}
c = *++s;
}
else {
while (isdigit(c)) {
f.precision = (f.precision * 10) + (c - '0');
c = *++s;
}
}
}
flag_found = 1;
switch (c) {
case 'h':
f.argument_options = short_argument;
if (s[1] == 'h') {
f.argument_options = char_argument;
c = *++s;
}
break;
case 'l':
f.argument_options = long_argument;
if (s[1] == 'l') {
f.argument_options = long_long_argument;
c = *++s;
}
break;
case 'L':
f.argument_options = long_double_argument;
break;
default:
flag_found = 0;
break;
}
if (flag_found) {
c = *++s;
}
f.conversion_char = c;
switch (c) {
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
if (f.argument_options == long_double_argument) {
f.conversion_char = 0xFF;
break;
}
if (!f.precision_specified) {
f.precision = 1;
}
else if (f.justification_options == zero_fill) {
f.justification_options = right_justification;
}
break;
case 'f':
case 'F':
if (f.argument_options == short_argument || f.argument_options == long_long_argument) {
f.conversion_char = 0xFF;
break;
}
if (!f.precision_specified) {
f.precision = 6;
}
break;
case 'a':
case 'A':
if (!f.precision_specified) {
f.precision = 0xD;
}
if (f.argument_options == short_argument || f.argument_options == long_long_argument || f.argument_options == char_argument) {
f.conversion_char = 0xFF;
}
break;
case 'g':
case 'G':
if (!f.precision) {
f.precision = 1;
}
case 'e':
case 'E':
if (f.argument_options == short_argument || f.argument_options == long_long_argument || f.argument_options == char_argument) {
f.conversion_char = 0xFF;
break;
}
if (!f.precision_specified) {
f.precision = 6;
}
break;
case 'p':
f.conversion_char = 'x';
f.alternate_form = 1;
f.argument_options = long_argument;
f.precision = 8;
break;
case '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 = 0xFF;
}
}
break;
case 's':
if (f.argument_options == long_argument) {
f.argument_options = wchar_argument;
}
else {
if (f.argument_options != normal_argument) {
f.conversion_char = 0xFF;
}
}
break;
case 'n':
if (f.argument_options == long_double_argument) {
f.conversion_char = 0xFF;
}
break;
default:
f.conversion_char = 0xFF;
break;
}
*format = f;
return ((const char*)s + 1);
}
static char* long2str(long num, char* buff, print_format format)
{
unsigned long unsigned_num, base;
char* 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 == 'o')) {
return p;
}
switch (format.conversion_char) {
case 'd':
case 'i':
base = 10;
if (num < 0) {
unsigned_num = -unsigned_num;
minus = 1;
}
break;
case 'o':
base = 8;
format.sign_options = only_minus;
break;
case 'u':
base = 10;
format.sign_options = only_minus;
break;
case 'x':
case 'X':
base = 16;
format.sign_options = only_minus;
break;
}
do {
n = unsigned_num % base;
unsigned_num /= base;
if (n < 10) {
n += '0';
} else {
n -= 10;
if (format.conversion_char == 'x') {
n += 'a';
} else {
n += 'A';
}
}
*--p = n;
++digits;
} while (unsigned_num != 0);
if (base == 8 && format.alternate_form && *p != '0') {
*--p = '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 > 509)
return (0);
while (digits < format.precision) {
*--p = '0';
++digits;
}
if (base == 16 && format.alternate_form) {
*--p = format.conversion_char;
*--p = '0';
}
if (minus) {
*--p = '-';
} else if (format.sign_options == sign_always) {
*--p = '+';
} else if (format.sign_options == space_holder) {
*--p = ' ';
}
return p;
}
static char* longlong2str(long long num, char* pBuf, print_format fmt)
{
unsigned long long unsigned_num, base;
char* 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 == 'o')) {
return p;
}
switch (fmt.conversion_char) {
case 'd':
case 'i':
base = 10;
if (num < 0) {
unsigned_num = -unsigned_num;
minus = 1;
}
break;
case 'o':
base = 8;
fmt.sign_options = only_minus;
break;
case 'u':
base = 10;
fmt.sign_options = only_minus;
break;
case 'x':
case 'X':
base = 16;
fmt.sign_options = only_minus;
break;
}
do {
n = unsigned_num % base;
unsigned_num /= base;
if (n < 10) {
n += '0';
} else {
n -= 10;
if (fmt.conversion_char == 'x') {
n += 'a';
} else {
n += 'A';
}
}
*--p = n;
++digits;
} while (unsigned_num != 0);
if (base == 8 && fmt.alternate_form && *p != '0') {
*--p = '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 > 509) {
return 0;
}
while (digits < fmt.precision) {
*--p = '0';
++digits;
}
if (base == 16 && fmt.alternate_form) {
*--p = fmt.conversion_char;
*--p = '0';
}
if (minus) {
*--p = '-';
} else if (fmt.sign_options == sign_always) {
*--p = '+';
} else if (fmt.sign_options == space_holder) {
*--p = ' ';
}
return p;
}
static char * double2hex(long double num, char * buff, print_format format);
static void round_decimal(decimal* dec, int new_length)
{
char c;
char* p;
int carry;
if (new_length < 0) {
return_zero:
dec->exp = 0;
dec->sig.length = 1;
*dec->sig.text = '0';
return;
}
if (new_length >= dec->sig.length) {
return;
}
p = (char*)dec->sig.text + new_length + 1;
c = *--p - '0';
if (c == 5) {
char* q = &((char*)dec->sig.text)[dec->sig.length];
while (--q > p && *q == '0')
;
carry = (q == p) ? p[-1] & 1 : 1;
} else {
carry = (c > 5);
}
while (new_length != 0) {
c = *--p - '0' + carry;
if ((carry = (c > 9)) != 0 || c == 0) {
--new_length;
} else {
*p = c + '0';
break;
}
}
if (carry != 0) {
dec->exp += 1;
dec->sig.length = 1;
*dec->sig.text = '1';
return;
} else if (new_length == 0) {
goto return_zero;
}
dec->sig.length = new_length;
}
static char* float2str(long double num, char *buff, print_format format) {
decimal dec;
decform form;
char* p;
char* q;
int n, digits, sign;
int int_digits, frac_digits;
if (format.precision > 509) {
return 0;
}
form.style = 0;
form.digits = 0x20;
__num2dec(&form, num, &dec);
p = (char*)dec.sig.text + dec.sig.length;
while (dec.sig.length > 1 && *--p == '0') {
--dec.sig.length;
++dec.exp;
}
switch (*dec.sig.text) {
case '0':
dec.exp = 0;
break;
case 'I':
if (num < 0) {
p = buff - 5;
if (isupper(format.conversion_char)) {
strcpy(p, "-INF");
}
else {
strcpy(p, "-inf");
}
}
else {
p = buff - 4;
if (isupper(format.conversion_char)) {
strcpy(p, "INF");
}
else {
strcpy(p, "inf");
}
}
return p;
case 'N':
if (dec.sign) {
p = buff - 5;
if (isupper(format.conversion_char)) {
strcpy(p, "-NAN");
}
else {
strcpy(p, "-nan");
}
}
else {
p = buff - 4;
if (isupper(format.conversion_char)) {
strcpy(p, "NAN");
}
else {
strcpy(p, "nan");
}
}
return p;
}
dec.exp += dec.sig.length - 1;
p = buff;
*--p = 0;
switch (format.conversion_char)
{
case 'g':
case '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 == 'g') {
format.conversion_char = 'e';
}
else {
format.conversion_char = '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 'e':
case 'E':
e_format:
if (dec.sig.length > format.precision + 1) {
round_decimal(&dec, format.precision + 1);
}
n = dec.exp;
sign = '+';
if (n < 0) {
n = -n;
sign = '-';
}
for (digits = 0; n || digits < 2; ++digits) {
*--p = n % 10 + '0';
n /= 10;
}
*--p = sign;
*--p = format.conversion_char;
if (buff - p + format.precision > 509) {
return 0;
}
if (dec.sig.length < format.precision + 1) {
for (n = format.precision + 1 - dec.sig.length + 1; --n;) {
*--p = '0';
}
}
for (n = dec.sig.length, q = (char*)dec.sig.text + dec.sig.length; --n;) {
*--p = *--q;
}
if (format.precision || format.alternate_form) {
*--p = '.';
}
*--p = *dec.sig.text;
if (dec.sign)
*--p = '-';
else if (format.sign_options == sign_always)
*--p = '+';
else if (format.sign_options == space_holder)
*--p = ' ';
break;
case 'f':
case '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 > 509)
return 0;
q = (char *) dec.sig.text + dec.sig.length;
for (digits = 0; digits < (format.precision - frac_digits); ++digits)
*--p = '0';
for (digits = 0; digits < frac_digits && digits < dec.sig.length; ++digits)
*--p = *--q;
for (; digits < frac_digits; ++digits)
*--p = '0';
if (format.precision || format.alternate_form)
*--p = '.';
if (int_digits) {
for (digits = 0; digits < int_digits - dec.sig.length; ++digits) {
*--p = '0';
}
for (; digits < int_digits; ++digits) {
*--p = *--q;
}
}
else {
*--p = '0';
}
if (dec.sign) {
*--p = '-';
}
else if (format.sign_options == sign_always) {
*--p = '+';
}
else if (format.sign_options == space_holder) {
*--p = ' ';
}
break;
}
return p;
}
static int __pformatter(void *(*WriteProc)(void *, const char *, size_t), void *WriteProcArg, const char * format_str, va_list arg) {
int num_chars, chars_written, field_width;
const char* format_ptr;
const char* curr_format;
print_format format;
long long_num;
long long long_long_num;
long double long_double_num;
char buff[512];
char* buff_ptr;
char* string_end;
char fill_char = ' ';
format_ptr = format_str;
chars_written = 0;
while (*format_ptr) {
if (!(curr_format = strchr(format_ptr, '%'))) {
num_chars = strlen(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, (va_list*)arg, &format);
switch (format.conversion_char) {
case 'd':
case '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 {
long_num = va_arg(arg, int);
}
if (format.argument_options == short_argument) {
long_num = (short)long_num;
}
if (format.argument_options == char_argument) {
long_num = (signed char)long_num;
}
if (format.argument_options == long_long_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 'o':
case 'u':
case 'x':
case '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 {
long_num = va_arg(arg, unsigned int);
}
if (format.argument_options == short_argument) {
long_num = (unsigned short)long_num;
}
if (format.argument_options == char_argument) {
long_num = (unsigned char)long_num;
}
if (format.argument_options == long_long_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 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case '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 'a':
case '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 's':
if (format.argument_options == wchar_argument) {
wchar_t* wcs_ptr = va_arg(arg, wchar_t*);
if(wcs_ptr == NULL){
wcs_ptr = L"";
}
if ((num_chars = wcstombs(buff, wcs_ptr, sizeof(buff))) < 0) {
goto conversion_error;
}
buff_ptr = &buff[0];
}
else {
buff_ptr = va_arg(arg, char *);
}
if (buff_ptr == NULL) {
buff_ptr = "";
}
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 = (char*)memchr((unsigned char*)buff_ptr, 0, num_chars)) != 0) {
num_chars = string_end - buff_ptr;
}
}
else {
num_chars = strlen(buff_ptr);
}
break;
case 'n':
buff_ptr = va_arg(arg, char *);
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 long_long_argument:
*(long long*)buff_ptr = chars_written;
break;
}
continue;
case 'c':
buff_ptr = buff;
*buff_ptr = va_arg(arg, int);
num_chars = 1;
break;
case '%':
buff_ptr = buff;
*buff_ptr = '%';
num_chars = 1;
break;
case 0xFF:
default:
conversion_error:
num_chars = strlen(curr_format);
chars_written += num_chars;
if (num_chars && !(*WriteProc)(WriteProcArg, curr_format, num_chars)) {
return -1;
}
return chars_written;
break;
}
field_width = num_chars;
if (format.justification_options != left_justification) {
fill_char = (format.justification_options == zero_fill) ? '0' : ' ';
if (((*buff_ptr == '+') || (*buff_ptr == '-') || (*buff_ptr == ' ')) && (fill_char == '0')) {
if ((*WriteProc)(WriteProcArg, buff_ptr, 1) == 0) {
return -1;
}
++buff_ptr;
num_chars--;
}
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) {
char blank = ' ';
if ((*WriteProc)(WriteProcArg, &blank, 1) == 0) {
return -1;
}
++field_width;
}
}
chars_written += field_width;
}
return chars_written;
}
void* __FileWrite(void* pFile, const char* pBuffer, size_t char_num)
{
return (fwrite(pBuffer, 1, char_num, (FILE*)pFile) == char_num ? pFile : 0);
}
void* __StringWrite(void* pCtrl, const char* pBuffer, size_t char_num)
{
size_t chars;
__OutStrCtrl* ctrl = (__OutStrCtrl*)pCtrl;
chars = ((ctrl->CharsWritten + char_num) <= ctrl->MaxCharCount)
? char_num
: ctrl->MaxCharCount - ctrl->CharsWritten;
memcpy(ctrl->CharStr + ctrl->CharsWritten, pBuffer, chars);
ctrl->CharsWritten += chars;
return (void*) 1;
}
int printf(const char* format, ...)
{
int res;
if (fwide(stdout, -1) >= 0) {
return -1;
}
{
va_list args;
va_start(args, format);
res = __pformatter(&__FileWrite, (void*)stdout, format, args);
}
return res;
}
int vprintf(const char* format, va_list arg)
{
int ret;
if (fwide(stdout, -1) >= 0) {
return -1;
}
ret = __pformatter(&__FileWrite, (void*)stdout, format, arg);
return ret;
}
int vsnprintf(char* s, size_t n, const char* format, va_list arg)
{
int end;
__OutStrCtrl osc;
osc.CharStr = s;
osc.MaxCharCount = n;
osc.CharsWritten = 0;
end = __pformatter(&__StringWrite, &osc, format, arg);
if (s) {
s[(end < n) ? end : n - 1] = '\0';
}
return end;
}
int vsprintf(char *s, const char *format, va_list arg) {
return vsnprintf(s, 0xFFFFFFFF, format, arg);
}
int sprintf(char* s, const char* format, ...)
{
va_list args;
va_start(args, format);
return vsnprintf(s, 0xFFFFFFFF, format, args);
}