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