vfs: check userland buffers before reading them.
[haiku.git] / src / kits / locale / FormattingConventions.cpp
blob4e7160bf7f3f653576abfaee4f23a3509928db80
1 /*
2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009-2010, Adrien Destugues, pulkomandy@gmail.com.
4 * Copyright 2010-2011, Oliver Tappe <zooey@hirschkaefer.de>.
5 * Distributed under the terms of the MIT License.
6 */
9 #include <unicode/uversion.h>
10 #include <FormattingConventions.h>
12 #include <AutoDeleter.h>
13 #include <IconUtils.h>
14 #include <List.h>
15 #include <Language.h>
16 #include <Locale.h>
17 #include <LocaleRoster.h>
18 #include <Resources.h>
19 #include <String.h>
20 #include <UnicodeChar.h>
22 #include <unicode/datefmt.h>
23 #include <unicode/locid.h>
24 #include <unicode/smpdtfmt.h>
25 #include <unicode/ulocdata.h>
26 #include <ICUWrapper.h>
28 #include <iostream>
29 #include <map>
30 #include <monetary.h>
31 #include <new>
32 #include <stdarg.h>
33 #include <stdlib.h>
36 // #pragma mark - helpers
39 static bool
40 FormatUsesAmPm(const BString& format)
42 if (format.Length() == 0)
43 return false;
45 bool inQuote = false;
46 for (const char* s = format.String(); *s != '\0'; ++s) {
47 switch (*s) {
48 case '\'':
49 inQuote = !inQuote;
50 break;
51 case 'a':
52 if (!inQuote)
53 return true;
54 break;
58 return false;
62 static void
63 CoerceFormatTo12HourClock(BString& format)
65 char* s = format.LockBuffer(format.Length());
66 if (s == NULL)
67 return;
69 // change format to use h instead of H, k instead of K, and append an
70 // am/pm marker
71 bool inQuote = false;
72 for (; *s != '\0'; ++s) {
73 switch (*s) {
74 case '\'':
75 inQuote = !inQuote;
76 break;
77 case 'H':
78 if (!inQuote)
79 *s = 'h';
80 break;
81 case 'K':
82 if (!inQuote)
83 *s = 'k';
84 break;
87 format.UnlockBuffer(format.Length());
89 format.Append(" a");
93 static void
94 CoerceFormatTo24HourClock(BString& format)
96 char* buffer = format.LockBuffer(format.Length());
97 char* currentPos = buffer;
98 if (currentPos == NULL)
99 return;
101 // change the format to use H instead of h, K instead of k, and determine
102 // and remove the am/pm marker (including leading whitespace)
103 bool inQuote = false;
104 bool lastWasWhitespace = false;
105 uint32 ch;
106 const char* amPmStartPos = NULL;
107 const char* amPmEndPos = NULL;
108 const char* lastWhitespaceStart = NULL;
109 for (char* previousPos = currentPos; (ch = BUnicodeChar::FromUTF8(
110 (const char**)&currentPos)) != 0; previousPos = currentPos) {
111 switch (ch) {
112 case '\'':
113 inQuote = !inQuote;
114 break;
115 case 'h':
116 if (!inQuote)
117 *previousPos = 'H';
118 break;
119 case 'k':
120 if (!inQuote)
121 *previousPos = 'K';
122 break;
123 case 'a':
124 if (!inQuote) {
125 if (lastWasWhitespace)
126 amPmStartPos = lastWhitespaceStart;
127 else
128 amPmStartPos = previousPos;
129 amPmEndPos = currentPos;
131 break;
132 default:
133 if (!inQuote && BUnicodeChar::IsWhitespace(ch)) {
134 if (!lastWasWhitespace) {
135 lastWhitespaceStart = previousPos;
136 lastWasWhitespace = true;
138 continue;
141 lastWasWhitespace = false;
144 format.UnlockBuffer(format.Length());
145 if (amPmStartPos != NULL && amPmEndPos > amPmStartPos)
146 format.Remove(amPmStartPos - buffer, amPmEndPos - amPmStartPos);
150 static void
151 CoerceFormatToAbbreviatedTimezone(BString& format)
153 char* s = format.LockBuffer(format.Length());
154 if (s == NULL)
155 return;
157 // replace a single 'z' with 'V'
158 bool inQuote = false;
159 bool lastWasZ = false;
160 for (; *s != '\0'; ++s) {
161 switch (*s) {
162 case '\'':
163 inQuote = !inQuote;
164 break;
165 case 'z':
166 if (!inQuote && !lastWasZ && *(s+1) != 'z')
167 *s = 'V';
168 lastWasZ = true;
169 continue;
171 lastWasZ = false;
173 format.UnlockBuffer(format.Length());
177 // #pragma mark - BFormattingConventions
180 enum ClockHoursState {
181 CLOCK_HOURS_UNSET = 0,
182 CLOCK_HOURS_24,
183 CLOCK_HOURS_12
187 BFormattingConventions::BFormattingConventions(const char* id)
189 fCachedUse24HourClock(CLOCK_HOURS_UNSET),
190 fExplicitUse24HourClock(CLOCK_HOURS_UNSET),
191 fUseStringsFromPreferredLanguage(false),
192 fICULocale(new icu::Locale(id))
197 BFormattingConventions::BFormattingConventions(
198 const BFormattingConventions& other)
200 fCachedNumericFormat(other.fCachedNumericFormat),
201 fCachedMonetaryFormat(other.fCachedMonetaryFormat),
202 fCachedUse24HourClock(other.fCachedUse24HourClock),
203 fExplicitNumericFormat(other.fExplicitNumericFormat),
204 fExplicitMonetaryFormat(other.fExplicitMonetaryFormat),
205 fExplicitUse24HourClock(other.fExplicitUse24HourClock),
206 fUseStringsFromPreferredLanguage(other.fUseStringsFromPreferredLanguage),
207 fICULocale(new icu::Locale(*other.fICULocale))
209 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
210 fCachedDateFormats[s] = other.fCachedDateFormats[s];
211 fExplicitDateFormats[s] = other.fExplicitDateFormats[s];
213 for (int t = 0; t < B_TIME_FORMAT_STYLE_COUNT; ++t) {
214 fCachedDateTimeFormats[s][t] = other.fCachedDateFormats[s][t];
215 fExplicitDateTimeFormats[s][t] = other.fExplicitDateFormats[s][t];
218 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
219 fCachedTimeFormats[s] = other.fCachedTimeFormats[s];
220 fExplicitTimeFormats[s] = other.fExplicitTimeFormats[s];
225 BFormattingConventions::BFormattingConventions(const BMessage* archive)
227 fCachedUse24HourClock(CLOCK_HOURS_UNSET),
228 fExplicitUse24HourClock(CLOCK_HOURS_UNSET),
229 fUseStringsFromPreferredLanguage(false)
231 BString conventionsID;
232 status_t status = archive->FindString("conventions", &conventionsID);
233 fICULocale = new icu::Locale(conventionsID);
235 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT && status == B_OK; ++s) {
236 BString format;
237 status = archive->FindString("dateFormat", s, &format);
238 if (status == B_OK)
239 fExplicitDateFormats[s] = format;
241 status = archive->FindString("timeFormat", s, &format);
242 if (status == B_OK)
243 fExplicitTimeFormats[s] = format;
246 if (status == B_OK) {
247 int8 use24HourClock;
248 status = archive->FindInt8("use24HourClock", &use24HourClock);
249 if (status == B_OK)
250 fExplicitUse24HourClock = use24HourClock;
252 if (status == B_OK) {
253 bool useStringsFromPreferredLanguage;
254 status = archive->FindBool("useStringsFromPreferredLanguage",
255 &useStringsFromPreferredLanguage);
256 if (status == B_OK)
257 fUseStringsFromPreferredLanguage = useStringsFromPreferredLanguage;
262 BFormattingConventions&
263 BFormattingConventions::operator=(const BFormattingConventions& other)
265 if (this == &other)
266 return *this;
268 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
269 fCachedDateFormats[s] = other.fCachedDateFormats[s];
270 fExplicitDateFormats[s] = other.fExplicitDateFormats[s];
271 for (int t = 0; t < B_TIME_FORMAT_STYLE_COUNT; ++t) {
272 fCachedDateTimeFormats[s][t] = other.fCachedDateTimeFormats[s][t];
273 fExplicitDateTimeFormats[s][t]
274 = other.fExplicitDateTimeFormats[s][t];
277 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
278 fCachedTimeFormats[s] = other.fCachedTimeFormats[s];
279 fExplicitTimeFormats[s] = other.fExplicitTimeFormats[s];
281 fCachedNumericFormat = other.fCachedNumericFormat;
282 fCachedMonetaryFormat = other.fCachedMonetaryFormat;
283 fCachedUse24HourClock = other.fCachedUse24HourClock;
285 fExplicitNumericFormat = other.fExplicitNumericFormat;
286 fExplicitMonetaryFormat = other.fExplicitMonetaryFormat;
287 fExplicitUse24HourClock = other.fExplicitUse24HourClock;
289 fUseStringsFromPreferredLanguage = other.fUseStringsFromPreferredLanguage;
291 *fICULocale = *other.fICULocale;
293 return *this;
297 BFormattingConventions::~BFormattingConventions()
299 delete fICULocale;
303 bool
304 BFormattingConventions::operator==(const BFormattingConventions& other) const
306 if (this == &other)
307 return true;
309 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
310 if (fExplicitDateFormats[s] != other.fExplicitDateFormats[s])
311 return false;
313 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
314 if (fExplicitTimeFormats[s] != other.fExplicitTimeFormats[s])
315 return false;
318 return fExplicitNumericFormat == other.fExplicitNumericFormat
319 && fExplicitMonetaryFormat == other.fExplicitMonetaryFormat
320 && fExplicitUse24HourClock == other.fExplicitUse24HourClock
321 && fUseStringsFromPreferredLanguage
322 == other.fUseStringsFromPreferredLanguage
323 && *fICULocale == *other.fICULocale;
327 bool
328 BFormattingConventions::operator!=(const BFormattingConventions& other) const
330 return !(*this == other);
334 const char*
335 BFormattingConventions::ID() const
337 return fICULocale->getName();
341 const char*
342 BFormattingConventions::LanguageCode() const
344 return fICULocale->getLanguage();
348 const char*
349 BFormattingConventions::CountryCode() const
351 const char* country = fICULocale->getCountry();
352 if (country == NULL || country[0] == '\0')
353 return NULL;
355 return country;
359 bool
360 BFormattingConventions::AreCountrySpecific() const
362 return CountryCode() != NULL;
366 status_t
367 BFormattingConventions::GetNativeName(BString& name) const
369 UnicodeString string;
370 fICULocale->getDisplayName(*fICULocale, string);
371 string.toTitle(NULL, *fICULocale);
373 name.Truncate(0);
374 BStringByteSink converter(&name);
375 string.toUTF8(converter);
377 return B_OK;
381 status_t
382 BFormattingConventions::GetName(BString& name,
383 const BLanguage* displayLanguage) const
385 BString displayLanguageID;
386 if (displayLanguage == NULL) {
387 BLanguage defaultLanguage;
388 BLocale::Default()->GetLanguage(&defaultLanguage);
389 displayLanguageID = defaultLanguage.Code();
390 } else {
391 displayLanguageID = displayLanguage->Code();
394 UnicodeString uString;
395 fICULocale->getDisplayName(Locale(displayLanguageID.String()), uString);
396 name.Truncate(0);
397 BStringByteSink stringConverter(&name);
398 uString.toUTF8(stringConverter);
400 return B_OK;
404 BMeasurementKind
405 BFormattingConventions::MeasurementKind() const
407 UErrorCode error = U_ZERO_ERROR;
408 switch (ulocdata_getMeasurementSystem(ID(), &error)) {
409 case UMS_US:
410 return B_US;
411 case UMS_SI:
412 default:
413 return B_METRIC;
418 status_t
419 BFormattingConventions::GetDateFormat(BDateFormatStyle style,
420 BString& outFormat) const
422 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
423 return B_BAD_VALUE;
425 outFormat = fExplicitDateFormats[style].Length()
426 ? fExplicitDateFormats[style]
427 : fCachedDateFormats[style];
429 if (outFormat.Length() > 0)
430 return B_OK;
432 ObjectDeleter<DateFormat> dateFormatter(
433 DateFormat::createDateInstance((DateFormat::EStyle)style, *fICULocale));
434 if (dateFormatter.Get() == NULL)
435 return B_NO_MEMORY;
437 SimpleDateFormat* dateFormatterImpl
438 = static_cast<SimpleDateFormat*>(dateFormatter.Get());
440 UnicodeString icuString;
441 dateFormatterImpl->toPattern(icuString);
442 BStringByteSink stringConverter(&outFormat);
443 icuString.toUTF8(stringConverter);
445 fCachedDateFormats[style] = outFormat;
447 return B_OK;
451 status_t
452 BFormattingConventions::GetTimeFormat(BTimeFormatStyle style,
453 BString& outFormat) const
455 if (style < 0 || style >= B_TIME_FORMAT_STYLE_COUNT)
456 return B_BAD_VALUE;
458 outFormat = fExplicitTimeFormats[style].Length()
459 ? fExplicitTimeFormats[style]
460 : fCachedTimeFormats[style];
462 if (outFormat.Length() > 0)
463 return B_OK;
465 ObjectDeleter<DateFormat> timeFormatter(
466 DateFormat::createTimeInstance((DateFormat::EStyle)style, *fICULocale));
467 if (timeFormatter.Get() == NULL)
468 return B_NO_MEMORY;
470 SimpleDateFormat* timeFormatterImpl
471 = static_cast<SimpleDateFormat*>(timeFormatter.Get());
473 UnicodeString icuString;
474 timeFormatterImpl->toPattern(icuString);
475 BStringByteSink stringConverter(&outFormat);
476 icuString.toUTF8(stringConverter);
478 CoerceFormatForClock(outFormat);
480 if (style != B_FULL_TIME_FORMAT) {
481 // use abbreviated timezone in short timezone format
482 CoerceFormatToAbbreviatedTimezone(outFormat);
485 fCachedTimeFormats[style] = outFormat;
487 return B_OK;
491 status_t
492 BFormattingConventions::GetDateTimeFormat(BDateFormatStyle dateStyle,
493 BTimeFormatStyle timeStyle, BString& outFormat) const
495 if (dateStyle < 0 || dateStyle >= B_DATE_FORMAT_STYLE_COUNT)
496 return B_BAD_VALUE;
498 if (timeStyle < 0 || timeStyle >= B_TIME_FORMAT_STYLE_COUNT)
499 return B_BAD_VALUE;
501 outFormat = fExplicitDateTimeFormats[dateStyle][timeStyle].Length()
502 ? fExplicitDateTimeFormats[dateStyle][timeStyle]
503 : fCachedDateTimeFormats[dateStyle][timeStyle];
505 if (outFormat.Length() > 0)
506 return B_OK;
508 ObjectDeleter<DateFormat> dateFormatter(
509 DateFormat::createDateTimeInstance((DateFormat::EStyle)dateStyle,
510 (DateFormat::EStyle)timeStyle, *fICULocale));
511 if (dateFormatter.Get() == NULL)
512 return B_NO_MEMORY;
514 SimpleDateFormat* dateFormatterImpl
515 = static_cast<SimpleDateFormat*>(dateFormatter.Get());
517 UnicodeString icuString;
518 dateFormatterImpl->toPattern(icuString);
519 BStringByteSink stringConverter(&outFormat);
520 icuString.toUTF8(stringConverter);
522 CoerceFormatForClock(outFormat);
524 if (dateStyle != B_FULL_DATE_FORMAT) {
525 // use abbreviated timezone in short timezone format
526 CoerceFormatToAbbreviatedTimezone(outFormat);
529 fCachedDateTimeFormats[dateStyle][timeStyle] = outFormat;
531 return B_OK;
535 status_t
536 BFormattingConventions::GetNumericFormat(BString& outFormat) const
538 // TODO!
539 return B_UNSUPPORTED;
543 status_t
544 BFormattingConventions::GetMonetaryFormat(BString& outFormat) const
546 // TODO!
547 return B_UNSUPPORTED;
551 void
552 BFormattingConventions::SetExplicitDateFormat(BDateFormatStyle style,
553 const BString& format)
555 fExplicitDateFormats[style] = format;
559 void
560 BFormattingConventions::SetExplicitTimeFormat(BTimeFormatStyle style,
561 const BString& format)
563 fExplicitTimeFormats[style] = format;
567 void
568 BFormattingConventions::SetExplicitDateTimeFormat(BDateFormatStyle dateStyle,
569 BTimeFormatStyle timeStyle, const BString& format)
571 fExplicitDateTimeFormats[dateStyle][timeStyle] = format;
575 void
576 BFormattingConventions::SetExplicitNumericFormat(const BString& format)
578 fExplicitNumericFormat = format;
582 void
583 BFormattingConventions::SetExplicitMonetaryFormat(const BString& format)
585 fExplicitMonetaryFormat = format;
589 bool
590 BFormattingConventions::UseStringsFromPreferredLanguage() const
592 return fUseStringsFromPreferredLanguage;
596 void
597 BFormattingConventions::SetUseStringsFromPreferredLanguage(bool value)
599 fUseStringsFromPreferredLanguage = value;
603 bool
604 BFormattingConventions::Use24HourClock() const
606 int8 use24HourClock = fExplicitUse24HourClock != CLOCK_HOURS_UNSET
607 ? fExplicitUse24HourClock : fCachedUse24HourClock;
609 if (use24HourClock == CLOCK_HOURS_UNSET) {
610 BString format;
611 GetTimeFormat(B_MEDIUM_TIME_FORMAT, format);
612 fCachedUse24HourClock
613 = FormatUsesAmPm(format) ? CLOCK_HOURS_12 : CLOCK_HOURS_24;
614 return fCachedUse24HourClock == CLOCK_HOURS_24;
617 return fExplicitUse24HourClock == CLOCK_HOURS_24;
621 void
622 BFormattingConventions::SetExplicitUse24HourClock(bool value)
624 int8 newUse24HourClock = value ? CLOCK_HOURS_24 : CLOCK_HOURS_12;
625 if (fExplicitUse24HourClock == newUse24HourClock)
626 return;
628 fExplicitUse24HourClock = newUse24HourClock;
630 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s)
631 fCachedTimeFormats[s].Truncate(0);
635 void
636 BFormattingConventions::UnsetExplicitUse24HourClock()
638 fExplicitUse24HourClock = CLOCK_HOURS_UNSET;
640 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s)
641 fCachedTimeFormats[s].Truncate(0);
645 status_t
646 BFormattingConventions::Archive(BMessage* archive, bool deep) const
648 status_t status = archive->AddString("conventions", fICULocale->getName());
649 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT && status == B_OK; ++s) {
650 status = archive->AddString("dateFormat", fExplicitDateFormats[s]);
651 if (status == B_OK)
652 status = archive->AddString("timeFormat", fExplicitTimeFormats[s]);
654 if (status == B_OK)
655 status = archive->AddInt8("use24HourClock", fExplicitUse24HourClock);
656 if (status == B_OK) {
657 status = archive->AddBool("useStringsFromPreferredLanguage",
658 fUseStringsFromPreferredLanguage);
661 return status;
665 void
666 BFormattingConventions::CoerceFormatForClock(BString& outFormat) const
668 int8 use24HourClock = fExplicitUse24HourClock != CLOCK_HOURS_UNSET
669 ? fExplicitUse24HourClock : fCachedUse24HourClock;
670 if (use24HourClock != CLOCK_HOURS_UNSET) {
671 // adjust to 12/24-hour clock as requested
672 bool localeUses24HourClock = !FormatUsesAmPm(outFormat);
673 if (localeUses24HourClock) {
674 if (use24HourClock == CLOCK_HOURS_12)
675 CoerceFormatTo12HourClock(outFormat);
676 } else {
677 if (use24HourClock == CLOCK_HOURS_24)
678 CoerceFormatTo24HourClock(outFormat);