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