vfs: check userland buffers before reading them.
[haiku.git] / src / system / libroot / add-ons / icu / ICUMonetaryData.cpp
blob03d7634dd5849c5d4489fa64a1cc22d5b9f84d29
1 /*
2 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "ICUMonetaryData.h"
9 #include <langinfo.h>
10 #include <limits.h>
11 #include <string.h>
12 #include <strings.h>
15 namespace BPrivate {
16 namespace Libroot {
19 ICUMonetaryData::ICUMonetaryData(pthread_key_t tlsKey, struct lconv& localeConv)
21 inherited(tlsKey),
22 fLocaleConv(localeConv),
23 fPosixLocaleConv(NULL)
25 fLocaleConv.int_curr_symbol = fIntCurrSymbol;
26 fLocaleConv.currency_symbol = fCurrencySymbol;
27 fLocaleConv.mon_decimal_point = fDecimalPoint;
28 fLocaleConv.mon_thousands_sep = fThousandsSep;
29 fLocaleConv.mon_grouping = fGrouping;
30 fLocaleConv.positive_sign = fPositiveSign;
31 fLocaleConv.negative_sign = fNegativeSign;
35 void
36 ICUMonetaryData::Initialize(LocaleMonetaryDataBridge* dataBridge)
38 fPosixLocaleConv = dataBridge->posixLocaleConv;
42 status_t
43 ICUMonetaryData::SetTo(const Locale& locale, const char* posixLocaleName)
45 status_t result = inherited::SetTo(locale, posixLocaleName);
47 if (result == B_OK) {
48 UErrorCode icuStatus = U_ZERO_ERROR;
49 UChar intlCurrencySeparatorChar = CHAR_MAX;
50 DecimalFormat* currencyFormat = dynamic_cast<DecimalFormat*>(
51 NumberFormat::createCurrencyInstance(locale, icuStatus));
52 if (!U_SUCCESS(icuStatus))
53 return B_UNSUPPORTED;
54 if (!currencyFormat)
55 return B_BAD_TYPE;
57 const DecimalFormatSymbols* formatSymbols
58 = currencyFormat->getDecimalFormatSymbols();
59 if (!formatSymbols)
60 result = B_BAD_DATA;
62 if (result == B_OK) {
63 result = _SetLocaleconvEntry(formatSymbols, fDecimalPoint,
64 DecimalFormatSymbols::kMonetarySeparatorSymbol);
66 if (result == B_OK) {
67 result = _SetLocaleconvEntry(formatSymbols, fThousandsSep,
68 DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol);
70 if (result == B_OK) {
71 int32 groupingSize = currencyFormat->getGroupingSize();
72 if (groupingSize < 1)
73 fGrouping[0] = '\0';
74 else {
75 fGrouping[0] = groupingSize;
76 int32 secondaryGroupingSize
77 = currencyFormat->getSecondaryGroupingSize();
78 if (secondaryGroupingSize < 1)
79 fGrouping[1] = '\0';
80 else {
81 fGrouping[1] = secondaryGroupingSize;
82 fGrouping[2] = '\0';
86 if (result == B_OK) {
87 fLocaleConv.int_frac_digits
88 = currencyFormat->getMinimumFractionDigits();
89 fLocaleConv.frac_digits
90 = currencyFormat->getMinimumFractionDigits();
92 if (result == B_OK) {
93 UnicodeString positivePrefix, positiveSuffix, negativePrefix,
94 negativeSuffix;
95 currencyFormat->getPositivePrefix(positivePrefix);
96 currencyFormat->getPositiveSuffix(positiveSuffix);
97 currencyFormat->getNegativePrefix(negativePrefix);
98 currencyFormat->getNegativeSuffix(negativeSuffix);
99 UnicodeString currencySymbol = formatSymbols->getSymbol(
100 DecimalFormatSymbols::kCurrencySymbol);
101 UnicodeString plusSymbol = formatSymbols->getSymbol(
102 DecimalFormatSymbols::kPlusSignSymbol);
103 UnicodeString minusSymbol = formatSymbols->getSymbol(
104 DecimalFormatSymbols::kMinusSignSymbol);
106 // fill national values
107 int32 positiveCurrencyFlags = _DetermineCurrencyPosAndSeparator(
108 positivePrefix, positiveSuffix, plusSymbol, currencySymbol,
109 intlCurrencySeparatorChar);
110 fLocaleConv.p_cs_precedes
111 = (positiveCurrencyFlags & kCsPrecedesFlag) ? 1 : 0;
112 fLocaleConv.p_sep_by_space
113 = (positiveCurrencyFlags & kSepBySpaceFlag) ? 1 : 0;
115 int32 negativeCurrencyFlags = _DetermineCurrencyPosAndSeparator(
116 negativePrefix, negativeSuffix, minusSymbol, currencySymbol,
117 intlCurrencySeparatorChar);
118 fLocaleConv.n_cs_precedes
119 = (negativeCurrencyFlags & kCsPrecedesFlag) ? 1 : 0;
120 fLocaleConv.n_sep_by_space
121 = (negativeCurrencyFlags & kSepBySpaceFlag) ? 1 : 0;
123 fLocaleConv.p_sign_posn = _DetermineSignPos(positivePrefix,
124 positiveSuffix, plusSymbol, currencySymbol);
125 fLocaleConv.n_sign_posn = _DetermineSignPos(negativePrefix,
126 negativeSuffix, minusSymbol, currencySymbol);
127 if (fLocaleConv.p_sign_posn == CHAR_MAX) {
128 // usually there is no positive sign indicator, so we
129 // adopt the sign pos of the negative sign symbol
130 fLocaleConv.p_sign_posn = fLocaleConv.n_sign_posn;
133 // copy national to international values, as ICU does not seem
134 // to have separate info for those
135 fLocaleConv.int_p_cs_precedes = fLocaleConv.p_cs_precedes;
136 fLocaleConv.int_p_sep_by_space = fLocaleConv.p_sep_by_space;
137 fLocaleConv.int_n_cs_precedes = fLocaleConv.n_cs_precedes;
138 fLocaleConv.int_n_sep_by_space = fLocaleConv.n_sep_by_space;
139 fLocaleConv.int_p_sign_posn = fLocaleConv.p_sign_posn;
140 fLocaleConv.int_n_sign_posn = fLocaleConv.n_sign_posn;
142 // only set sign symbols if they are actually used in any pattern
143 if (positivePrefix.indexOf(plusSymbol) > -1
144 || positiveSuffix.indexOf(plusSymbol) > -1) {
145 result = _SetLocaleconvEntry(formatSymbols, fPositiveSign,
146 DecimalFormatSymbols::kPlusSignSymbol);
147 } else
148 fPositiveSign[0] = '\0';
149 if (negativePrefix.indexOf(minusSymbol) > -1
150 || negativeSuffix.indexOf(minusSymbol) > -1) {
151 result = _SetLocaleconvEntry(formatSymbols, fNegativeSign,
152 DecimalFormatSymbols::kMinusSignSymbol);
153 } else
154 fNegativeSign[0] = '\0';
156 if (result == B_OK) {
157 UnicodeString intlCurrencySymbol = formatSymbols->getSymbol(
158 DecimalFormatSymbols::kIntlCurrencySymbol);
159 if (intlCurrencySeparatorChar != CHAR_MAX)
160 intlCurrencySymbol += intlCurrencySeparatorChar;
161 else
162 intlCurrencySymbol += ' ';
163 result = _ConvertUnicodeStringToLocaleconvEntry(intlCurrencySymbol,
164 fIntCurrSymbol, skLCBufSize);
166 if (result == B_OK) {
167 result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol,
168 DecimalFormatSymbols::kCurrencySymbol);
169 if (fCurrencySymbol[0] == '\0') {
170 // fall back to the international currency symbol
171 result = _SetLocaleconvEntry(formatSymbols, fCurrencySymbol,
172 DecimalFormatSymbols::kIntlCurrencySymbol);
173 fCurrencySymbol[3] = '\0';
174 // drop separator char that's contained in int-curr-symbol
178 delete currencyFormat;
181 return result;
185 status_t
186 ICUMonetaryData::SetToPosix()
188 status_t result = inherited::SetToPosix();
190 if (result == B_OK) {
191 strcpy(fDecimalPoint, fPosixLocaleConv->mon_decimal_point);
192 strcpy(fThousandsSep, fPosixLocaleConv->mon_thousands_sep);
193 strcpy(fGrouping, fPosixLocaleConv->mon_grouping);
194 strcpy(fCurrencySymbol, fPosixLocaleConv->currency_symbol);
195 strcpy(fIntCurrSymbol, fPosixLocaleConv->int_curr_symbol);
196 strcpy(fPositiveSign, fPosixLocaleConv->positive_sign);
197 strcpy(fNegativeSign, fPosixLocaleConv->negative_sign);
198 fLocaleConv.int_frac_digits = fPosixLocaleConv->int_frac_digits;
199 fLocaleConv.frac_digits = fPosixLocaleConv->frac_digits;
200 fLocaleConv.p_cs_precedes = fPosixLocaleConv->p_cs_precedes;
201 fLocaleConv.p_sep_by_space = fPosixLocaleConv->p_sep_by_space;
202 fLocaleConv.n_cs_precedes = fPosixLocaleConv->n_cs_precedes;
203 fLocaleConv.n_sep_by_space = fPosixLocaleConv->n_sep_by_space;
204 fLocaleConv.p_sign_posn = fPosixLocaleConv->p_sign_posn;
205 fLocaleConv.n_sign_posn = fPosixLocaleConv->n_sign_posn;
206 fLocaleConv.int_p_cs_precedes = fPosixLocaleConv->int_p_cs_precedes;
207 fLocaleConv.int_p_sep_by_space = fPosixLocaleConv->int_p_sep_by_space;
208 fLocaleConv.int_n_cs_precedes = fPosixLocaleConv->int_n_cs_precedes;
209 fLocaleConv.int_n_sep_by_space = fPosixLocaleConv->int_n_sep_by_space;
210 fLocaleConv.int_p_sign_posn = fPosixLocaleConv->int_p_sign_posn;
211 fLocaleConv.int_n_sign_posn = fPosixLocaleConv->int_n_sign_posn;
214 return result;
218 const char*
219 ICUMonetaryData::GetLanginfo(int index)
221 switch(index) {
222 case CRNCYSTR:
223 return fCurrencySymbol;
224 default:
225 return "";
230 int32
231 ICUMonetaryData::_DetermineCurrencyPosAndSeparator(const UnicodeString& prefix,
232 const UnicodeString& suffix, const UnicodeString& signSymbol,
233 const UnicodeString& currencySymbol, UChar& currencySeparatorChar)
235 int32 result = 0;
237 int32 currencySymbolPos = prefix.indexOf(currencySymbol);
238 if (currencySymbolPos > -1) {
239 result |= kCsPrecedesFlag;
240 int32 signSymbolPos = prefix.indexOf(signSymbol);
241 // if a char is following the currency symbol, we assume it's
242 // the separator (usually space), but we need to take care to
243 // skip over the sign symbol, if found
244 int32 potentialSeparatorPos
245 = currencySymbolPos + currencySymbol.length();
246 if (potentialSeparatorPos == signSymbolPos)
247 potentialSeparatorPos++;
248 if (prefix.charAt(potentialSeparatorPos) != 0xFFFF) {
249 // We can't use the actual separator char since this is usually
250 // 'c2a0' (non-breakable space), which is not available in the
251 // ASCII charset used/assumed by POSIX lconv. So we use space
252 // instead.
253 currencySeparatorChar = ' ';
254 result |= kSepBySpaceFlag;
256 } else {
257 currencySymbolPos = suffix.indexOf(currencySymbol);
258 if (currencySymbolPos > -1) {
259 int32 signSymbolPos = suffix.indexOf(signSymbol);
260 // if a char is preceding the currency symbol, we assume
261 // it's the separator (usually space), but we need to take
262 // care to skip the sign symbol, if found
263 int32 potentialSeparatorPos = currencySymbolPos - 1;
264 if (potentialSeparatorPos == signSymbolPos)
265 potentialSeparatorPos--;
266 if (suffix.charAt(potentialSeparatorPos) != 0xFFFF) {
267 // We can't use the actual separator char since this is usually
268 // 'c2a0' (non-breakable space), which is not available in the
269 // ASCII charset used/assumed by POSIX lconv. So we use space
270 // instead.
271 currencySeparatorChar = ' ';
272 result |= kSepBySpaceFlag;
277 return result;
282 * This method determines the positive/negative sign position value according to
283 * the following map (where '$' indicated the currency symbol, '#' the number
284 * value, and '-' or parantheses the sign symbol):
285 * ($#) -> 0
286 * (#$) -> 0
287 * -$# -> 1
288 * -#$ -> 1
289 * $-# -> 4
290 * $#- -> 2
291 * #$- -> 2
292 * #-$ -> 3
294 int32
295 ICUMonetaryData::_DetermineSignPos(const UnicodeString& prefix,
296 const UnicodeString& suffix, const UnicodeString& signSymbol,
297 const UnicodeString& currencySymbol)
299 if (prefix.indexOf(UnicodeString("(", "")) >= 0
300 && suffix.indexOf(UnicodeString(")", "")) >= 0)
301 return kParenthesesAroundCurrencyAndValue;
303 UnicodeString value("#", "");
304 UnicodeString prefixNumberSuffixString = prefix + value + suffix;
305 int32 signSymbolPos = prefixNumberSuffixString.indexOf(signSymbol);
306 if (signSymbolPos >= 0) {
307 int32 valuePos = prefixNumberSuffixString.indexOf(value);
308 int32 currencySymbolPos
309 = prefixNumberSuffixString.indexOf(currencySymbol);
311 if (signSymbolPos < valuePos && signSymbolPos < currencySymbolPos)
312 return kSignPrecedesCurrencyAndValue;
313 if (signSymbolPos > valuePos && signSymbolPos > currencySymbolPos)
314 return kSignSucceedsCurrencyAndValue;
315 if (signSymbolPos == currencySymbolPos - 1)
316 return kSignImmediatelyPrecedesCurrency;
317 if (signSymbolPos == currencySymbolPos + currencySymbol.length())
318 return kSignImmediatelySucceedsCurrency;
321 return CHAR_MAX;
325 } // namespace Libroot
326 } // namespace BPrivate