#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); }