winnmfmt.cpp 11 KB

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