Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* |
michael@0 | 2 | ****************************************************************************** |
michael@0 | 3 | * |
michael@0 | 4 | * Copyright (C) 1998-2011, International Business Machines |
michael@0 | 5 | * Corporation and others. All Rights Reserved. |
michael@0 | 6 | * |
michael@0 | 7 | ****************************************************************************** |
michael@0 | 8 | * |
michael@0 | 9 | * File uprntf_p.c |
michael@0 | 10 | * |
michael@0 | 11 | * Modification History: |
michael@0 | 12 | * |
michael@0 | 13 | * Date Name Description |
michael@0 | 14 | * 11/23/98 stephen Creation. |
michael@0 | 15 | * 03/12/99 stephen Modified for new C API. |
michael@0 | 16 | * 08/07/2003 george Reunify printf implementations |
michael@0 | 17 | ****************************************************************************** |
michael@0 | 18 | */ |
michael@0 | 19 | |
michael@0 | 20 | #include "unicode/utypes.h" |
michael@0 | 21 | |
michael@0 | 22 | #if !UCONFIG_NO_FORMATTING |
michael@0 | 23 | |
michael@0 | 24 | #include "unicode/ustring.h" |
michael@0 | 25 | #include "unicode/utf16.h" |
michael@0 | 26 | #include "uprintf.h" |
michael@0 | 27 | #include "ufmt_cmn.h" |
michael@0 | 28 | #include "cmemory.h" |
michael@0 | 29 | #include "putilimp.h" |
michael@0 | 30 | |
michael@0 | 31 | /* ANSI style formatting */ |
michael@0 | 32 | /* Use US-ASCII characters only for formatting */ |
michael@0 | 33 | |
michael@0 | 34 | /* % */ |
michael@0 | 35 | #define UFMT_SIMPLE_PERCENT {ufmt_simple_percent, u_printf_simple_percent_handler} |
michael@0 | 36 | /* s */ |
michael@0 | 37 | #define UFMT_STRING {ufmt_string, u_printf_string_handler} |
michael@0 | 38 | /* c */ |
michael@0 | 39 | #define UFMT_CHAR {ufmt_char, u_printf_char_handler} |
michael@0 | 40 | /* d, i */ |
michael@0 | 41 | #define UFMT_INT {ufmt_int, u_printf_integer_handler} |
michael@0 | 42 | /* u */ |
michael@0 | 43 | #define UFMT_UINT {ufmt_int, u_printf_uinteger_handler} |
michael@0 | 44 | /* o */ |
michael@0 | 45 | #define UFMT_OCTAL {ufmt_int, u_printf_octal_handler} |
michael@0 | 46 | /* x, X */ |
michael@0 | 47 | #define UFMT_HEX {ufmt_int, u_printf_hex_handler} |
michael@0 | 48 | /* f */ |
michael@0 | 49 | #define UFMT_DOUBLE {ufmt_double, u_printf_double_handler} |
michael@0 | 50 | /* e, E */ |
michael@0 | 51 | #define UFMT_SCIENTIFIC {ufmt_double, u_printf_scientific_handler} |
michael@0 | 52 | /* g, G */ |
michael@0 | 53 | #define UFMT_SCIDBL {ufmt_double, u_printf_scidbl_handler} |
michael@0 | 54 | /* n */ |
michael@0 | 55 | #define UFMT_COUNT {ufmt_count, u_printf_count_handler} |
michael@0 | 56 | |
michael@0 | 57 | /* non-ANSI extensions */ |
michael@0 | 58 | /* Use US-ASCII characters only for formatting */ |
michael@0 | 59 | |
michael@0 | 60 | /* p */ |
michael@0 | 61 | #define UFMT_POINTER {ufmt_pointer, u_printf_pointer_handler} |
michael@0 | 62 | /* V */ |
michael@0 | 63 | #define UFMT_SPELLOUT {ufmt_double, u_printf_spellout_handler} |
michael@0 | 64 | /* P */ |
michael@0 | 65 | #define UFMT_PERCENT {ufmt_double, u_printf_percent_handler} |
michael@0 | 66 | /* C K is old format */ |
michael@0 | 67 | #define UFMT_UCHAR {ufmt_uchar, u_printf_uchar_handler} |
michael@0 | 68 | /* S U is old format */ |
michael@0 | 69 | #define UFMT_USTRING {ufmt_ustring, u_printf_ustring_handler} |
michael@0 | 70 | |
michael@0 | 71 | |
michael@0 | 72 | #define UFMT_EMPTY {ufmt_empty, NULL} |
michael@0 | 73 | |
michael@0 | 74 | /** |
michael@0 | 75 | * A u_printf handler function. |
michael@0 | 76 | * A u_printf handler is responsible for handling a single u_printf |
michael@0 | 77 | * format specification, for example 'd' or 's'. |
michael@0 | 78 | * @param stream The UFILE to which to write output. |
michael@0 | 79 | * @param info A pointer to a <TT>u_printf_spec_info</TT> struct containing |
michael@0 | 80 | * information on the format specification. |
michael@0 | 81 | * @param args A pointer to the argument data |
michael@0 | 82 | * @return The number of Unicode characters written to <TT>stream</TT>. |
michael@0 | 83 | */ |
michael@0 | 84 | typedef int32_t U_EXPORT2 |
michael@0 | 85 | u_printf_handler(const u_printf_stream_handler *handler, |
michael@0 | 86 | |
michael@0 | 87 | void *context, |
michael@0 | 88 | ULocaleBundle *formatBundle, |
michael@0 | 89 | const u_printf_spec_info *info, |
michael@0 | 90 | const ufmt_args *args); |
michael@0 | 91 | |
michael@0 | 92 | typedef struct u_printf_info { |
michael@0 | 93 | ufmt_type_info info; |
michael@0 | 94 | u_printf_handler *handler; |
michael@0 | 95 | } u_printf_info; |
michael@0 | 96 | |
michael@0 | 97 | /** |
michael@0 | 98 | * Struct encapsulating a single uprintf format specification. |
michael@0 | 99 | */ |
michael@0 | 100 | typedef struct u_printf_spec { |
michael@0 | 101 | u_printf_spec_info fInfo; /* Information on this spec */ |
michael@0 | 102 | int32_t fWidthPos; /* Position of width in arg list */ |
michael@0 | 103 | int32_t fPrecisionPos; /* Position of precision in arg list */ |
michael@0 | 104 | int32_t fArgPos; /* Position of data in arg list */ |
michael@0 | 105 | } u_printf_spec; |
michael@0 | 106 | |
michael@0 | 107 | #define UPRINTF_NUM_FMT_HANDLERS 108 |
michael@0 | 108 | |
michael@0 | 109 | /* We do not use handlers for 0-0x1f */ |
michael@0 | 110 | #define UPRINTF_BASE_FMT_HANDLERS 0x20 |
michael@0 | 111 | |
michael@0 | 112 | /* buffer size for formatting */ |
michael@0 | 113 | #define UPRINTF_BUFFER_SIZE 1024 |
michael@0 | 114 | #define UPRINTF_SYMBOL_BUFFER_SIZE 8 |
michael@0 | 115 | |
michael@0 | 116 | static const UChar gNullStr[] = {0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0}; /* "(null)" */ |
michael@0 | 117 | static const UChar gSpaceStr[] = {0x20, 0}; /* " " */ |
michael@0 | 118 | |
michael@0 | 119 | /* Sets the sign of a format based on u_printf_spec_info */ |
michael@0 | 120 | /* TODO: Is setting the prefix symbol to a positive sign a good idea in all locales? */ |
michael@0 | 121 | static void |
michael@0 | 122 | u_printf_set_sign(UNumberFormat *format, |
michael@0 | 123 | const u_printf_spec_info *info, |
michael@0 | 124 | UChar *prefixBuffer, |
michael@0 | 125 | int32_t *prefixBufLen, |
michael@0 | 126 | UErrorCode *status) |
michael@0 | 127 | { |
michael@0 | 128 | if(info->fShowSign) { |
michael@0 | 129 | *prefixBufLen = unum_getTextAttribute(format, |
michael@0 | 130 | UNUM_POSITIVE_PREFIX, |
michael@0 | 131 | prefixBuffer, |
michael@0 | 132 | *prefixBufLen, |
michael@0 | 133 | status); |
michael@0 | 134 | if (info->fSpace) { |
michael@0 | 135 | /* Setting UNUM_PLUS_SIGN_SYMBOL affects the exponent too. */ |
michael@0 | 136 | /* unum_setSymbol(format, UNUM_PLUS_SIGN_SYMBOL, gSpaceStr, 1, &status); */ |
michael@0 | 137 | unum_setTextAttribute(format, UNUM_POSITIVE_PREFIX, gSpaceStr, 1, status); |
michael@0 | 138 | } |
michael@0 | 139 | else { |
michael@0 | 140 | UChar plusSymbol[UPRINTF_SYMBOL_BUFFER_SIZE]; |
michael@0 | 141 | int32_t symbolLen; |
michael@0 | 142 | |
michael@0 | 143 | symbolLen = unum_getSymbol(format, |
michael@0 | 144 | UNUM_PLUS_SIGN_SYMBOL, |
michael@0 | 145 | plusSymbol, |
michael@0 | 146 | sizeof(plusSymbol)/sizeof(*plusSymbol), |
michael@0 | 147 | status); |
michael@0 | 148 | unum_setTextAttribute(format, |
michael@0 | 149 | UNUM_POSITIVE_PREFIX, |
michael@0 | 150 | plusSymbol, |
michael@0 | 151 | symbolLen, |
michael@0 | 152 | status); |
michael@0 | 153 | } |
michael@0 | 154 | } |
michael@0 | 155 | else { |
michael@0 | 156 | *prefixBufLen = 0; |
michael@0 | 157 | } |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | static void |
michael@0 | 161 | u_printf_reset_sign(UNumberFormat *format, |
michael@0 | 162 | const u_printf_spec_info *info, |
michael@0 | 163 | UChar *prefixBuffer, |
michael@0 | 164 | int32_t *prefixBufLen, |
michael@0 | 165 | UErrorCode *status) |
michael@0 | 166 | { |
michael@0 | 167 | if(info->fShowSign) { |
michael@0 | 168 | unum_setTextAttribute(format, |
michael@0 | 169 | UNUM_POSITIVE_PREFIX, |
michael@0 | 170 | prefixBuffer, |
michael@0 | 171 | *prefixBufLen, |
michael@0 | 172 | status); |
michael@0 | 173 | } |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | |
michael@0 | 177 | /* handle a '%' */ |
michael@0 | 178 | static int32_t |
michael@0 | 179 | u_printf_simple_percent_handler(const u_printf_stream_handler *handler, |
michael@0 | 180 | void *context, |
michael@0 | 181 | ULocaleBundle *formatBundle, |
michael@0 | 182 | const u_printf_spec_info *info, |
michael@0 | 183 | const ufmt_args *args) |
michael@0 | 184 | { |
michael@0 | 185 | static const UChar PERCENT[] = { UP_PERCENT }; |
michael@0 | 186 | |
michael@0 | 187 | /* put a single '%' onto the output */ |
michael@0 | 188 | return handler->write(context, PERCENT, 1); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | /* handle 's' */ |
michael@0 | 192 | static int32_t |
michael@0 | 193 | u_printf_string_handler(const u_printf_stream_handler *handler, |
michael@0 | 194 | void *context, |
michael@0 | 195 | ULocaleBundle *formatBundle, |
michael@0 | 196 | const u_printf_spec_info *info, |
michael@0 | 197 | const ufmt_args *args) |
michael@0 | 198 | { |
michael@0 | 199 | UChar *s; |
michael@0 | 200 | UChar buffer[UFMT_DEFAULT_BUFFER_SIZE]; |
michael@0 | 201 | int32_t len, written; |
michael@0 | 202 | int32_t argSize; |
michael@0 | 203 | const char *arg = (const char*)(args[0].ptrValue); |
michael@0 | 204 | |
michael@0 | 205 | /* convert from the default codepage to Unicode */ |
michael@0 | 206 | if (arg) { |
michael@0 | 207 | argSize = (int32_t)strlen(arg) + 1; |
michael@0 | 208 | if (argSize >= MAX_UCHAR_BUFFER_SIZE(buffer)) { |
michael@0 | 209 | s = ufmt_defaultCPToUnicode(arg, argSize, |
michael@0 | 210 | (UChar *)uprv_malloc(MAX_UCHAR_BUFFER_NEEDED(argSize)), |
michael@0 | 211 | MAX_UCHAR_BUFFER_NEEDED(argSize)); |
michael@0 | 212 | if(s == NULL) { |
michael@0 | 213 | return 0; |
michael@0 | 214 | } |
michael@0 | 215 | } |
michael@0 | 216 | else { |
michael@0 | 217 | s = ufmt_defaultCPToUnicode(arg, argSize, buffer, |
michael@0 | 218 | sizeof(buffer)/sizeof(UChar)); |
michael@0 | 219 | } |
michael@0 | 220 | } |
michael@0 | 221 | else { |
michael@0 | 222 | s = (UChar *)gNullStr; |
michael@0 | 223 | } |
michael@0 | 224 | len = u_strlen(s); |
michael@0 | 225 | |
michael@0 | 226 | /* width = minimum # of characters to write */ |
michael@0 | 227 | /* precision = maximum # of characters to write */ |
michael@0 | 228 | if (info->fPrecision != -1 && info->fPrecision < len) { |
michael@0 | 229 | len = info->fPrecision; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | written = handler->pad_and_justify(context, info, s, len); |
michael@0 | 233 | |
michael@0 | 234 | /* clean up */ |
michael@0 | 235 | if (gNullStr != s && buffer != s) { |
michael@0 | 236 | uprv_free(s); |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | return written; |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | static int32_t |
michael@0 | 243 | u_printf_char_handler(const u_printf_stream_handler *handler, |
michael@0 | 244 | void *context, |
michael@0 | 245 | ULocaleBundle *formatBundle, |
michael@0 | 246 | const u_printf_spec_info *info, |
michael@0 | 247 | const ufmt_args *args) |
michael@0 | 248 | { |
michael@0 | 249 | UChar s[U16_MAX_LENGTH+1]; |
michael@0 | 250 | int32_t len = 1, written; |
michael@0 | 251 | unsigned char arg = (unsigned char)(args[0].int64Value); |
michael@0 | 252 | |
michael@0 | 253 | /* convert from default codepage to Unicode */ |
michael@0 | 254 | ufmt_defaultCPToUnicode((const char *)&arg, 2, s, sizeof(s)/sizeof(UChar)); |
michael@0 | 255 | |
michael@0 | 256 | /* Remember that this may be an MBCS character */ |
michael@0 | 257 | if (arg != 0) { |
michael@0 | 258 | len = u_strlen(s); |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | /* width = minimum # of characters to write */ |
michael@0 | 262 | /* precision = maximum # of characters to write */ |
michael@0 | 263 | /* precision is ignored when handling a char */ |
michael@0 | 264 | |
michael@0 | 265 | written = handler->pad_and_justify(context, info, s, len); |
michael@0 | 266 | |
michael@0 | 267 | return written; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | static int32_t |
michael@0 | 271 | u_printf_double_handler(const u_printf_stream_handler *handler, |
michael@0 | 272 | void *context, |
michael@0 | 273 | ULocaleBundle *formatBundle, |
michael@0 | 274 | const u_printf_spec_info *info, |
michael@0 | 275 | const ufmt_args *args) |
michael@0 | 276 | { |
michael@0 | 277 | double num = (double) (args[0].doubleValue); |
michael@0 | 278 | UNumberFormat *format; |
michael@0 | 279 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 280 | UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; |
michael@0 | 281 | int32_t prefixBufferLen = sizeof(prefixBuffer); |
michael@0 | 282 | int32_t minDecimalDigits; |
michael@0 | 283 | int32_t maxDecimalDigits; |
michael@0 | 284 | int32_t resultLen; |
michael@0 | 285 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 286 | |
michael@0 | 287 | prefixBuffer[0] = 0; |
michael@0 | 288 | |
michael@0 | 289 | /* mask off any necessary bits */ |
michael@0 | 290 | /* if(! info->fIsLongDouble) |
michael@0 | 291 | num &= DBL_MAX;*/ |
michael@0 | 292 | |
michael@0 | 293 | /* get the formatter */ |
michael@0 | 294 | format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); |
michael@0 | 295 | |
michael@0 | 296 | /* handle error */ |
michael@0 | 297 | if(format == 0) |
michael@0 | 298 | return 0; |
michael@0 | 299 | |
michael@0 | 300 | /* save the formatter's state */ |
michael@0 | 301 | minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); |
michael@0 | 302 | maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); |
michael@0 | 303 | |
michael@0 | 304 | /* set the appropriate flags and number of decimal digits on the formatter */ |
michael@0 | 305 | if(info->fPrecision != -1) { |
michael@0 | 306 | /* set the # of decimal digits */ |
michael@0 | 307 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); |
michael@0 | 308 | } |
michael@0 | 309 | else if(info->fAlt) { |
michael@0 | 310 | /* '#' means always show decimal point */ |
michael@0 | 311 | /* copy of printf behavior on Solaris - '#' shows 6 digits */ |
michael@0 | 312 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 313 | } |
michael@0 | 314 | else { |
michael@0 | 315 | /* # of decimal digits is 6 if precision not specified regardless of locale */ |
michael@0 | 316 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | /* set whether to show the sign */ |
michael@0 | 320 | if (info->fShowSign) { |
michael@0 | 321 | u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | /* format the number */ |
michael@0 | 325 | resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 326 | |
michael@0 | 327 | if (U_FAILURE(status)) { |
michael@0 | 328 | resultLen = 0; |
michael@0 | 329 | } |
michael@0 | 330 | |
michael@0 | 331 | /* restore the number format */ |
michael@0 | 332 | /* TODO: Is this needed? */ |
michael@0 | 333 | unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); |
michael@0 | 334 | unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); |
michael@0 | 335 | |
michael@0 | 336 | if (info->fShowSign) { |
michael@0 | 337 | /* Reset back to original value regardless of what the error was */ |
michael@0 | 338 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 339 | u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | /* HSYS */ |
michael@0 | 346 | static int32_t |
michael@0 | 347 | u_printf_integer_handler(const u_printf_stream_handler *handler, |
michael@0 | 348 | void *context, |
michael@0 | 349 | ULocaleBundle *formatBundle, |
michael@0 | 350 | const u_printf_spec_info *info, |
michael@0 | 351 | const ufmt_args *args) |
michael@0 | 352 | { |
michael@0 | 353 | int64_t num = args[0].int64Value; |
michael@0 | 354 | UNumberFormat *format; |
michael@0 | 355 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 356 | UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; |
michael@0 | 357 | int32_t prefixBufferLen = sizeof(prefixBuffer); |
michael@0 | 358 | int32_t minDigits = -1; |
michael@0 | 359 | int32_t resultLen; |
michael@0 | 360 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 361 | |
michael@0 | 362 | prefixBuffer[0] = 0; |
michael@0 | 363 | |
michael@0 | 364 | /* mask off any necessary bits */ |
michael@0 | 365 | if (info->fIsShort) |
michael@0 | 366 | num = (int16_t)num; |
michael@0 | 367 | else if (!info->fIsLongLong) |
michael@0 | 368 | num = (int32_t)num; |
michael@0 | 369 | |
michael@0 | 370 | /* get the formatter */ |
michael@0 | 371 | format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); |
michael@0 | 372 | |
michael@0 | 373 | /* handle error */ |
michael@0 | 374 | if(format == 0) |
michael@0 | 375 | return 0; |
michael@0 | 376 | |
michael@0 | 377 | /* set the appropriate flags on the formatter */ |
michael@0 | 378 | |
michael@0 | 379 | /* set the minimum integer digits */ |
michael@0 | 380 | if(info->fPrecision != -1) { |
michael@0 | 381 | /* set the minimum # of digits */ |
michael@0 | 382 | minDigits = unum_getAttribute(format, UNUM_MIN_INTEGER_DIGITS); |
michael@0 | 383 | unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, info->fPrecision); |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | /* set whether to show the sign */ |
michael@0 | 387 | if(info->fShowSign) { |
michael@0 | 388 | u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | /* format the number */ |
michael@0 | 392 | resultLen = unum_formatInt64(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 393 | |
michael@0 | 394 | if (U_FAILURE(status)) { |
michael@0 | 395 | resultLen = 0; |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | /* restore the number format */ |
michael@0 | 399 | if (minDigits != -1) { |
michael@0 | 400 | unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, minDigits); |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | if (info->fShowSign) { |
michael@0 | 404 | /* Reset back to original value regardless of what the error was */ |
michael@0 | 405 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 406 | u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | static int32_t |
michael@0 | 413 | u_printf_hex_handler(const u_printf_stream_handler *handler, |
michael@0 | 414 | void *context, |
michael@0 | 415 | ULocaleBundle *formatBundle, |
michael@0 | 416 | const u_printf_spec_info *info, |
michael@0 | 417 | const ufmt_args *args) |
michael@0 | 418 | { |
michael@0 | 419 | int64_t num = args[0].int64Value; |
michael@0 | 420 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 421 | int32_t len = UPRINTF_BUFFER_SIZE; |
michael@0 | 422 | |
michael@0 | 423 | |
michael@0 | 424 | /* mask off any necessary bits */ |
michael@0 | 425 | if (info->fIsShort) |
michael@0 | 426 | num &= UINT16_MAX; |
michael@0 | 427 | else if (!info->fIsLongLong) |
michael@0 | 428 | num &= UINT32_MAX; |
michael@0 | 429 | |
michael@0 | 430 | /* format the number, preserving the minimum # of digits */ |
michael@0 | 431 | ufmt_64tou(result, &len, num, 16, |
michael@0 | 432 | (UBool)(info->fSpec == 0x0078), |
michael@0 | 433 | (info->fPrecision == -1 && info->fZero) ? info->fWidth : info->fPrecision); |
michael@0 | 434 | |
michael@0 | 435 | /* convert to alt form, if desired */ |
michael@0 | 436 | if(num != 0 && info->fAlt && len < UPRINTF_BUFFER_SIZE - 2) { |
michael@0 | 437 | /* shift the formatted string right by 2 chars */ |
michael@0 | 438 | memmove(result + 2, result, len * sizeof(UChar)); |
michael@0 | 439 | result[0] = 0x0030; |
michael@0 | 440 | result[1] = info->fSpec; |
michael@0 | 441 | len += 2; |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | return handler->pad_and_justify(context, info, result, len); |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | static int32_t |
michael@0 | 448 | u_printf_octal_handler(const u_printf_stream_handler *handler, |
michael@0 | 449 | void *context, |
michael@0 | 450 | ULocaleBundle *formatBundle, |
michael@0 | 451 | const u_printf_spec_info *info, |
michael@0 | 452 | const ufmt_args *args) |
michael@0 | 453 | { |
michael@0 | 454 | int64_t num = args[0].int64Value; |
michael@0 | 455 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 456 | int32_t len = UPRINTF_BUFFER_SIZE; |
michael@0 | 457 | |
michael@0 | 458 | |
michael@0 | 459 | /* mask off any necessary bits */ |
michael@0 | 460 | if (info->fIsShort) |
michael@0 | 461 | num &= UINT16_MAX; |
michael@0 | 462 | else if (!info->fIsLongLong) |
michael@0 | 463 | num &= UINT32_MAX; |
michael@0 | 464 | |
michael@0 | 465 | /* format the number, preserving the minimum # of digits */ |
michael@0 | 466 | ufmt_64tou(result, &len, num, 8, |
michael@0 | 467 | FALSE, /* doesn't matter for octal */ |
michael@0 | 468 | info->fPrecision == -1 && info->fZero ? info->fWidth : info->fPrecision); |
michael@0 | 469 | |
michael@0 | 470 | /* convert to alt form, if desired */ |
michael@0 | 471 | if(info->fAlt && result[0] != 0x0030 && len < UPRINTF_BUFFER_SIZE - 1) { |
michael@0 | 472 | /* shift the formatted string right by 1 char */ |
michael@0 | 473 | memmove(result + 1, result, len * sizeof(UChar)); |
michael@0 | 474 | result[0] = 0x0030; |
michael@0 | 475 | len += 1; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | return handler->pad_and_justify(context, info, result, len); |
michael@0 | 479 | } |
michael@0 | 480 | |
michael@0 | 481 | static int32_t |
michael@0 | 482 | u_printf_uinteger_handler(const u_printf_stream_handler *handler, |
michael@0 | 483 | void *context, |
michael@0 | 484 | ULocaleBundle *formatBundle, |
michael@0 | 485 | const u_printf_spec_info *info, |
michael@0 | 486 | const ufmt_args *args) |
michael@0 | 487 | { |
michael@0 | 488 | int64_t num = args[0].int64Value; |
michael@0 | 489 | UNumberFormat *format; |
michael@0 | 490 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 491 | int32_t minDigits = -1; |
michael@0 | 492 | int32_t resultLen; |
michael@0 | 493 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 494 | |
michael@0 | 495 | /* TODO: Fix this once uint64_t can be formatted. */ |
michael@0 | 496 | if (info->fIsShort) |
michael@0 | 497 | num &= UINT16_MAX; |
michael@0 | 498 | else if (!info->fIsLongLong) |
michael@0 | 499 | num &= UINT32_MAX; |
michael@0 | 500 | |
michael@0 | 501 | /* get the formatter */ |
michael@0 | 502 | format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); |
michael@0 | 503 | |
michael@0 | 504 | /* handle error */ |
michael@0 | 505 | if(format == 0) |
michael@0 | 506 | return 0; |
michael@0 | 507 | |
michael@0 | 508 | /* set the appropriate flags on the formatter */ |
michael@0 | 509 | |
michael@0 | 510 | /* set the minimum integer digits */ |
michael@0 | 511 | if(info->fPrecision != -1) { |
michael@0 | 512 | /* set the minimum # of digits */ |
michael@0 | 513 | minDigits = unum_getAttribute(format, UNUM_MIN_INTEGER_DIGITS); |
michael@0 | 514 | unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, info->fPrecision); |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | /* To mirror other stdio implementations, we ignore the sign argument */ |
michael@0 | 518 | |
michael@0 | 519 | /* format the number */ |
michael@0 | 520 | resultLen = unum_formatInt64(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 521 | |
michael@0 | 522 | if (U_FAILURE(status)) { |
michael@0 | 523 | resultLen = 0; |
michael@0 | 524 | } |
michael@0 | 525 | |
michael@0 | 526 | /* restore the number format */ |
michael@0 | 527 | if (minDigits != -1) { |
michael@0 | 528 | unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, minDigits); |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | static int32_t |
michael@0 | 535 | u_printf_pointer_handler(const u_printf_stream_handler *handler, |
michael@0 | 536 | void *context, |
michael@0 | 537 | ULocaleBundle *formatBundle, |
michael@0 | 538 | const u_printf_spec_info *info, |
michael@0 | 539 | const ufmt_args *args) |
michael@0 | 540 | { |
michael@0 | 541 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 542 | int32_t len = UPRINTF_BUFFER_SIZE; |
michael@0 | 543 | |
michael@0 | 544 | /* format the pointer in hex */ |
michael@0 | 545 | ufmt_ptou(result, &len, args[0].ptrValue, TRUE/*, info->fPrecision*/); |
michael@0 | 546 | |
michael@0 | 547 | return handler->pad_and_justify(context, info, result, len); |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | static int32_t |
michael@0 | 551 | u_printf_scientific_handler(const u_printf_stream_handler *handler, |
michael@0 | 552 | void *context, |
michael@0 | 553 | ULocaleBundle *formatBundle, |
michael@0 | 554 | const u_printf_spec_info *info, |
michael@0 | 555 | const ufmt_args *args) |
michael@0 | 556 | { |
michael@0 | 557 | double num = (double) (args[0].doubleValue); |
michael@0 | 558 | UNumberFormat *format; |
michael@0 | 559 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 560 | UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; |
michael@0 | 561 | int32_t prefixBufferLen = sizeof(prefixBuffer); |
michael@0 | 562 | int32_t minDecimalDigits; |
michael@0 | 563 | int32_t maxDecimalDigits; |
michael@0 | 564 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 565 | UChar srcExpBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; |
michael@0 | 566 | int32_t srcLen, expLen; |
michael@0 | 567 | int32_t resultLen; |
michael@0 | 568 | UChar expBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; |
michael@0 | 569 | |
michael@0 | 570 | prefixBuffer[0] = 0; |
michael@0 | 571 | |
michael@0 | 572 | /* mask off any necessary bits */ |
michael@0 | 573 | /* if(! info->fIsLongDouble) |
michael@0 | 574 | num &= DBL_MAX;*/ |
michael@0 | 575 | |
michael@0 | 576 | /* get the formatter */ |
michael@0 | 577 | format = u_locbund_getNumberFormat(formatBundle, UNUM_SCIENTIFIC); |
michael@0 | 578 | |
michael@0 | 579 | /* handle error */ |
michael@0 | 580 | if(format == 0) |
michael@0 | 581 | return 0; |
michael@0 | 582 | |
michael@0 | 583 | /* set the appropriate flags on the formatter */ |
michael@0 | 584 | |
michael@0 | 585 | srcLen = unum_getSymbol(format, |
michael@0 | 586 | UNUM_EXPONENTIAL_SYMBOL, |
michael@0 | 587 | srcExpBuf, |
michael@0 | 588 | sizeof(srcExpBuf), |
michael@0 | 589 | &status); |
michael@0 | 590 | |
michael@0 | 591 | /* Upper/lower case the e */ |
michael@0 | 592 | if (info->fSpec == (UChar)0x65 /* e */) { |
michael@0 | 593 | expLen = u_strToLower(expBuf, (int32_t)sizeof(expBuf), |
michael@0 | 594 | srcExpBuf, srcLen, |
michael@0 | 595 | formatBundle->fLocale, |
michael@0 | 596 | &status); |
michael@0 | 597 | } |
michael@0 | 598 | else { |
michael@0 | 599 | expLen = u_strToUpper(expBuf, (int32_t)sizeof(expBuf), |
michael@0 | 600 | srcExpBuf, srcLen, |
michael@0 | 601 | formatBundle->fLocale, |
michael@0 | 602 | &status); |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | unum_setSymbol(format, |
michael@0 | 606 | UNUM_EXPONENTIAL_SYMBOL, |
michael@0 | 607 | expBuf, |
michael@0 | 608 | expLen, |
michael@0 | 609 | &status); |
michael@0 | 610 | |
michael@0 | 611 | /* save the formatter's state */ |
michael@0 | 612 | minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); |
michael@0 | 613 | maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); |
michael@0 | 614 | |
michael@0 | 615 | /* set the appropriate flags and number of decimal digits on the formatter */ |
michael@0 | 616 | if(info->fPrecision != -1) { |
michael@0 | 617 | /* set the # of decimal digits */ |
michael@0 | 618 | if (info->fOrigSpec == (UChar)0x65 /* e */ || info->fOrigSpec == (UChar)0x45 /* E */) { |
michael@0 | 619 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); |
michael@0 | 620 | } |
michael@0 | 621 | else { |
michael@0 | 622 | unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, 1); |
michael@0 | 623 | unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, info->fPrecision); |
michael@0 | 624 | } |
michael@0 | 625 | } |
michael@0 | 626 | else if(info->fAlt) { |
michael@0 | 627 | /* '#' means always show decimal point */ |
michael@0 | 628 | /* copy of printf behavior on Solaris - '#' shows 6 digits */ |
michael@0 | 629 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 630 | } |
michael@0 | 631 | else { |
michael@0 | 632 | /* # of decimal digits is 6 if precision not specified */ |
michael@0 | 633 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | /* set whether to show the sign */ |
michael@0 | 637 | if (info->fShowSign) { |
michael@0 | 638 | u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); |
michael@0 | 639 | } |
michael@0 | 640 | |
michael@0 | 641 | /* format the number */ |
michael@0 | 642 | resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 643 | |
michael@0 | 644 | if (U_FAILURE(status)) { |
michael@0 | 645 | resultLen = 0; |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | /* restore the number format */ |
michael@0 | 649 | /* TODO: Is this needed? */ |
michael@0 | 650 | unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); |
michael@0 | 651 | unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); |
michael@0 | 652 | |
michael@0 | 653 | /* Since we're the only one using the scientific |
michael@0 | 654 | format, we don't need to save the old exponent value. */ |
michael@0 | 655 | /*unum_setSymbol(format, |
michael@0 | 656 | UNUM_EXPONENTIAL_SYMBOL, |
michael@0 | 657 | srcExpBuf, |
michael@0 | 658 | srcLen, |
michael@0 | 659 | &status);*/ |
michael@0 | 660 | |
michael@0 | 661 | if (info->fShowSign) { |
michael@0 | 662 | /* Reset back to original value regardless of what the error was */ |
michael@0 | 663 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 664 | u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | static int32_t |
michael@0 | 671 | u_printf_percent_handler(const u_printf_stream_handler *handler, |
michael@0 | 672 | void *context, |
michael@0 | 673 | ULocaleBundle *formatBundle, |
michael@0 | 674 | const u_printf_spec_info *info, |
michael@0 | 675 | const ufmt_args *args) |
michael@0 | 676 | { |
michael@0 | 677 | double num = (double) (args[0].doubleValue); |
michael@0 | 678 | UNumberFormat *format; |
michael@0 | 679 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 680 | UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; |
michael@0 | 681 | int32_t prefixBufferLen = sizeof(prefixBuffer); |
michael@0 | 682 | int32_t minDecimalDigits; |
michael@0 | 683 | int32_t maxDecimalDigits; |
michael@0 | 684 | int32_t resultLen; |
michael@0 | 685 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 686 | |
michael@0 | 687 | prefixBuffer[0] = 0; |
michael@0 | 688 | |
michael@0 | 689 | /* mask off any necessary bits */ |
michael@0 | 690 | /* if(! info->fIsLongDouble) |
michael@0 | 691 | num &= DBL_MAX;*/ |
michael@0 | 692 | |
michael@0 | 693 | /* get the formatter */ |
michael@0 | 694 | format = u_locbund_getNumberFormat(formatBundle, UNUM_PERCENT); |
michael@0 | 695 | |
michael@0 | 696 | /* handle error */ |
michael@0 | 697 | if(format == 0) |
michael@0 | 698 | return 0; |
michael@0 | 699 | |
michael@0 | 700 | /* save the formatter's state */ |
michael@0 | 701 | minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); |
michael@0 | 702 | maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); |
michael@0 | 703 | |
michael@0 | 704 | /* set the appropriate flags and number of decimal digits on the formatter */ |
michael@0 | 705 | if(info->fPrecision != -1) { |
michael@0 | 706 | /* set the # of decimal digits */ |
michael@0 | 707 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); |
michael@0 | 708 | } |
michael@0 | 709 | else if(info->fAlt) { |
michael@0 | 710 | /* '#' means always show decimal point */ |
michael@0 | 711 | /* copy of printf behavior on Solaris - '#' shows 6 digits */ |
michael@0 | 712 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 713 | } |
michael@0 | 714 | else { |
michael@0 | 715 | /* # of decimal digits is 6 if precision not specified */ |
michael@0 | 716 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 717 | } |
michael@0 | 718 | |
michael@0 | 719 | /* set whether to show the sign */ |
michael@0 | 720 | if (info->fShowSign) { |
michael@0 | 721 | u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); |
michael@0 | 722 | } |
michael@0 | 723 | |
michael@0 | 724 | /* format the number */ |
michael@0 | 725 | resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 726 | |
michael@0 | 727 | if (U_FAILURE(status)) { |
michael@0 | 728 | resultLen = 0; |
michael@0 | 729 | } |
michael@0 | 730 | |
michael@0 | 731 | /* restore the number format */ |
michael@0 | 732 | /* TODO: Is this needed? */ |
michael@0 | 733 | unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); |
michael@0 | 734 | unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); |
michael@0 | 735 | |
michael@0 | 736 | if (info->fShowSign) { |
michael@0 | 737 | /* Reset back to original value regardless of what the error was */ |
michael@0 | 738 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 739 | u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | static int32_t |
michael@0 | 746 | u_printf_ustring_handler(const u_printf_stream_handler *handler, |
michael@0 | 747 | void *context, |
michael@0 | 748 | ULocaleBundle *formatBundle, |
michael@0 | 749 | const u_printf_spec_info *info, |
michael@0 | 750 | const ufmt_args *args) |
michael@0 | 751 | { |
michael@0 | 752 | int32_t len, written; |
michael@0 | 753 | const UChar *arg = (const UChar*)(args[0].ptrValue); |
michael@0 | 754 | |
michael@0 | 755 | /* allocate enough space for the buffer */ |
michael@0 | 756 | if (arg == NULL) { |
michael@0 | 757 | arg = gNullStr; |
michael@0 | 758 | } |
michael@0 | 759 | len = u_strlen(arg); |
michael@0 | 760 | |
michael@0 | 761 | /* width = minimum # of characters to write */ |
michael@0 | 762 | /* precision = maximum # of characters to write */ |
michael@0 | 763 | if (info->fPrecision != -1 && info->fPrecision < len) { |
michael@0 | 764 | len = info->fPrecision; |
michael@0 | 765 | } |
michael@0 | 766 | |
michael@0 | 767 | /* determine if the string should be padded */ |
michael@0 | 768 | written = handler->pad_and_justify(context, info, arg, len); |
michael@0 | 769 | |
michael@0 | 770 | return written; |
michael@0 | 771 | } |
michael@0 | 772 | |
michael@0 | 773 | static int32_t |
michael@0 | 774 | u_printf_uchar_handler(const u_printf_stream_handler *handler, |
michael@0 | 775 | void *context, |
michael@0 | 776 | ULocaleBundle *formatBundle, |
michael@0 | 777 | const u_printf_spec_info *info, |
michael@0 | 778 | const ufmt_args *args) |
michael@0 | 779 | { |
michael@0 | 780 | int32_t written = 0; |
michael@0 | 781 | UChar arg = (UChar)(args[0].int64Value); |
michael@0 | 782 | |
michael@0 | 783 | /* width = minimum # of characters to write */ |
michael@0 | 784 | /* precision = maximum # of characters to write */ |
michael@0 | 785 | /* precision is ignored when handling a uchar */ |
michael@0 | 786 | |
michael@0 | 787 | /* determine if the string should be padded */ |
michael@0 | 788 | written = handler->pad_and_justify(context, info, &arg, 1); |
michael@0 | 789 | |
michael@0 | 790 | return written; |
michael@0 | 791 | } |
michael@0 | 792 | |
michael@0 | 793 | static int32_t |
michael@0 | 794 | u_printf_scidbl_handler(const u_printf_stream_handler *handler, |
michael@0 | 795 | void *context, |
michael@0 | 796 | ULocaleBundle *formatBundle, |
michael@0 | 797 | const u_printf_spec_info *info, |
michael@0 | 798 | const ufmt_args *args) |
michael@0 | 799 | { |
michael@0 | 800 | u_printf_spec_info scidbl_info; |
michael@0 | 801 | double num = args[0].doubleValue; |
michael@0 | 802 | int32_t retVal; |
michael@0 | 803 | UNumberFormat *format; |
michael@0 | 804 | int32_t maxSigDecimalDigits, significantDigits; |
michael@0 | 805 | |
michael@0 | 806 | memcpy(&scidbl_info, info, sizeof(u_printf_spec_info)); |
michael@0 | 807 | |
michael@0 | 808 | /* determine whether to use 'd', 'e' or 'f' notation */ |
michael@0 | 809 | if (scidbl_info.fPrecision == -1 && num == uprv_trunc(num)) |
michael@0 | 810 | { |
michael@0 | 811 | /* use 'f' notation */ |
michael@0 | 812 | scidbl_info.fSpec = 0x0066; |
michael@0 | 813 | scidbl_info.fPrecision = 0; |
michael@0 | 814 | /* call the double handler */ |
michael@0 | 815 | retVal = u_printf_double_handler(handler, context, formatBundle, &scidbl_info, args); |
michael@0 | 816 | } |
michael@0 | 817 | else if(num < 0.0001 || (scidbl_info.fPrecision < 1 && 1000000.0 <= num) |
michael@0 | 818 | || (scidbl_info.fPrecision != -1 && num > uprv_pow10(scidbl_info.fPrecision))) |
michael@0 | 819 | { |
michael@0 | 820 | /* use 'e' or 'E' notation */ |
michael@0 | 821 | scidbl_info.fSpec = scidbl_info.fSpec - 2; |
michael@0 | 822 | if (scidbl_info.fPrecision == -1) { |
michael@0 | 823 | scidbl_info.fPrecision = 5; |
michael@0 | 824 | } |
michael@0 | 825 | /* call the scientific handler */ |
michael@0 | 826 | retVal = u_printf_scientific_handler(handler, context, formatBundle, &scidbl_info, args); |
michael@0 | 827 | } |
michael@0 | 828 | else { |
michael@0 | 829 | format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL); |
michael@0 | 830 | /* Check for null pointer */ |
michael@0 | 831 | if (format == NULL) { |
michael@0 | 832 | return 0; |
michael@0 | 833 | } |
michael@0 | 834 | maxSigDecimalDigits = unum_getAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS); |
michael@0 | 835 | significantDigits = scidbl_info.fPrecision; |
michael@0 | 836 | |
michael@0 | 837 | /* use 'f' notation */ |
michael@0 | 838 | scidbl_info.fSpec = 0x0066; |
michael@0 | 839 | if (significantDigits == -1) { |
michael@0 | 840 | significantDigits = 6; |
michael@0 | 841 | } |
michael@0 | 842 | unum_setAttribute(format, UNUM_SIGNIFICANT_DIGITS_USED, TRUE); |
michael@0 | 843 | unum_setAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS, significantDigits); |
michael@0 | 844 | /* call the double handler */ |
michael@0 | 845 | retVal = u_printf_double_handler(handler, context, formatBundle, &scidbl_info, args); |
michael@0 | 846 | unum_setAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS, maxSigDecimalDigits); |
michael@0 | 847 | unum_setAttribute(format, UNUM_SIGNIFICANT_DIGITS_USED, FALSE); |
michael@0 | 848 | } |
michael@0 | 849 | return retVal; |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | static int32_t |
michael@0 | 853 | u_printf_count_handler(const u_printf_stream_handler *handler, |
michael@0 | 854 | void *context, |
michael@0 | 855 | ULocaleBundle *formatBundle, |
michael@0 | 856 | const u_printf_spec_info *info, |
michael@0 | 857 | const ufmt_args *args) |
michael@0 | 858 | { |
michael@0 | 859 | int32_t *count = (int32_t*)(args[0].ptrValue); |
michael@0 | 860 | |
michael@0 | 861 | /* in the special case of count, the u_printf_spec_info's width */ |
michael@0 | 862 | /* will contain the # of chars written thus far */ |
michael@0 | 863 | *count = info->fWidth; |
michael@0 | 864 | |
michael@0 | 865 | return 0; |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | static int32_t |
michael@0 | 869 | u_printf_spellout_handler(const u_printf_stream_handler *handler, |
michael@0 | 870 | void *context, |
michael@0 | 871 | ULocaleBundle *formatBundle, |
michael@0 | 872 | const u_printf_spec_info *info, |
michael@0 | 873 | const ufmt_args *args) |
michael@0 | 874 | { |
michael@0 | 875 | double num = (double) (args[0].doubleValue); |
michael@0 | 876 | UNumberFormat *format; |
michael@0 | 877 | UChar result[UPRINTF_BUFFER_SIZE]; |
michael@0 | 878 | UChar prefixBuffer[UPRINTF_BUFFER_SIZE]; |
michael@0 | 879 | int32_t prefixBufferLen = sizeof(prefixBuffer); |
michael@0 | 880 | int32_t minDecimalDigits; |
michael@0 | 881 | int32_t maxDecimalDigits; |
michael@0 | 882 | int32_t resultLen; |
michael@0 | 883 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 884 | |
michael@0 | 885 | prefixBuffer[0] = 0; |
michael@0 | 886 | |
michael@0 | 887 | /* mask off any necessary bits */ |
michael@0 | 888 | /* if(! info->fIsLongDouble) |
michael@0 | 889 | num &= DBL_MAX;*/ |
michael@0 | 890 | |
michael@0 | 891 | /* get the formatter */ |
michael@0 | 892 | format = u_locbund_getNumberFormat(formatBundle, UNUM_SPELLOUT); |
michael@0 | 893 | |
michael@0 | 894 | /* handle error */ |
michael@0 | 895 | if(format == 0) |
michael@0 | 896 | return 0; |
michael@0 | 897 | |
michael@0 | 898 | /* save the formatter's state */ |
michael@0 | 899 | minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS); |
michael@0 | 900 | maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS); |
michael@0 | 901 | |
michael@0 | 902 | /* set the appropriate flags and number of decimal digits on the formatter */ |
michael@0 | 903 | if(info->fPrecision != -1) { |
michael@0 | 904 | /* set the # of decimal digits */ |
michael@0 | 905 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision); |
michael@0 | 906 | } |
michael@0 | 907 | else if(info->fAlt) { |
michael@0 | 908 | /* '#' means always show decimal point */ |
michael@0 | 909 | /* copy of printf behavior on Solaris - '#' shows 6 digits */ |
michael@0 | 910 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 911 | } |
michael@0 | 912 | else { |
michael@0 | 913 | /* # of decimal digits is 6 if precision not specified */ |
michael@0 | 914 | unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6); |
michael@0 | 915 | } |
michael@0 | 916 | |
michael@0 | 917 | /* set whether to show the sign */ |
michael@0 | 918 | if (info->fShowSign) { |
michael@0 | 919 | u_printf_set_sign(format, info, prefixBuffer, &prefixBufferLen, &status); |
michael@0 | 920 | } |
michael@0 | 921 | |
michael@0 | 922 | /* format the number */ |
michael@0 | 923 | resultLen = unum_formatDouble(format, num, result, UPRINTF_BUFFER_SIZE, 0, &status); |
michael@0 | 924 | |
michael@0 | 925 | if (U_FAILURE(status)) { |
michael@0 | 926 | resultLen = 0; |
michael@0 | 927 | } |
michael@0 | 928 | |
michael@0 | 929 | /* restore the number format */ |
michael@0 | 930 | /* TODO: Is this needed? */ |
michael@0 | 931 | unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits); |
michael@0 | 932 | unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits); |
michael@0 | 933 | |
michael@0 | 934 | if (info->fShowSign) { |
michael@0 | 935 | /* Reset back to original value regardless of what the error was */ |
michael@0 | 936 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 937 | u_printf_reset_sign(format, info, prefixBuffer, &prefixBufferLen, &localStatus); |
michael@0 | 938 | } |
michael@0 | 939 | |
michael@0 | 940 | return handler->pad_and_justify(context, info, result, resultLen); |
michael@0 | 941 | } |
michael@0 | 942 | |
michael@0 | 943 | /* Use US-ASCII characters only for formatting. Most codepages have |
michael@0 | 944 | characters 20-7F from Unicode. Using any other codepage specific |
michael@0 | 945 | characters will make it very difficult to format the string on |
michael@0 | 946 | non-Unicode machines */ |
michael@0 | 947 | static const u_printf_info g_u_printf_infos[UPRINTF_NUM_FMT_HANDLERS] = { |
michael@0 | 948 | /* 0x20 */ |
michael@0 | 949 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 950 | UFMT_EMPTY, UFMT_SIMPLE_PERCENT,UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 951 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 952 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 953 | |
michael@0 | 954 | /* 0x30 */ |
michael@0 | 955 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 956 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 957 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 958 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 959 | |
michael@0 | 960 | /* 0x40 */ |
michael@0 | 961 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR, |
michael@0 | 962 | UFMT_EMPTY, UFMT_SCIENTIFIC, UFMT_EMPTY, UFMT_SCIDBL, |
michael@0 | 963 | #ifdef U_USE_OBSOLETE_IO_FORMATTING |
michael@0 | 964 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR/*deprecated*/, |
michael@0 | 965 | #else |
michael@0 | 966 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 967 | #endif |
michael@0 | 968 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 969 | |
michael@0 | 970 | /* 0x50 */ |
michael@0 | 971 | UFMT_PERCENT, UFMT_EMPTY, UFMT_EMPTY, UFMT_USTRING, |
michael@0 | 972 | #ifdef U_USE_OBSOLETE_IO_FORMATTING |
michael@0 | 973 | UFMT_EMPTY, UFMT_USTRING/*deprecated*/,UFMT_SPELLOUT, UFMT_EMPTY, |
michael@0 | 974 | #else |
michael@0 | 975 | UFMT_EMPTY, UFMT_EMPTY, UFMT_SPELLOUT, UFMT_EMPTY, |
michael@0 | 976 | #endif |
michael@0 | 977 | UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 978 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 979 | |
michael@0 | 980 | /* 0x60 */ |
michael@0 | 981 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_CHAR, |
michael@0 | 982 | UFMT_INT, UFMT_SCIENTIFIC, UFMT_DOUBLE, UFMT_SCIDBL, |
michael@0 | 983 | UFMT_EMPTY, UFMT_INT, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 984 | UFMT_EMPTY, UFMT_EMPTY, UFMT_COUNT, UFMT_OCTAL, |
michael@0 | 985 | |
michael@0 | 986 | /* 0x70 */ |
michael@0 | 987 | UFMT_POINTER, UFMT_EMPTY, UFMT_EMPTY, UFMT_STRING, |
michael@0 | 988 | UFMT_EMPTY, UFMT_UINT, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 989 | UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 990 | UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, |
michael@0 | 991 | }; |
michael@0 | 992 | |
michael@0 | 993 | /* flag characters for uprintf */ |
michael@0 | 994 | #define FLAG_MINUS 0x002D |
michael@0 | 995 | #define FLAG_PLUS 0x002B |
michael@0 | 996 | #define FLAG_SPACE 0x0020 |
michael@0 | 997 | #define FLAG_POUND 0x0023 |
michael@0 | 998 | #define FLAG_ZERO 0x0030 |
michael@0 | 999 | #define FLAG_PAREN 0x0028 |
michael@0 | 1000 | |
michael@0 | 1001 | #define ISFLAG(s) (s) == FLAG_MINUS || \ |
michael@0 | 1002 | (s) == FLAG_PLUS || \ |
michael@0 | 1003 | (s) == FLAG_SPACE || \ |
michael@0 | 1004 | (s) == FLAG_POUND || \ |
michael@0 | 1005 | (s) == FLAG_ZERO || \ |
michael@0 | 1006 | (s) == FLAG_PAREN |
michael@0 | 1007 | |
michael@0 | 1008 | /* special characters for uprintf */ |
michael@0 | 1009 | #define SPEC_ASTERISK 0x002A |
michael@0 | 1010 | #define SPEC_DOLLARSIGN 0x0024 |
michael@0 | 1011 | #define SPEC_PERIOD 0x002E |
michael@0 | 1012 | #define SPEC_PERCENT 0x0025 |
michael@0 | 1013 | |
michael@0 | 1014 | /* unicode digits */ |
michael@0 | 1015 | #define DIGIT_ZERO 0x0030 |
michael@0 | 1016 | #define DIGIT_ONE 0x0031 |
michael@0 | 1017 | #define DIGIT_TWO 0x0032 |
michael@0 | 1018 | #define DIGIT_THREE 0x0033 |
michael@0 | 1019 | #define DIGIT_FOUR 0x0034 |
michael@0 | 1020 | #define DIGIT_FIVE 0x0035 |
michael@0 | 1021 | #define DIGIT_SIX 0x0036 |
michael@0 | 1022 | #define DIGIT_SEVEN 0x0037 |
michael@0 | 1023 | #define DIGIT_EIGHT 0x0038 |
michael@0 | 1024 | #define DIGIT_NINE 0x0039 |
michael@0 | 1025 | |
michael@0 | 1026 | #define ISDIGIT(s) (s) == DIGIT_ZERO || \ |
michael@0 | 1027 | (s) == DIGIT_ONE || \ |
michael@0 | 1028 | (s) == DIGIT_TWO || \ |
michael@0 | 1029 | (s) == DIGIT_THREE || \ |
michael@0 | 1030 | (s) == DIGIT_FOUR || \ |
michael@0 | 1031 | (s) == DIGIT_FIVE || \ |
michael@0 | 1032 | (s) == DIGIT_SIX || \ |
michael@0 | 1033 | (s) == DIGIT_SEVEN || \ |
michael@0 | 1034 | (s) == DIGIT_EIGHT || \ |
michael@0 | 1035 | (s) == DIGIT_NINE |
michael@0 | 1036 | |
michael@0 | 1037 | /* u_printf modifiers */ |
michael@0 | 1038 | #define MOD_H 0x0068 |
michael@0 | 1039 | #define MOD_LOWERL 0x006C |
michael@0 | 1040 | #define MOD_L 0x004C |
michael@0 | 1041 | |
michael@0 | 1042 | #define ISMOD(s) (s) == MOD_H || \ |
michael@0 | 1043 | (s) == MOD_LOWERL || \ |
michael@0 | 1044 | (s) == MOD_L |
michael@0 | 1045 | /* Returns an array of the parsed argument type given in the format string. */ |
michael@0 | 1046 | static ufmt_args* parseArguments(const UChar *alias, va_list ap, UErrorCode *status) { |
michael@0 | 1047 | ufmt_args *arglist = NULL; |
michael@0 | 1048 | ufmt_type_info *typelist = NULL; |
michael@0 | 1049 | UBool *islonglong = NULL; |
michael@0 | 1050 | int32_t size = 0; |
michael@0 | 1051 | int32_t pos = 0; |
michael@0 | 1052 | UChar type; |
michael@0 | 1053 | uint16_t handlerNum; |
michael@0 | 1054 | const UChar *aliasStart = alias; |
michael@0 | 1055 | |
michael@0 | 1056 | /* get maximum number of arguments */ |
michael@0 | 1057 | for(;;) { |
michael@0 | 1058 | /* find % */ |
michael@0 | 1059 | while(*alias != UP_PERCENT && *alias != 0x0000) { |
michael@0 | 1060 | alias++; |
michael@0 | 1061 | } |
michael@0 | 1062 | |
michael@0 | 1063 | if(*alias == 0x0000) { |
michael@0 | 1064 | break; |
michael@0 | 1065 | } |
michael@0 | 1066 | |
michael@0 | 1067 | alias++; |
michael@0 | 1068 | |
michael@0 | 1069 | /* handle the pos number */ |
michael@0 | 1070 | if(ISDIGIT(*alias)) { |
michael@0 | 1071 | |
michael@0 | 1072 | /* handle positional parameters */ |
michael@0 | 1073 | if(ISDIGIT(*alias)) { |
michael@0 | 1074 | pos = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1075 | |
michael@0 | 1076 | while(ISDIGIT(*alias)) { |
michael@0 | 1077 | pos *= 10; |
michael@0 | 1078 | pos += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1079 | } |
michael@0 | 1080 | } |
michael@0 | 1081 | |
michael@0 | 1082 | /* if there is no '$', don't read anything */ |
michael@0 | 1083 | if(*alias != SPEC_DOLLARSIGN) { |
michael@0 | 1084 | return NULL; |
michael@0 | 1085 | } |
michael@0 | 1086 | } else { |
michael@0 | 1087 | return NULL; |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | if (pos > size) { |
michael@0 | 1091 | size = pos; |
michael@0 | 1092 | } |
michael@0 | 1093 | } |
michael@0 | 1094 | |
michael@0 | 1095 | /* create the parsed argument list */ |
michael@0 | 1096 | typelist = (ufmt_type_info*)uprv_malloc(sizeof(ufmt_type_info) * size); |
michael@0 | 1097 | islonglong = (UBool*)uprv_malloc(sizeof(UBool) * size); |
michael@0 | 1098 | arglist = (ufmt_args*)uprv_malloc(sizeof(ufmt_args) * size); |
michael@0 | 1099 | |
michael@0 | 1100 | /* If malloc failed, return NULL */ |
michael@0 | 1101 | if (!typelist || !islonglong || !arglist) { |
michael@0 | 1102 | if (typelist) { |
michael@0 | 1103 | uprv_free(typelist); |
michael@0 | 1104 | } |
michael@0 | 1105 | |
michael@0 | 1106 | if (islonglong) { |
michael@0 | 1107 | uprv_free(islonglong); |
michael@0 | 1108 | } |
michael@0 | 1109 | |
michael@0 | 1110 | if (arglist) { |
michael@0 | 1111 | uprv_free(arglist); |
michael@0 | 1112 | } |
michael@0 | 1113 | |
michael@0 | 1114 | *status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1115 | return NULL; |
michael@0 | 1116 | } |
michael@0 | 1117 | |
michael@0 | 1118 | /* reset alias back to the beginning */ |
michael@0 | 1119 | alias = aliasStart; |
michael@0 | 1120 | |
michael@0 | 1121 | for(;;) { |
michael@0 | 1122 | /* find % */ |
michael@0 | 1123 | while(*alias != UP_PERCENT && *alias != 0x0000) { |
michael@0 | 1124 | alias++; |
michael@0 | 1125 | } |
michael@0 | 1126 | |
michael@0 | 1127 | if(*alias == 0x0000) { |
michael@0 | 1128 | break; |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | alias++; |
michael@0 | 1132 | |
michael@0 | 1133 | /* handle positional parameters */ |
michael@0 | 1134 | if(ISDIGIT(*alias)) { |
michael@0 | 1135 | pos = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1136 | |
michael@0 | 1137 | while(ISDIGIT(*alias)) { |
michael@0 | 1138 | pos *= 10; |
michael@0 | 1139 | pos += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1140 | } |
michael@0 | 1141 | } |
michael@0 | 1142 | /* offset position by 1 */ |
michael@0 | 1143 | pos--; |
michael@0 | 1144 | |
michael@0 | 1145 | /* skip over everything except for the type */ |
michael@0 | 1146 | while (ISMOD(*alias) || ISFLAG(*alias) || ISDIGIT(*alias) || |
michael@0 | 1147 | *alias == SPEC_ASTERISK || *alias == SPEC_PERIOD || *alias == SPEC_DOLLARSIGN) { |
michael@0 | 1148 | islonglong[pos] = FALSE; |
michael@0 | 1149 | if (ISMOD(*alias)) { |
michael@0 | 1150 | alias++; |
michael@0 | 1151 | if (*alias == MOD_LOWERL) { |
michael@0 | 1152 | islonglong[pos] = TRUE; |
michael@0 | 1153 | } |
michael@0 | 1154 | } |
michael@0 | 1155 | alias++; |
michael@0 | 1156 | } |
michael@0 | 1157 | type = *alias; |
michael@0 | 1158 | |
michael@0 | 1159 | /* store the argument type in the correct position of the parsed argument list */ |
michael@0 | 1160 | handlerNum = (uint16_t)(type - UPRINTF_BASE_FMT_HANDLERS); |
michael@0 | 1161 | if (handlerNum < UPRINTF_NUM_FMT_HANDLERS) { |
michael@0 | 1162 | typelist[pos] = g_u_printf_infos[ handlerNum ].info; |
michael@0 | 1163 | } else { |
michael@0 | 1164 | typelist[pos] = ufmt_empty; |
michael@0 | 1165 | } |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | /* store argument in arglist */ |
michael@0 | 1169 | for (pos = 0; pos < size; pos++) { |
michael@0 | 1170 | switch (typelist[pos]) { |
michael@0 | 1171 | case ufmt_string: |
michael@0 | 1172 | case ufmt_ustring: |
michael@0 | 1173 | case ufmt_pointer: |
michael@0 | 1174 | arglist[pos].ptrValue = va_arg(ap, void*); |
michael@0 | 1175 | break; |
michael@0 | 1176 | case ufmt_char: |
michael@0 | 1177 | case ufmt_uchar: |
michael@0 | 1178 | case ufmt_int: |
michael@0 | 1179 | if (islonglong[pos]) { |
michael@0 | 1180 | arglist[pos].int64Value = va_arg(ap, int64_t); |
michael@0 | 1181 | } |
michael@0 | 1182 | else { |
michael@0 | 1183 | arglist[pos].int64Value = va_arg(ap, int32_t); |
michael@0 | 1184 | } |
michael@0 | 1185 | break; |
michael@0 | 1186 | case ufmt_float: |
michael@0 | 1187 | arglist[pos].floatValue = (float) va_arg(ap, double); |
michael@0 | 1188 | break; |
michael@0 | 1189 | case ufmt_double: |
michael@0 | 1190 | arglist[pos].doubleValue = va_arg(ap, double); |
michael@0 | 1191 | break; |
michael@0 | 1192 | default: |
michael@0 | 1193 | /* else args is ignored */ |
michael@0 | 1194 | arglist[pos].ptrValue = NULL; |
michael@0 | 1195 | break; |
michael@0 | 1196 | } |
michael@0 | 1197 | } |
michael@0 | 1198 | |
michael@0 | 1199 | uprv_free(typelist); |
michael@0 | 1200 | uprv_free(islonglong); |
michael@0 | 1201 | |
michael@0 | 1202 | return arglist; |
michael@0 | 1203 | } |
michael@0 | 1204 | |
michael@0 | 1205 | /* We parse the argument list in Unicode */ |
michael@0 | 1206 | U_CFUNC int32_t |
michael@0 | 1207 | u_printf_parse(const u_printf_stream_handler *streamHandler, |
michael@0 | 1208 | const UChar *fmt, |
michael@0 | 1209 | void *context, |
michael@0 | 1210 | u_localized_print_string *locStringContext, |
michael@0 | 1211 | ULocaleBundle *formatBundle, |
michael@0 | 1212 | int32_t *written, |
michael@0 | 1213 | va_list ap) |
michael@0 | 1214 | { |
michael@0 | 1215 | uint16_t handlerNum; |
michael@0 | 1216 | ufmt_args args; |
michael@0 | 1217 | ufmt_type_info argType; |
michael@0 | 1218 | u_printf_handler *handler; |
michael@0 | 1219 | u_printf_spec spec; |
michael@0 | 1220 | u_printf_spec_info *info = &(spec.fInfo); |
michael@0 | 1221 | |
michael@0 | 1222 | const UChar *alias = fmt; |
michael@0 | 1223 | const UChar *backup; |
michael@0 | 1224 | const UChar *lastAlias; |
michael@0 | 1225 | const UChar *orgAlias = fmt; |
michael@0 | 1226 | /* parsed argument list */ |
michael@0 | 1227 | ufmt_args *arglist = NULL; /* initialized it to avoid compiler warnings */ |
michael@0 | 1228 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 1229 | if (!locStringContext || locStringContext->available >= 0) { |
michael@0 | 1230 | /* get the parsed list of argument types */ |
michael@0 | 1231 | arglist = parseArguments(orgAlias, ap, &status); |
michael@0 | 1232 | |
michael@0 | 1233 | /* Return error if parsing failed. */ |
michael@0 | 1234 | if (U_FAILURE(status)) { |
michael@0 | 1235 | return -1; |
michael@0 | 1236 | } |
michael@0 | 1237 | } |
michael@0 | 1238 | |
michael@0 | 1239 | /* iterate through the pattern */ |
michael@0 | 1240 | while(!locStringContext || locStringContext->available >= 0) { |
michael@0 | 1241 | |
michael@0 | 1242 | /* find the next '%' */ |
michael@0 | 1243 | lastAlias = alias; |
michael@0 | 1244 | while(*alias != UP_PERCENT && *alias != 0x0000) { |
michael@0 | 1245 | alias++; |
michael@0 | 1246 | } |
michael@0 | 1247 | |
michael@0 | 1248 | /* write any characters before the '%' */ |
michael@0 | 1249 | if(alias > lastAlias) { |
michael@0 | 1250 | *written += (streamHandler->write)(context, lastAlias, (int32_t)(alias - lastAlias)); |
michael@0 | 1251 | } |
michael@0 | 1252 | |
michael@0 | 1253 | /* break if at end of string */ |
michael@0 | 1254 | if(*alias == 0x0000) { |
michael@0 | 1255 | break; |
michael@0 | 1256 | } |
michael@0 | 1257 | |
michael@0 | 1258 | /* initialize spec to default values */ |
michael@0 | 1259 | spec.fWidthPos = -1; |
michael@0 | 1260 | spec.fPrecisionPos = -1; |
michael@0 | 1261 | spec.fArgPos = -1; |
michael@0 | 1262 | |
michael@0 | 1263 | uprv_memset(info, 0, sizeof(*info)); |
michael@0 | 1264 | info->fPrecision = -1; |
michael@0 | 1265 | info->fWidth = -1; |
michael@0 | 1266 | info->fPadChar = 0x0020; |
michael@0 | 1267 | |
michael@0 | 1268 | /* skip over the initial '%' */ |
michael@0 | 1269 | alias++; |
michael@0 | 1270 | |
michael@0 | 1271 | /* Check for positional argument */ |
michael@0 | 1272 | if(ISDIGIT(*alias)) { |
michael@0 | 1273 | |
michael@0 | 1274 | /* Save the current position */ |
michael@0 | 1275 | backup = alias; |
michael@0 | 1276 | |
michael@0 | 1277 | /* handle positional parameters */ |
michael@0 | 1278 | if(ISDIGIT(*alias)) { |
michael@0 | 1279 | spec.fArgPos = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1280 | |
michael@0 | 1281 | while(ISDIGIT(*alias)) { |
michael@0 | 1282 | spec.fArgPos *= 10; |
michael@0 | 1283 | spec.fArgPos += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1284 | } |
michael@0 | 1285 | } |
michael@0 | 1286 | |
michael@0 | 1287 | /* if there is no '$', don't read anything */ |
michael@0 | 1288 | if(*alias != SPEC_DOLLARSIGN) { |
michael@0 | 1289 | spec.fArgPos = -1; |
michael@0 | 1290 | alias = backup; |
michael@0 | 1291 | } |
michael@0 | 1292 | /* munge the '$' */ |
michael@0 | 1293 | else |
michael@0 | 1294 | alias++; |
michael@0 | 1295 | } |
michael@0 | 1296 | |
michael@0 | 1297 | /* Get any format flags */ |
michael@0 | 1298 | while(ISFLAG(*alias)) { |
michael@0 | 1299 | switch(*alias++) { |
michael@0 | 1300 | |
michael@0 | 1301 | /* left justify */ |
michael@0 | 1302 | case FLAG_MINUS: |
michael@0 | 1303 | info->fLeft = TRUE; |
michael@0 | 1304 | break; |
michael@0 | 1305 | |
michael@0 | 1306 | /* always show sign */ |
michael@0 | 1307 | case FLAG_PLUS: |
michael@0 | 1308 | info->fShowSign = TRUE; |
michael@0 | 1309 | break; |
michael@0 | 1310 | |
michael@0 | 1311 | /* use space if no sign present */ |
michael@0 | 1312 | case FLAG_SPACE: |
michael@0 | 1313 | info->fShowSign = TRUE; |
michael@0 | 1314 | info->fSpace = TRUE; |
michael@0 | 1315 | break; |
michael@0 | 1316 | |
michael@0 | 1317 | /* use alternate form */ |
michael@0 | 1318 | case FLAG_POUND: |
michael@0 | 1319 | info->fAlt = TRUE; |
michael@0 | 1320 | break; |
michael@0 | 1321 | |
michael@0 | 1322 | /* pad with leading zeroes */ |
michael@0 | 1323 | case FLAG_ZERO: |
michael@0 | 1324 | info->fZero = TRUE; |
michael@0 | 1325 | info->fPadChar = 0x0030; |
michael@0 | 1326 | break; |
michael@0 | 1327 | |
michael@0 | 1328 | /* pad character specified */ |
michael@0 | 1329 | case FLAG_PAREN: |
michael@0 | 1330 | |
michael@0 | 1331 | /* TODO test that all four are numbers */ |
michael@0 | 1332 | /* first four characters are hex values for pad char */ |
michael@0 | 1333 | info->fPadChar = (UChar)ufmt_digitvalue(*alias++); |
michael@0 | 1334 | info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); |
michael@0 | 1335 | info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); |
michael@0 | 1336 | info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); |
michael@0 | 1337 | |
michael@0 | 1338 | /* final character is ignored */ |
michael@0 | 1339 | alias++; |
michael@0 | 1340 | |
michael@0 | 1341 | break; |
michael@0 | 1342 | } |
michael@0 | 1343 | } |
michael@0 | 1344 | |
michael@0 | 1345 | /* Get the width */ |
michael@0 | 1346 | |
michael@0 | 1347 | /* width is specified out of line */ |
michael@0 | 1348 | if(*alias == SPEC_ASTERISK) { |
michael@0 | 1349 | |
michael@0 | 1350 | info->fWidth = -2; |
michael@0 | 1351 | |
michael@0 | 1352 | /* Skip the '*' */ |
michael@0 | 1353 | alias++; |
michael@0 | 1354 | |
michael@0 | 1355 | /* Save the current position */ |
michael@0 | 1356 | backup = alias; |
michael@0 | 1357 | |
michael@0 | 1358 | /* handle positional parameters */ |
michael@0 | 1359 | if(ISDIGIT(*alias)) { |
michael@0 | 1360 | spec.fWidthPos = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1361 | |
michael@0 | 1362 | while(ISDIGIT(*alias)) { |
michael@0 | 1363 | spec.fWidthPos *= 10; |
michael@0 | 1364 | spec.fWidthPos += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1365 | } |
michael@0 | 1366 | } |
michael@0 | 1367 | |
michael@0 | 1368 | /* if there is no '$', don't read anything */ |
michael@0 | 1369 | if(*alias != SPEC_DOLLARSIGN) { |
michael@0 | 1370 | spec.fWidthPos = -1; |
michael@0 | 1371 | alias = backup; |
michael@0 | 1372 | } |
michael@0 | 1373 | /* munge the '$' */ |
michael@0 | 1374 | else |
michael@0 | 1375 | alias++; |
michael@0 | 1376 | } |
michael@0 | 1377 | /* read the width, if present */ |
michael@0 | 1378 | else if(ISDIGIT(*alias)){ |
michael@0 | 1379 | info->fWidth = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1380 | |
michael@0 | 1381 | while(ISDIGIT(*alias)) { |
michael@0 | 1382 | info->fWidth *= 10; |
michael@0 | 1383 | info->fWidth += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1384 | } |
michael@0 | 1385 | } |
michael@0 | 1386 | |
michael@0 | 1387 | /* Get the precision */ |
michael@0 | 1388 | |
michael@0 | 1389 | if(*alias == SPEC_PERIOD) { |
michael@0 | 1390 | |
michael@0 | 1391 | /* eat up the '.' */ |
michael@0 | 1392 | alias++; |
michael@0 | 1393 | |
michael@0 | 1394 | /* precision is specified out of line */ |
michael@0 | 1395 | if(*alias == SPEC_ASTERISK) { |
michael@0 | 1396 | |
michael@0 | 1397 | info->fPrecision = -2; |
michael@0 | 1398 | |
michael@0 | 1399 | /* Skip the '*' */ |
michael@0 | 1400 | alias++; |
michael@0 | 1401 | |
michael@0 | 1402 | /* save the current position */ |
michael@0 | 1403 | backup = alias; |
michael@0 | 1404 | |
michael@0 | 1405 | /* handle positional parameters */ |
michael@0 | 1406 | if(ISDIGIT(*alias)) { |
michael@0 | 1407 | spec.fPrecisionPos = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1408 | |
michael@0 | 1409 | while(ISDIGIT(*alias)) { |
michael@0 | 1410 | spec.fPrecisionPos *= 10; |
michael@0 | 1411 | spec.fPrecisionPos += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1412 | } |
michael@0 | 1413 | |
michael@0 | 1414 | /* if there is no '$', don't read anything */ |
michael@0 | 1415 | if(*alias != SPEC_DOLLARSIGN) { |
michael@0 | 1416 | spec.fPrecisionPos = -1; |
michael@0 | 1417 | alias = backup; |
michael@0 | 1418 | } |
michael@0 | 1419 | else { |
michael@0 | 1420 | /* munge the '$' */ |
michael@0 | 1421 | alias++; |
michael@0 | 1422 | } |
michael@0 | 1423 | } |
michael@0 | 1424 | } |
michael@0 | 1425 | /* read the precision */ |
michael@0 | 1426 | else if(ISDIGIT(*alias)){ |
michael@0 | 1427 | info->fPrecision = (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1428 | |
michael@0 | 1429 | while(ISDIGIT(*alias)) { |
michael@0 | 1430 | info->fPrecision *= 10; |
michael@0 | 1431 | info->fPrecision += (int) (*alias++ - DIGIT_ZERO); |
michael@0 | 1432 | } |
michael@0 | 1433 | } |
michael@0 | 1434 | } |
michael@0 | 1435 | |
michael@0 | 1436 | /* Get any modifiers */ |
michael@0 | 1437 | if(ISMOD(*alias)) { |
michael@0 | 1438 | switch(*alias++) { |
michael@0 | 1439 | |
michael@0 | 1440 | /* short */ |
michael@0 | 1441 | case MOD_H: |
michael@0 | 1442 | info->fIsShort = TRUE; |
michael@0 | 1443 | break; |
michael@0 | 1444 | |
michael@0 | 1445 | /* long or long long */ |
michael@0 | 1446 | case MOD_LOWERL: |
michael@0 | 1447 | if(*alias == MOD_LOWERL) { |
michael@0 | 1448 | info->fIsLongLong = TRUE; |
michael@0 | 1449 | /* skip over the next 'l' */ |
michael@0 | 1450 | alias++; |
michael@0 | 1451 | } |
michael@0 | 1452 | else |
michael@0 | 1453 | info->fIsLong = TRUE; |
michael@0 | 1454 | break; |
michael@0 | 1455 | |
michael@0 | 1456 | /* long double */ |
michael@0 | 1457 | case MOD_L: |
michael@0 | 1458 | info->fIsLongDouble = TRUE; |
michael@0 | 1459 | break; |
michael@0 | 1460 | } |
michael@0 | 1461 | } |
michael@0 | 1462 | |
michael@0 | 1463 | /* finally, get the specifier letter */ |
michael@0 | 1464 | info->fSpec = *alias++; |
michael@0 | 1465 | info->fOrigSpec = info->fSpec; |
michael@0 | 1466 | |
michael@0 | 1467 | /* fill in the precision and width, if specified out of line */ |
michael@0 | 1468 | |
michael@0 | 1469 | /* width specified out of line */ |
michael@0 | 1470 | if(spec.fInfo.fWidth == -2) { |
michael@0 | 1471 | if(spec.fWidthPos == -1) { |
michael@0 | 1472 | /* read the width from the argument list */ |
michael@0 | 1473 | info->fWidth = va_arg(ap, int32_t); |
michael@0 | 1474 | } |
michael@0 | 1475 | /* else handle positional parameter */ |
michael@0 | 1476 | |
michael@0 | 1477 | /* if it's negative, take the absolute value and set left alignment */ |
michael@0 | 1478 | if(info->fWidth < 0) { |
michael@0 | 1479 | info->fWidth *= -1; /* Make positive */ |
michael@0 | 1480 | info->fLeft = TRUE; |
michael@0 | 1481 | } |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | /* precision specified out of line */ |
michael@0 | 1485 | if(info->fPrecision == -2) { |
michael@0 | 1486 | if(spec.fPrecisionPos == -1) { |
michael@0 | 1487 | /* read the precision from the argument list */ |
michael@0 | 1488 | info->fPrecision = va_arg(ap, int32_t); |
michael@0 | 1489 | } |
michael@0 | 1490 | /* else handle positional parameter */ |
michael@0 | 1491 | |
michael@0 | 1492 | /* if it's negative, set it to zero */ |
michael@0 | 1493 | if(info->fPrecision < 0) |
michael@0 | 1494 | info->fPrecision = 0; |
michael@0 | 1495 | } |
michael@0 | 1496 | |
michael@0 | 1497 | handlerNum = (uint16_t)(info->fSpec - UPRINTF_BASE_FMT_HANDLERS); |
michael@0 | 1498 | if (handlerNum < UPRINTF_NUM_FMT_HANDLERS) { |
michael@0 | 1499 | /* query the info function for argument information */ |
michael@0 | 1500 | argType = g_u_printf_infos[ handlerNum ].info; |
michael@0 | 1501 | |
michael@0 | 1502 | /* goto the correct argument on arg_list if position is specified */ |
michael@0 | 1503 | if (spec.fArgPos > 0) { |
michael@0 | 1504 | /* offset position by 1 */ |
michael@0 | 1505 | spec.fArgPos--; |
michael@0 | 1506 | switch(argType) { |
michael@0 | 1507 | case ufmt_count: |
michael@0 | 1508 | /* set the spec's width to the # of chars written */ |
michael@0 | 1509 | info->fWidth = *written; |
michael@0 | 1510 | /* fall through to set the pointer */ |
michael@0 | 1511 | case ufmt_string: |
michael@0 | 1512 | case ufmt_ustring: |
michael@0 | 1513 | case ufmt_pointer: |
michael@0 | 1514 | args.ptrValue = arglist[spec.fArgPos].ptrValue; |
michael@0 | 1515 | break; |
michael@0 | 1516 | case ufmt_char: |
michael@0 | 1517 | case ufmt_uchar: |
michael@0 | 1518 | case ufmt_int: |
michael@0 | 1519 | args.int64Value = arglist[spec.fArgPos].int64Value; |
michael@0 | 1520 | break; |
michael@0 | 1521 | case ufmt_float: |
michael@0 | 1522 | args.floatValue = arglist[spec.fArgPos].floatValue; |
michael@0 | 1523 | break; |
michael@0 | 1524 | case ufmt_double: |
michael@0 | 1525 | args.doubleValue = arglist[spec.fArgPos].doubleValue; |
michael@0 | 1526 | break; |
michael@0 | 1527 | default: |
michael@0 | 1528 | /* else args is ignored */ |
michael@0 | 1529 | args.ptrValue = NULL; |
michael@0 | 1530 | break; |
michael@0 | 1531 | } |
michael@0 | 1532 | } else { /* no positional argument specified */ |
michael@0 | 1533 | switch(argType) { |
michael@0 | 1534 | case ufmt_count: |
michael@0 | 1535 | /* set the spec's width to the # of chars written */ |
michael@0 | 1536 | info->fWidth = *written; |
michael@0 | 1537 | /* fall through to set the pointer */ |
michael@0 | 1538 | case ufmt_string: |
michael@0 | 1539 | case ufmt_ustring: |
michael@0 | 1540 | case ufmt_pointer: |
michael@0 | 1541 | args.ptrValue = va_arg(ap, void*); |
michael@0 | 1542 | break; |
michael@0 | 1543 | case ufmt_char: |
michael@0 | 1544 | case ufmt_uchar: |
michael@0 | 1545 | case ufmt_int: |
michael@0 | 1546 | if (info->fIsLongLong) { |
michael@0 | 1547 | args.int64Value = va_arg(ap, int64_t); |
michael@0 | 1548 | } |
michael@0 | 1549 | else { |
michael@0 | 1550 | args.int64Value = va_arg(ap, int32_t); |
michael@0 | 1551 | } |
michael@0 | 1552 | break; |
michael@0 | 1553 | case ufmt_float: |
michael@0 | 1554 | args.floatValue = (float) va_arg(ap, double); |
michael@0 | 1555 | break; |
michael@0 | 1556 | case ufmt_double: |
michael@0 | 1557 | args.doubleValue = va_arg(ap, double); |
michael@0 | 1558 | break; |
michael@0 | 1559 | default: |
michael@0 | 1560 | /* else args is ignored */ |
michael@0 | 1561 | args.ptrValue = NULL; |
michael@0 | 1562 | break; |
michael@0 | 1563 | } |
michael@0 | 1564 | } |
michael@0 | 1565 | |
michael@0 | 1566 | /* call the handler function */ |
michael@0 | 1567 | handler = g_u_printf_infos[ handlerNum ].handler; |
michael@0 | 1568 | if(handler != 0) { |
michael@0 | 1569 | *written += (*handler)(streamHandler, context, formatBundle, info, &args); |
michael@0 | 1570 | } |
michael@0 | 1571 | else { |
michael@0 | 1572 | /* just echo unknown tags */ |
michael@0 | 1573 | *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias)); |
michael@0 | 1574 | } |
michael@0 | 1575 | } |
michael@0 | 1576 | else { |
michael@0 | 1577 | /* just echo unknown tags */ |
michael@0 | 1578 | *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias)); |
michael@0 | 1579 | } |
michael@0 | 1580 | } |
michael@0 | 1581 | /* delete parsed argument list */ |
michael@0 | 1582 | if (arglist != NULL) { |
michael@0 | 1583 | uprv_free(arglist); |
michael@0 | 1584 | } |
michael@0 | 1585 | /* return # of characters in this format that have been parsed. */ |
michael@0 | 1586 | return (int32_t)(alias - fmt); |
michael@0 | 1587 | } |
michael@0 | 1588 | |
michael@0 | 1589 | #endif /* #if !UCONFIG_NO_FORMATTING */ |