diff options
Diffstat (limited to 'Build/source/libs/icu/icu-xetex/io/uprntf_p.c')
-rw-r--r-- | Build/source/libs/icu/icu-xetex/io/uprntf_p.c | 1374 |
1 files changed, 1374 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-xetex/io/uprntf_p.c b/Build/source/libs/icu/icu-xetex/io/uprntf_p.c new file mode 100644 index 00000000000..08236762210 --- /dev/null +++ b/Build/source/libs/icu/icu-xetex/io/uprntf_p.c @@ -0,0 +1,1374 @@ +/* +****************************************************************************** +* +* Copyright (C) 1998-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File uprntf_p.c +* +* Modification History: +* +* Date Name Description +* 11/23/98 stephen Creation. +* 03/12/99 stephen Modified for new C API. +* 08/07/2003 george Reunify printf implementations +****************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ustring.h" + +#include "uprintf.h" +#include "ufmt_cmn.h" +#include "cmemory.h" +#include "putilimp.h" + +/* ANSI style formatting */ +/* Use US-ASCII characters only for formatting */ + +/* % */ +#define UFMT_SIMPLE_PERCENT {ufmt_simple_percent, u_printf_simple_percent_handler} +/* s */ +#define UFMT_STRING {ufmt_string, u_printf_string_handler} +/* c */ +#define UFMT_CHAR {ufmt_char, u_printf_char_handler} +/* d, i */ +#define UFMT_INT {ufmt_int, u_printf_integer_handler} +/* u */ +#define UFMT_UINT {ufmt_int, u_printf_uinteger_handler} +/* o */ +#define UFMT_OCTAL {ufmt_int, u_printf_octal_handler} +/* x, X */ +#define UFMT_HEX {ufmt_int, u_printf_hex_handler} +/* f */ +#define UFMT_DOUBLE {ufmt_double, u_printf_double_handler} +/* e, E */ +#define UFMT_SCIENTIFIC {ufmt_double, u_printf_scientific_handler} +/* g, G */ +#define UFMT_SCIDBL {ufmt_double, u_printf_scidbl_handler} +/* n */ +#define UFMT_COUNT {ufmt_count, u_printf_count_handler} + +/* non-ANSI extensions */ +/* Use US-ASCII characters only for formatting */ + +/* p */ +#define UFMT_POINTER {ufmt_pointer, u_printf_pointer_handler} +/* V */ +#define UFMT_SPELLOUT {ufmt_double, u_printf_spellout_handler} +/* P */ +#define UFMT_PERCENT {ufmt_double, u_printf_percent_handler} +/* C K is old format */ +#define UFMT_UCHAR {ufmt_uchar, u_printf_uchar_handler} +/* S U is old format */ +#define UFMT_USTRING {ufmt_ustring, u_printf_ustring_handler} + + +#define UFMT_EMPTY {ufmt_empty, NULL} + +/** + * A u_printf handler function. + * A u_printf handler is responsible for handling a single u_printf + * format specification, for example 'd' or 's'. + * @param stream The UFILE to which to write output. + * @param info A pointer to a <TT>u_printf_spec_info</TT> struct containing + * information on the format specification. + * @param args A pointer to the argument data + * @return The number of Unicode characters written to <TT>stream</TT>. + */ +typedef int32_t U_EXPORT2 +u_printf_handler(const u_printf_stream_handler *handler, + + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args); + +typedef struct u_printf_info { + ufmt_type_info info; + u_printf_handler *handler; +} u_printf_info; + +/** + * Struct encapsulating a single uprintf format specification. + */ +typedef struct u_printf_spec { + u_printf_spec_info fInfo; /* Information on this spec */ + int32_t fWidthPos; /* Position of width in arg list */ + int32_t fPrecisionPos; /* Position of precision in arg list */ + int32_t fArgPos; /* Position of data in arg list */ +} u_printf_spec; + +#define UPRINTF_NUM_FMT_HANDLERS 108 + +/* We do not use handlers for 0-0x1f */ +#define UPRINTF_BASE_FMT_HANDLERS 0x20 + +/* buffer size for formatting */ +#define UPRINTF_BUFFER_SIZE 1024 +#define UPRINTF_SYMBOL_BUFFER_SIZE 8 + +static const UChar gNullStr[] = {0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0}; /* "(null)" */ +static const UChar gSpaceStr[] = {0x20, 0}; /* " " */ + +/* Sets the sign of a format based on u_printf_spec_info */ +/* TODO: Is setting the prefix symbol to a positive sign a good idea in all locales? */ +static void +u_printf_set_sign(UNumberFormat *format, + const u_printf_spec_info *info, + UChar *prefixBuffer, + int32_t *prefixBufLen, + UErrorCode *status) +{ + if(info->fShowSign) { + *prefixBufLen = unum_getTextAttribute(format, + UNUM_POSITIVE_PREFIX, + prefixBuffer, + *prefixBufLen, + status); + if (info->fSpace) { + /* Setting UNUM_PLUS_SIGN_SYMBOL affects the exponent too. */ + /* unum_setSymbol(format, UNUM_PLUS_SIGN_SYMBOL, gSpaceStr, 1, &status); */ + unum_setTextAttribute(format, UNUM_POSITIVE_PREFIX, gSpaceStr, 1, status); + } + else { + UChar plusSymbol[UPRINTF_SYMBOL_BUFFER_SIZE]; + int32_t symbolLen; + + symbolLen = unum_getSymbol(format, + UNUM_PLUS_SIGN_SYMBOL, + plusSymbol, + sizeof(plusSymbol)/sizeof(*plusSymbol), + status); + unum_setTextAttribute(format, + UNUM_POSITIVE_PREFIX, + plusSymbol, + symbolLen, + status); + } + } + else { + *prefixBufLen = 0; + } +} + +static void +u_printf_reset_sign(UNumberFormat *format, + const u_printf_spec_info *info, + UChar *prefixBuffer, + int32_t *prefixBufLen, + UErrorCode *status) +{ + if(info->fShowSign) { + unum_setTextAttribute(format, + UNUM_POSITIVE_PREFIX, + prefixBuffer, + *prefixBufLen, + status); + } +} + + +/* handle a '%' */ +static int32_t +u_printf_simple_percent_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + static const UChar PERCENT[] = { UP_PERCENT }; + + /* put a single '%' onto the output */ + return handler->write(context, PERCENT, 1); +} + +/* handle 's' */ +static int32_t +u_printf_string_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + UChar *s; + UChar buffer[UFMT_DEFAULT_BUFFER_SIZE]; + int32_t len, written; + int32_t argSize; + const char *arg = (const char*)(args[0].ptrValue); + + /* convert from the default codepage to Unicode */ + if (arg) { + argSize = (int32_t)strlen(arg) + 1; + if (argSize >= MAX_UCHAR_BUFFER_SIZE(buffer)) { + s = ufmt_defaultCPToUnicode(arg, argSize, + (UChar *)uprv_malloc(MAX_UCHAR_BUFFER_NEEDED(argSize)), + MAX_UCHAR_BUFFER_NEEDED(argSize)); + if(s == NULL) { + return 0; + } + } + else { + s = ufmt_defaultCPToUnicode(arg, argSize, buffer, + sizeof(buffer)/sizeof(UChar)); + } + } + else { + s = (UChar *)gNullStr; + } + len = u_strlen(s); + + /* width = minimum # of characters to write */ + /* precision = maximum # of characters to write */ + if (info->fPrecision != -1 && info->fPrecision < len) { + len = info->fPrecision; + } + + written = handler->pad_and_justify(context, info, s, len); + + /* clean up */ + if (gNullStr != s && buffer != s) { + uprv_free(s); + } + + return written; +} + +static int32_t +u_printf_char_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + UChar s[UTF_MAX_CHAR_LENGTH+1]; + int32_t len = 1, written; + unsigned char arg = (unsigned char)(args[0].int64Value); + + /* convert from default codepage to Unicode */ + ufmt_defaultCPToUnicode((const char *)&arg, 2, s, sizeof(s)/sizeof(UChar)); + + /* Remember that this may be an MBCS character */ + if (arg != 0) { + len = u_strlen(s); + } + + /* width = minimum # of characters to write */ + /* precision = maximum # of characters to write */ + /* precision is ignored when handling a char */ + + written = handler->pad_and_justify(context, info, s, len); + + return written; +} + +static int32_t +u_printf_double_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + double num = (double) (args[0].doubleValue); + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; + int32_t prefixBufferLen = sizeof(prefixBuffer); + int32_t minDecimalDigits; + int32_t maxDecimalDigits; + int32_t resultLen; + UErrorCode status = U_ZERO_ERROR; + + prefixBuffer[0] = 0; + + /* mask off any necessary bits */ + /* if(! info->fIsLongDouble) + num &= DBL_MAX;*/ + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); + + /* handle error */ + if(format == 0) + return 0; + + /* save the formatter's state */ + minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); + maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); + + /* set the appropriate flags and number of decimal digits on the formatter */ + if(info->fPrecision != -1) { + /* set the # of decimal digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); + } + else if(info->fAlt) { + /* '#' means always show decimal point */ + /* copy of printf behavior on Solaris - '#' shows 6 digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + else { + /* # of decimal digits is 6 if precision not specified regardless of locale */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + + /* set whether to show the sign */ + if (info->fShowSign) { + u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); + } + + /* format the number */ + resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + /* TODO: Is this needed? */ + unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); + unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); + + if (info->fShowSign) { + /* Reset back to original value regardless of what the error was */ + UErrorCode localStatus = U_ZERO_ERROR; + u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +/* HSYS */ +static int32_t +u_printf_integer_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int64_t num = args[0].int64Value; + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; + int32_t prefixBufferLen = sizeof(prefixBuffer); + int32_t minDigits = -1; + int32_t resultLen; + UErrorCode status = U_ZERO_ERROR; + + prefixBuffer[0] = 0; + + /* mask off any necessary bits */ + if (info->fIsShort) + num = (int16_t)num; + else if (!info->fIsLongLong) + num = (int32_t)num; + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); + + /* handle error */ + if(format == 0) + return 0; + + /* set the appropriate flags on the formatter */ + + /* set the minimum integer digits */ + if(info->fPrecision != -1) { + /* set the minimum # of digits */ + minDigits = unum_getAttribute(format, UNUM_MIN_INTEGER_DIGITS); + unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, info->fPrecision); + } + + /* set whether to show the sign */ + if(info->fShowSign) { + u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); + } + + /* format the number */ + resultLen = unum_formatInt64(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + if (minDigits != -1) { + unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, minDigits); + } + + if (info->fShowSign) { + /* Reset back to original value regardless of what the error was */ + UErrorCode localStatus = U_ZERO_ERROR; + u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +static int32_t +u_printf_hex_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int64_t num = args[0].int64Value; + UChar result[UPRINTF_BUFFER_SIZE]; + int32_t len = UPRINTF_BUFFER_SIZE; + + + /* mask off any necessary bits */ + if (info->fIsShort) + num &= UINT16_MAX; + else if (!info->fIsLongLong) + num &= UINT32_MAX; + + /* format the number, preserving the minimum # of digits */ + ufmt_64tou(result, &len, num, 16, + (UBool)(info->fSpec == 0x0078), + (info->fPrecision == -1 && info->fZero) ? info->fWidth : info->fPrecision); + + /* convert to alt form, if desired */ + if(num != 0 && info->fAlt && len < UPRINTF_BUFFER_SIZE - 2) { + /* shift the formatted string right by 2 chars */ + memmove(result + 2, result, len * sizeof(UChar)); + result[0] = 0x0030; + result[1] = info->fSpec; + len += 2; + } + + return handler->pad_and_justify(context, info, result, len); +} + +static int32_t +u_printf_octal_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int64_t num = args[0].int64Value; + UChar result[UPRINTF_BUFFER_SIZE]; + int32_t len = UPRINTF_BUFFER_SIZE; + + + /* mask off any necessary bits */ + if (info->fIsShort) + num &= UINT16_MAX; + else if (!info->fIsLongLong) + num &= UINT32_MAX; + + /* format the number, preserving the minimum # of digits */ + ufmt_64tou(result, &len, num, 8, + FALSE, /* doesn't matter for octal */ + info->fPrecision == -1 && info->fZero ? info->fWidth : info->fPrecision); + + /* convert to alt form, if desired */ + if(info->fAlt && result[0] != 0x0030 && len < UPRINTF_BUFFER_SIZE - 1) { + /* shift the formatted string right by 1 char */ + memmove(result + 1, result, len * sizeof(UChar)); + result[0] = 0x0030; + len += 1; + } + + return handler->pad_and_justify(context, info, result, len); +} + +static int32_t +u_printf_uinteger_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int64_t num = args[0].int64Value; + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + int32_t minDigits = -1; + int32_t resultLen; + UErrorCode status = U_ZERO_ERROR; + + /* TODO: Fix this once uint64_t can be formatted. */ + if (info->fIsShort) + num &= UINT16_MAX; + else if (!info->fIsLongLong) + num &= UINT32_MAX; + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); + + /* handle error */ + if(format == 0) + return 0; + + /* set the appropriate flags on the formatter */ + + /* set the minimum integer digits */ + if(info->fPrecision != -1) { + /* set the minimum # of digits */ + minDigits = unum_getAttribute(format, UNUM_MIN_INTEGER_DIGITS); + unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, info->fPrecision); + } + + /* To mirror other stdio implementations, we ignore the sign argument */ + + /* format the number */ + resultLen = unum_formatInt64(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + if (minDigits != -1) { + unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, minDigits); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +static int32_t +u_printf_pointer_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + UChar result[UPRINTF_BUFFER_SIZE]; + int32_t len = UPRINTF_BUFFER_SIZE; + + /* format the pointer in hex */ + ufmt_ptou(result, &len, args[0].ptrValue, TRUE/*, info->fPrecision*/); + + return handler->pad_and_justify(context, info, result, len); +} + +static int32_t +u_printf_scientific_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + double num = (double) (args[0].doubleValue); + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; + int32_t prefixBufferLen = sizeof(prefixBuffer); + int32_t minDecimalDigits; + int32_t maxDecimalDigits; + UErrorCode status = U_ZERO_ERROR; + UChar srcExpBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; + int32_t srcLen, expLen; + int32_t resultLen; + UChar expBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; + + prefixBuffer[0] = 0; + + /* mask off any necessary bits */ + /* if(! info->fIsLongDouble) + num &= DBL_MAX;*/ + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_SCIENTIFIC); + + /* handle error */ + if(format == 0) + return 0; + + /* set the appropriate flags on the formatter */ + + srcLen = unum_getSymbol(format, + UNUM_EXPONENTIAL_SYMBOL, + srcExpBuf, + sizeof(srcExpBuf), + &status); + + /* Upper/lower case the e */ + if (info->fSpec == (UChar)0x65 /* e */) { + expLen = u_strToLower(expBuf, (int32_t)sizeof(expBuf), + srcExpBuf, srcLen, + formatBundle->fLocale, + &status); + } + else { + expLen = u_strToUpper(expBuf, (int32_t)sizeof(expBuf), + srcExpBuf, srcLen, + formatBundle->fLocale, + &status); + } + + unum_setSymbol(format, + UNUM_EXPONENTIAL_SYMBOL, + expBuf, + expLen, + &status); + + /* save the formatter's state */ + minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); + maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); + + /* set the appropriate flags and number of decimal digits on the formatter */ + if(info->fPrecision != -1) { + /* set the # of decimal digits */ + if (info->fOrigSpec == (UChar)0x65 /* e */ || info->fOrigSpec == (UChar)0x45 /* E */) { + unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); + } + else { + unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, 1); + unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, info->fPrecision); + } + } + else if(info->fAlt) { + /* '#' means always show decimal point */ + /* copy of printf behavior on Solaris - '#' shows 6 digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + else { + /* # of decimal digits is 6 if precision not specified */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + + /* set whether to show the sign */ + if (info->fShowSign) { + u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); + } + + /* format the number */ + resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + /* TODO: Is this needed? */ + unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); + unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); + + /* Since we're the only one using the scientific + format, we don't need to save the old exponent value. */ + /*unum_setSymbol(format, + UNUM_EXPONENTIAL_SYMBOL, + srcExpBuf, + srcLen, + &status);*/ + + if (info->fShowSign) { + /* Reset back to original value regardless of what the error was */ + UErrorCode localStatus = U_ZERO_ERROR; + u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +static int32_t +u_printf_percent_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + double num = (double) (args[0].doubleValue); + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; + int32_t prefixBufferLen = sizeof(prefixBuffer); + int32_t minDecimalDigits; + int32_t maxDecimalDigits; + int32_t resultLen; + UErrorCode status = U_ZERO_ERROR; + + prefixBuffer[0] = 0; + + /* mask off any necessary bits */ + /* if(! info->fIsLongDouble) + num &= DBL_MAX;*/ + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_PERCENT); + + /* handle error */ + if(format == 0) + return 0; + + /* save the formatter's state */ + minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); + maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); + + /* set the appropriate flags and number of decimal digits on the formatter */ + if(info->fPrecision != -1) { + /* set the # of decimal digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); + } + else if(info->fAlt) { + /* '#' means always show decimal point */ + /* copy of printf behavior on Solaris - '#' shows 6 digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + else { + /* # of decimal digits is 6 if precision not specified */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + + /* set whether to show the sign */ + if (info->fShowSign) { + u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); + } + + /* format the number */ + resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + /* TODO: Is this needed? */ + unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); + unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); + + if (info->fShowSign) { + /* Reset back to original value regardless of what the error was */ + UErrorCode localStatus = U_ZERO_ERROR; + u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +static int32_t +u_printf_ustring_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int32_t len, written; + const UChar *arg = (const UChar*)(args[0].ptrValue); + + /* allocate enough space for the buffer */ + if (arg == NULL) { + arg = gNullStr; + } + len = u_strlen(arg); + + /* width = minimum # of characters to write */ + /* precision = maximum # of characters to write */ + if (info->fPrecision != -1 && info->fPrecision < len) { + len = info->fPrecision; + } + + /* determine if the string should be padded */ + written = handler->pad_and_justify(context, info, arg, len); + + return written; +} + +static int32_t +u_printf_uchar_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int32_t written = 0; + UChar arg = (UChar)(args[0].int64Value); + + /* width = minimum # of characters to write */ + /* precision = maximum # of characters to write */ + /* precision is ignored when handling a uchar */ + + /* determine if the string should be padded */ + written = handler->pad_and_justify(context, info, &arg, 1); + + return written; +} + +static int32_t +u_printf_scidbl_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + u_printf_spec_info scidbl_info; + double num = args[0].doubleValue; + int32_t retVal; + + memcpy(&scidbl_info, info, sizeof(u_printf_spec_info)); + + /* determine whether to use 'd', 'e' or 'f' notation */ + if (scidbl_info.fPrecision == -1 && num == uprv_trunc(num)) + { + /* use 'f' notation */ + scidbl_info.fSpec = 0x0066; + scidbl_info.fPrecision = 0; + /* call the double handler */ + retVal = u_printf_double_handler(handler, context, formatBundle, &scidbl_info, args); + } + else if(num < 0.0001 || (scidbl_info.fPrecision < 1 && 1000000.0 <= num) + || (scidbl_info.fPrecision != -1 && num > uprv_pow10(scidbl_info.fPrecision))) + { + /* use 'e' or 'E' notation */ + scidbl_info.fSpec = scidbl_info.fSpec - 2; + if (scidbl_info.fPrecision == -1) { + scidbl_info.fPrecision = 5; + } + /* call the scientific handler */ + retVal = u_printf_scientific_handler(handler, context, formatBundle, &scidbl_info, args); + } + else { + UNumberFormat *format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); + int32_t maxSigDecimalDigits = unum_getAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS); + int32_t significantDigits = scidbl_info.fPrecision; + + /* use 'f' notation */ + scidbl_info.fSpec = 0x0066; + if (significantDigits == -1) { + significantDigits = 6; + } + unum_setAttribute(format, UNUM_SIGNIFICANT_DIGITS_USED, TRUE); + unum_setAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS, significantDigits); + /* call the double handler */ + retVal = u_printf_double_handler(handler, context, formatBundle, &scidbl_info, args); + unum_setAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS, maxSigDecimalDigits); + unum_setAttribute(format, UNUM_SIGNIFICANT_DIGITS_USED, FALSE); + } + return retVal; +} + +static int32_t +u_printf_count_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + int32_t *count = (int32_t*)(args[0].ptrValue); + + /* in the special case of count, the u_printf_spec_info's width */ + /* will contain the # of chars written thus far */ + *count = info->fWidth; + + return 0; +} + +static int32_t +u_printf_spellout_handler(const u_printf_stream_handler *handler, + void *context, + ULocaleBundle *formatBundle, + const u_printf_spec_info *info, + const ufmt_args *args) +{ + double num = (double) (args[0].doubleValue); + UNumberFormat *format; + UChar result[UPRINTF_BUFFER_SIZE]; + UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; + int32_t prefixBufferLen = sizeof(prefixBuffer); + int32_t minDecimalDigits; + int32_t maxDecimalDigits; + int32_t resultLen; + UErrorCode status = U_ZERO_ERROR; + + prefixBuffer[0] = 0; + + /* mask off any necessary bits */ + /* if(! info->fIsLongDouble) + num &= DBL_MAX;*/ + + /* get the formatter */ + format = u_locbund_getNumberFormat(formatBundle, UNUM_SPELLOUT); + + /* handle error */ + if(format == 0) + return 0; + + /* save the formatter's state */ + minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); + maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); + + /* set the appropriate flags and number of decimal digits on the formatter */ + if(info->fPrecision != -1) { + /* set the # of decimal digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); + } + else if(info->fAlt) { + /* '#' means always show decimal point */ + /* copy of printf behavior on Solaris - '#' shows 6 digits */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + else { + /* # of decimal digits is 6 if precision not specified */ + unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); + } + + /* set whether to show the sign */ + if (info->fShowSign) { + u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); + } + + /* format the number */ + resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); + + if (U_FAILURE(status)) { + resultLen = 0; + } + + /* restore the number format */ + /* TODO: Is this needed? */ + unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); + unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); + + if (info->fShowSign) { + /* Reset back to original value regardless of what the error was */ + UErrorCode localStatus = U_ZERO_ERROR; + u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); + } + + return handler->pad_and_justify(context, info, result, resultLen); +} + +/* Use US-ASCII characters only for formatting. Most codepages have + characters 20-7F from Unicode. Using any other codepage specific + characters will make it very difficult to format the string on + non-Unicode machines */ +static const u_printf_info g_u_printf_infos[UPRINTF_NUM_FMT_HANDLERS] = { +/* 0x20 */ + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_SIMPLE_PERCENT,UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + +/* 0x30 */ + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + +/* 0x40 */ + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR, + UFMT_EMPTY, UFMT_SCIENTIFIC, UFMT_EMPTY, UFMT_SCIDBL, +#ifdef U_USE_OBSOLETE_IO_FORMATTING + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR/*deprecated*/, +#else + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, +#endif + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + +/* 0x50 */ + UFMT_PERCENT, UFMT_EMPTY, UFMT_EMPTY, UFMT_USTRING, +#ifdef U_USE_OBSOLETE_IO_FORMATTING + UFMT_EMPTY, UFMT_USTRING/*deprecated*/,UFMT_SPELLOUT, UFMT_EMPTY, +#else + UFMT_EMPTY, UFMT_EMPTY, UFMT_SPELLOUT, UFMT_EMPTY, +#endif + UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + +/* 0x60 */ + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_CHAR, + UFMT_INT, UFMT_SCIENTIFIC, UFMT_DOUBLE, UFMT_SCIDBL, + UFMT_EMPTY, UFMT_INT, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_COUNT, UFMT_OCTAL, + +/* 0x70 */ + UFMT_POINTER, UFMT_EMPTY, UFMT_EMPTY, UFMT_STRING, + UFMT_EMPTY, UFMT_UINT, UFMT_EMPTY, UFMT_EMPTY, + UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, + UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, +}; + +/* flag characters for uprintf */ +#define FLAG_MINUS 0x002D +#define FLAG_PLUS 0x002B +#define FLAG_SPACE 0x0020 +#define FLAG_POUND 0x0023 +#define FLAG_ZERO 0x0030 +#define FLAG_PAREN 0x0028 + +#define ISFLAG(s) (s) == FLAG_MINUS || \ + (s) == FLAG_PLUS || \ + (s) == FLAG_SPACE || \ + (s) == FLAG_POUND || \ + (s) == FLAG_ZERO || \ + (s) == FLAG_PAREN + +/* special characters for uprintf */ +#define SPEC_ASTERISK 0x002A +#define SPEC_DOLLARSIGN 0x0024 +#define SPEC_PERIOD 0x002E +#define SPEC_PERCENT 0x0025 + +/* unicode digits */ +#define DIGIT_ZERO 0x0030 +#define DIGIT_ONE 0x0031 +#define DIGIT_TWO 0x0032 +#define DIGIT_THREE 0x0033 +#define DIGIT_FOUR 0x0034 +#define DIGIT_FIVE 0x0035 +#define DIGIT_SIX 0x0036 +#define DIGIT_SEVEN 0x0037 +#define DIGIT_EIGHT 0x0038 +#define DIGIT_NINE 0x0039 + +#define ISDIGIT(s) (s) == DIGIT_ZERO || \ + (s) == DIGIT_ONE || \ + (s) == DIGIT_TWO || \ + (s) == DIGIT_THREE || \ + (s) == DIGIT_FOUR || \ + (s) == DIGIT_FIVE || \ + (s) == DIGIT_SIX || \ + (s) == DIGIT_SEVEN || \ + (s) == DIGIT_EIGHT || \ + (s) == DIGIT_NINE + +/* u_printf modifiers */ +#define MOD_H 0x0068 +#define MOD_LOWERL 0x006C +#define MOD_L 0x004C + +#define ISMOD(s) (s) == MOD_H || \ + (s) == MOD_LOWERL || \ + (s) == MOD_L + +/* We parse the argument list in Unicode */ +U_CFUNC int32_t +u_printf_parse(const u_printf_stream_handler *streamHandler, + const UChar *fmt, + void *context, + u_localized_print_string *locStringContext, + ULocaleBundle *formatBundle, + int32_t *written, + va_list ap) +{ + uint16_t handlerNum; + ufmt_args args; + ufmt_type_info argType; + u_printf_handler *handler; + u_printf_spec spec; + u_printf_spec_info *info = &(spec.fInfo); + + const UChar *alias = fmt; + const UChar *backup; + const UChar *lastAlias; + + /* iterate through the pattern */ + while(!locStringContext || locStringContext->available > 0) { + + /* find the next '%' */ + lastAlias = alias; + while(*alias != UP_PERCENT && *alias != 0x0000) { + alias++; + } + + /* write any characters before the '%' */ + if(alias > lastAlias) { + *written += (streamHandler->write)(context, lastAlias, (int32_t)(alias - lastAlias)); + } + + /* break if at end of string */ + if(*alias == 0x0000) { + break; + } + + /* initialize spec to default values */ + spec.fWidthPos = -1; + spec.fPrecisionPos = -1; + spec.fArgPos = -1; + + uprv_memset(info, 0, sizeof(*info)); + info->fPrecision = -1; + info->fWidth = -1; + info->fPadChar = 0x0020; + + /* skip over the initial '%' */ + alias++; + + /* Check for positional argument */ + if(ISDIGIT(*alias)) { + + /* Save the current position */ + backup = alias; + + /* handle positional parameters */ + if(ISDIGIT(*alias)) { + spec.fArgPos = (int) (*alias++ - DIGIT_ZERO); + + while(ISDIGIT(*alias)) { + spec.fArgPos *= 10; + spec.fArgPos += (int) (*alias++ - DIGIT_ZERO); + } + } + + /* if there is no '$', don't read anything */ + if(*alias != SPEC_DOLLARSIGN) { + spec.fArgPos = -1; + alias = backup; + } + /* munge the '$' */ + else + alias++; + } + + /* Get any format flags */ + while(ISFLAG(*alias)) { + switch(*alias++) { + + /* left justify */ + case FLAG_MINUS: + info->fLeft = TRUE; + break; + + /* always show sign */ + case FLAG_PLUS: + info->fShowSign = TRUE; + break; + + /* use space if no sign present */ + case FLAG_SPACE: + info->fShowSign = TRUE; + info->fSpace = TRUE; + break; + + /* use alternate form */ + case FLAG_POUND: + info->fAlt = TRUE; + break; + + /* pad with leading zeroes */ + case FLAG_ZERO: + info->fZero = TRUE; + info->fPadChar = 0x0030; + break; + + /* pad character specified */ + case FLAG_PAREN: + + /* TODO test that all four are numbers */ + /* first four characters are hex values for pad char */ + info->fPadChar = (UChar)ufmt_digitvalue(*alias++); + info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); + info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); + info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); + + /* final character is ignored */ + alias++; + + break; + } + } + + /* Get the width */ + + /* width is specified out of line */ + if(*alias == SPEC_ASTERISK) { + + info->fWidth = -2; + + /* Skip the '*' */ + alias++; + + /* Save the current position */ + backup = alias; + + /* handle positional parameters */ + if(ISDIGIT(*alias)) { + spec.fWidthPos = (int) (*alias++ - DIGIT_ZERO); + + while(ISDIGIT(*alias)) { + spec.fWidthPos *= 10; + spec.fWidthPos += (int) (*alias++ - DIGIT_ZERO); + } + } + + /* if there is no '$', don't read anything */ + if(*alias != SPEC_DOLLARSIGN) { + spec.fWidthPos = -1; + alias = backup; + } + /* munge the '$' */ + else + alias++; + } + /* read the width, if present */ + else if(ISDIGIT(*alias)){ + info->fWidth = (int) (*alias++ - DIGIT_ZERO); + + while(ISDIGIT(*alias)) { + info->fWidth *= 10; + info->fWidth += (int) (*alias++ - DIGIT_ZERO); + } + } + + /* Get the precision */ + + if(*alias == SPEC_PERIOD) { + + /* eat up the '.' */ + alias++; + + /* precision is specified out of line */ + if(*alias == SPEC_ASTERISK) { + + info->fPrecision = -2; + + /* Skip the '*' */ + alias++; + + /* save the current position */ + backup = alias; + + /* handle positional parameters */ + if(ISDIGIT(*alias)) { + spec.fPrecisionPos = (int) (*alias++ - DIGIT_ZERO); + + while(ISDIGIT(*alias)) { + spec.fPrecisionPos *= 10; + spec.fPrecisionPos += (int) (*alias++ - DIGIT_ZERO); + } + + /* if there is no '$', don't read anything */ + if(*alias != SPEC_DOLLARSIGN) { + spec.fPrecisionPos = -1; + alias = backup; + } + else { + /* munge the '$' */ + alias++; + } + } + } + /* read the precision */ + else if(ISDIGIT(*alias)){ + info->fPrecision = (int) (*alias++ - DIGIT_ZERO); + + while(ISDIGIT(*alias)) { + info->fPrecision *= 10; + info->fPrecision += (int) (*alias++ - DIGIT_ZERO); + } + } + } + + /* Get any modifiers */ + if(ISMOD(*alias)) { + switch(*alias++) { + + /* short */ + case MOD_H: + info->fIsShort = TRUE; + break; + + /* long or long long */ + case MOD_LOWERL: + if(*alias == MOD_LOWERL) { + info->fIsLongLong = TRUE; + /* skip over the next 'l' */ + alias++; + } + else + info->fIsLong = TRUE; + break; + + /* long double */ + case MOD_L: + info->fIsLongDouble = TRUE; + break; + } + } + + /* finally, get the specifier letter */ + info->fSpec = *alias++; + info->fOrigSpec = info->fSpec; + + /* fill in the precision and width, if specified out of line */ + + /* width specified out of line */ + if(spec.fInfo.fWidth == -2) { + if(spec.fWidthPos == -1) { + /* read the width from the argument list */ + info->fWidth = va_arg(ap, int32_t); + } + /* else handle positional parameter */ + + /* if it's negative, take the absolute value and set left alignment */ + if(info->fWidth < 0) { + info->fWidth *= -1; /* Make positive */ + info->fLeft = TRUE; + } + } + + /* precision specified out of line */ + if(info->fPrecision == -2) { + if(spec.fPrecisionPos == -1) { + /* read the precision from the argument list */ + info->fPrecision = va_arg(ap, int32_t); + } + /* else handle positional parameter */ + + /* if it's negative, set it to zero */ + if(info->fPrecision < 0) + info->fPrecision = 0; + } + + handlerNum = (uint16_t)(info->fSpec - UPRINTF_BASE_FMT_HANDLERS); + if (handlerNum < UPRINTF_NUM_FMT_HANDLERS) { + /* query the info function for argument information */ + argType = g_u_printf_infos[ handlerNum ].info; + switch(argType) { + case ufmt_count: + /* set the spec's width to the # of chars written */ + info->fWidth = *written; + /* fall through to set the pointer */ + case ufmt_string: + case ufmt_ustring: + case ufmt_pointer: + args.ptrValue = va_arg(ap, void*); + break; + case ufmt_char: + case ufmt_uchar: + case ufmt_int: + if (info->fIsLongLong) { + args.int64Value = va_arg(ap, int64_t); + } + else { + args.int64Value = va_arg(ap, int32_t); + } + break; + case ufmt_float: + args.floatValue = (float) va_arg(ap, double); + break; + case ufmt_double: + args.doubleValue = va_arg(ap, double); + break; + default: + /* else args is ignored */ + args.ptrValue = NULL; + break; + } + + /* call the handler function */ + handler = g_u_printf_infos[ handlerNum ].handler; + if(handler != 0) { + *written += (*handler)(streamHandler, context, formatBundle, info, &args); + } + else { + /* just echo unknown tags */ + *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias)); + } + } + else { + /* just echo unknown tags */ + *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias)); + } + } + /* return # of characters in this format that have been parsed. */ + return (int32_t)(alias - fmt); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ |