1 /* 2 ******************************************************************************** 3 * Copyright (C) 2005-2015, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ******************************************************************************** 6 * 7 * File WINNMFMT.CPP 8 * 9 ******************************************************************************** 10 */ 11 12 #include "unicode/utypes.h" 13 14 #if U_PLATFORM_USES_ONLY_WIN32_API 15 16 #if !UCONFIG_NO_FORMATTING 17 18 #include "winnmfmt.h" 19 20 #include "unicode/format.h" 21 #include "unicode/numfmt.h" 22 #include "unicode/locid.h" 23 #include "unicode/ustring.h" 24 25 #include "cmemory.h" 26 #include "uassert.h" 27 #include "locmap.h" 28 29 # define WIN32_LEAN_AND_MEAN 30 # define VC_EXTRALEAN 31 # define NOUSER 32 # define NOSERVICE 33 # define NOIME 34 # define NOMCX 35 #include <windows.h> 36 #include <stdio.h> 37 38 U_NAMESPACE_BEGIN 39 40 union FormatInfo 41 { 42 NUMBERFMTW number; 43 CURRENCYFMTW currency; 44 }; 45 46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat) 47 48 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) 49 #define DELETE_ARRAY(array) uprv_free((void *) (array)) 50 51 #define STACK_BUFFER_SIZE 32 52 53 /* 54 * Turns a string of the form "3;2;0" into the grouping UINT 55 * needed for NUMBERFMT and CURRENCYFMT. If the string does not 56 * end in ";0" then the return value should be multiplied by 10. 57 * (e.g. "3" => 30, "3;2" => 320) 58 */ 59 static UINT getGrouping(const char *grouping) 60 { 61 UINT g = 0; 62 const char *s; 63 64 for (s = grouping; *s != '\0'; s += 1) { 65 if (*s > '0' && *s < '9') { 66 g = g * 10 + (*s - '0'); 67 } else if (*s != ';') { 68 break; 69 } 70 } 71 72 if (*s != '0') { 73 g *= 10; 74 } 75 76 return g; 77 } 78 79 static void getNumberFormat(NUMBERFMTW *fmt, int32_t lcid) 80 { 81 char buf[10]; 82 83 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); 84 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); 85 86 GetLocaleInfoA(lcid, LOCALE_SGROUPING, buf, 10); 87 fmt->Grouping = getGrouping(buf); 88 89 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); 90 GetLocaleInfoW(lcid, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6); 91 92 fmt->lpThousandSep = NEW_ARRAY(UChar, 6); 93 GetLocaleInfoW(lcid, LOCALE_STHOUSAND, fmt->lpThousandSep, 6); 94 95 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); 96 } 97 98 static void freeNumberFormat(NUMBERFMTW *fmt) 99 { 100 if (fmt != NULL) { 101 DELETE_ARRAY(fmt->lpThousandSep); 102 DELETE_ARRAY(fmt->lpDecimalSep); 103 } 104 } 105 106 static void getCurrencyFormat(CURRENCYFMTW *fmt, int32_t lcid) 107 { 108 char buf[10]; 109 110 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); 111 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); 112 113 GetLocaleInfoA(lcid, LOCALE_SMONGROUPING, buf, sizeof(buf)); 114 fmt->Grouping = getGrouping(buf); 115 116 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); 117 GetLocaleInfoW(lcid, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6); 118 119 fmt->lpThousandSep = NEW_ARRAY(UChar, 6); 120 GetLocaleInfoW(lcid, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6); 121 122 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); 123 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT)); 124 125 fmt->lpCurrencySymbol = NEW_ARRAY(UChar, 8); 126 GetLocaleInfoW(lcid, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8); 127 } 128 129 static void freeCurrencyFormat(CURRENCYFMTW *fmt) 130 { 131 if (fmt != NULL) { 132 DELETE_ARRAY(fmt->lpCurrencySymbol); 133 DELETE_ARRAY(fmt->lpThousandSep); 134 DELETE_ARRAY(fmt->lpDecimalSep); 135 } 136 } 137 138 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status) 139 : NumberFormat(), fCurrency(currency), fFormatInfo(NULL), fFractionDigitsSet(FALSE) 140 { 141 if (!U_FAILURE(status)) { 142 fLCID = locale.getLCID(); 143 144 // Resolve actual locale to be used later 145 UErrorCode tmpsts = U_ZERO_ERROR; 146 char tmpLocID[ULOC_FULLNAME_CAPACITY]; 147 int32_t len = uloc_getLocaleForLCID(fLCID, tmpLocID, sizeof(tmpLocID)/sizeof(tmpLocID[0]) - 1, &tmpsts); 148 if (U_SUCCESS(tmpsts)) { 149 tmpLocID[len] = 0; 150 fLocale = Locale((const char*)tmpLocID); 151 } 152 153 fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo)); 154 155 if (fCurrency) { 156 getCurrencyFormat(&fFormatInfo->currency, fLCID); 157 } else { 158 getNumberFormat(&fFormatInfo->number, fLCID); 159 } 160 } 161 } 162 163 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other) 164 : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo))) 165 { 166 if (fFormatInfo != NULL) { 167 uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo)); 168 } 169 *this = other; 170 } 171 172 Win32NumberFormat::~Win32NumberFormat() 173 { 174 if (fFormatInfo != NULL) { 175 if (fCurrency) { 176 freeCurrencyFormat(&fFormatInfo->currency); 177 } else { 178 freeNumberFormat(&fFormatInfo->number); 179 } 180 181 uprv_free(fFormatInfo); 182 } 183 } 184 185 Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other) 186 { 187 NumberFormat::operator=(other); 188 189 this->fCurrency = other.fCurrency; 190 this->fLocale = other.fLocale; 191 this->fLCID = other.fLCID; 192 this->fFractionDigitsSet = other.fFractionDigitsSet; 193 194 if (fCurrency) { 195 freeCurrencyFormat(&fFormatInfo->currency); 196 getCurrencyFormat(&fFormatInfo->currency, fLCID); 197 } else { 198 freeNumberFormat(&fFormatInfo->number); 199 getNumberFormat(&fFormatInfo->number, fLCID); 200 } 201 202 return *this; 203 } 204 205 Format *Win32NumberFormat::clone(void) const 206 { 207 return new Win32NumberFormat(*this); 208 } 209 210 UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const 211 { 212 return format(getMaximumFractionDigits(), appendTo, L"%.16f", number); 213 } 214 215 UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const 216 { 217 return format(getMinimumFractionDigits(), appendTo, L"%I32d", number); 218 } 219 220 UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const 221 { 222 return format(getMinimumFractionDigits(), appendTo, L"%I64d", number); 223 } 224 225 void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const 226 { 227 UErrorCode status = U_ZERO_ERROR; 228 NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(fLocale, status) : NumberFormat::createInstance(fLocale, status); 229 230 nf->parse(text, result, parsePosition); 231 delete nf; 232 } 233 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue) 234 { 235 fFractionDigitsSet = TRUE; 236 NumberFormat::setMaximumFractionDigits(newValue); 237 } 238 239 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue) 240 { 241 fFractionDigitsSet = TRUE; 242 NumberFormat::setMinimumFractionDigits(newValue); 243 } 244 245 UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appendTo, const wchar_t *fmt, ...) const 246 { 247 wchar_t nStackBuffer[STACK_BUFFER_SIZE]; 248 wchar_t *nBuffer = nStackBuffer; 249 va_list args; 250 int result; 251 252 nBuffer[0] = 0x0000; 253 254 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus), 255 we don't need to reallocate the buffer. */ 256 va_start(args, fmt); 257 result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args); 258 va_end(args); 259 260 /* Just to make sure of the above statement, we add this assert */ 261 U_ASSERT(result >=0); 262 // The following code is not used because _vscwprintf isn't available on MinGW at the moment. 263 /*if (result < 0) { 264 int newLength; 265 266 va_start(args, fmt); 267 newLength = _vscwprintf(fmt, args); 268 va_end(args); 269 270 nBuffer = NEW_ARRAY(UChar, newLength + 1); 271 272 va_start(args, fmt); 273 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args); 274 va_end(args); 275 }*/ 276 277 // vswprintf is sensitive to the locale set by setlocale. For some locales 278 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW 279 // and GetCurrencyFormatW both expect to see. 280 // 281 // To fix this, we scan over the string and replace the first non-digits, except 282 // for a leading "-", with a "." 283 // 284 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the 285 // number, and 0 otherwise. 286 for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) { 287 if (*p < L'0' || *p > L'9') { 288 *p = L'.'; 289 break; 290 } 291 } 292 293 UChar stackBuffer[STACK_BUFFER_SIZE]; 294 UChar *buffer = stackBuffer; 295 FormatInfo formatInfo; 296 297 formatInfo = *fFormatInfo; 298 buffer[0] = 0x0000; 299 300 if (fCurrency) { 301 if (fFractionDigitsSet) { 302 formatInfo.currency.NumDigits = (UINT) numDigits; 303 } 304 305 if (!isGroupingUsed()) { 306 formatInfo.currency.Grouping = 0; 307 } 308 309 result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE); 310 311 if (result == 0) { 312 DWORD lastError = GetLastError(); 313 314 if (lastError == ERROR_INSUFFICIENT_BUFFER) { 315 int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, NULL, 0); 316 317 buffer = NEW_ARRAY(UChar, newLength); 318 buffer[0] = 0x0000; 319 GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, newLength); 320 } 321 } 322 } else { 323 if (fFractionDigitsSet) { 324 formatInfo.number.NumDigits = (UINT) numDigits; 325 } 326 327 if (!isGroupingUsed()) { 328 formatInfo.number.Grouping = 0; 329 } 330 331 result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE); 332 333 if (result == 0) { 334 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 335 int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, NULL, 0); 336 337 buffer = NEW_ARRAY(UChar, newLength); 338 buffer[0] = 0x0000; 339 GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, newLength); 340 } 341 } 342 } 343 344 appendTo.append(buffer, (int32_t) wcslen(buffer)); 345 346 if (buffer != stackBuffer) { 347 DELETE_ARRAY(buffer); 348 } 349 350 /*if (nBuffer != nStackBuffer) { 351 DELETE_ARRAY(nBuffer); 352 }*/ 353 354 return appendTo; 355 } 356 357 U_NAMESPACE_END 358 359 #endif /* #if !UCONFIG_NO_FORMATTING */ 360 361 #endif // U_PLATFORM_USES_ONLY_WIN32_API 362