1/*
2 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ICULocaleBackend.h"
8
9#include <new>
10
11#include <langinfo.h>
12#include <locale.h>
13#include <string.h>
14
15#include <ErrnoMaintainer.h>
16
17
18namespace BPrivate {
19namespace Libroot {
20
21
22extern "C" LocaleBackend*
23CreateInstance()
24{
25	return new(std::nothrow) ICULocaleBackend();
26}
27
28
29ICULocaleBackend::ICULocaleBackend()
30	:
31	fThreadLocalStorageKey(_CreateThreadLocalStorageKey()),
32	fCollateData(fThreadLocalStorageKey),
33	fCtypeData(fThreadLocalStorageKey),
34	fMessagesData(fThreadLocalStorageKey),
35	fMonetaryData(fThreadLocalStorageKey, fLocaleConv),
36	fNumericData(fThreadLocalStorageKey, fLocaleConv),
37	fTimeData(fThreadLocalStorageKey, fLCTimeInfo, fMessagesData),
38	fTimeConversion(fTimeData)
39{
40}
41
42
43ICULocaleBackend::~ICULocaleBackend()
44{
45	pthread_key_delete(fThreadLocalStorageKey);
46}
47
48
49void
50ICULocaleBackend::Initialize(LocaleDataBridge* dataBridge)
51{
52	ErrnoMaintainer errnoMaintainer;
53
54	fCtypeData.Initialize(&dataBridge->ctypeDataBridge);
55	fMessagesData.Initialize(&dataBridge->messagesDataBridge);
56	fMonetaryData.Initialize(&dataBridge->monetaryDataBridge);
57	fNumericData.Initialize(&dataBridge->numericDataBridge);
58	fTimeData.Initialize(&dataBridge->timeDataBridge);
59	fTimeConversion.Initialize(&dataBridge->timeConversionDataBridge);
60
61	fPosixLanginfo = dataBridge->posixLanginfo;
62
63	_SetPosixLocale(LC_ALL);
64}
65
66
67const char*
68ICULocaleBackend::SetLocale(int category, const char* posixLocaleName)
69{
70	ErrnoMaintainer errnoMaintainer;
71
72	if (posixLocaleName == NULL)
73		return _QueryLocale(category);
74
75	if (strcasecmp(posixLocaleName, "POSIX") == 0
76		|| strcasecmp(posixLocaleName, "C") == 0)
77		return _SetPosixLocale(category);
78
79	Locale locale = Locale::createCanonical(posixLocaleName);
80	switch (category) {
81		case LC_ALL:
82			if (fCollateData.SetTo(locale, posixLocaleName) != B_OK
83				|| fCtypeData.SetTo(locale, posixLocaleName) != B_OK
84				|| fMessagesData.SetTo(locale, posixLocaleName) != B_OK
85				|| fMonetaryData.SetTo(locale, posixLocaleName) != B_OK
86				|| fNumericData.SetTo(locale, posixLocaleName) != B_OK
87				|| fTimeData.SetTo(locale, posixLocaleName) != B_OK)
88				return NULL;
89			break;
90		case LC_COLLATE:
91			if (fCollateData.SetTo(locale, posixLocaleName) != B_OK)
92				return NULL;
93			break;
94		case LC_CTYPE:
95			if (fCtypeData.SetTo(locale, posixLocaleName) != B_OK)
96				return NULL;
97			break;
98		case LC_MESSAGES:
99			if (fMessagesData.SetTo(locale, posixLocaleName) != B_OK)
100				return NULL;
101			break;
102		case LC_MONETARY:
103			if (fMonetaryData.SetTo(locale, posixLocaleName) != B_OK)
104				return NULL;
105			break;
106		case LC_NUMERIC:
107			if (fNumericData.SetTo(locale, posixLocaleName) != B_OK)
108				return NULL;
109			break;
110		case LC_TIME:
111			if (fTimeData.SetTo(locale, posixLocaleName) != B_OK)
112				return NULL;
113			break;
114		default:
115			// unsupported category
116			return NULL;
117	}
118
119	return posixLocaleName;
120}
121
122
123const struct lconv*
124ICULocaleBackend::LocaleConv()
125{
126	return &fLocaleConv;
127}
128
129
130const struct lc_time_t*
131ICULocaleBackend::LCTimeInfo()
132{
133	return &fLCTimeInfo;
134}
135
136
137int
138ICULocaleBackend::IsWCType(wint_t wc, wctype_t charClass)
139{
140	ErrnoMaintainer errnoMaintainer;
141
142	return fCtypeData.IsWCType(wc, charClass);
143}
144
145
146status_t
147ICULocaleBackend::ToWCTrans(wint_t wc, wctrans_t transition, wint_t& result)
148{
149	ErrnoMaintainer errnoMaintainer;
150
151	return fCtypeData.ToWCTrans(wc, transition, result);
152}
153
154
155status_t
156ICULocaleBackend::MultibyteToWchar(wchar_t* wcOut, const char* mb,
157	size_t mbLength, mbstate_t* mbState, size_t& lengthOut)
158{
159	ErrnoMaintainer errnoMaintainer;
160
161	return fCtypeData.MultibyteToWchar(wcOut, mb, mbLength, mbState, lengthOut);
162}
163
164
165status_t
166ICULocaleBackend::MultibyteStringToWchar(wchar_t* wcDest, size_t wcDestLength,
167	const char** mbSource, size_t mbSourceLength, mbstate_t* mbState,
168	size_t& lengthOut)
169{
170	ErrnoMaintainer errnoMaintainer;
171
172	return fCtypeData.MultibyteStringToWchar(wcDest, wcDestLength, mbSource,
173		mbSourceLength, mbState, lengthOut);
174}
175
176
177status_t
178ICULocaleBackend::WcharToMultibyte(char* mbOut, wchar_t wc, mbstate_t* mbState,
179	size_t& lengthOut)
180{
181	ErrnoMaintainer errnoMaintainer;
182
183	return fCtypeData.WcharToMultibyte(mbOut, wc, mbState, lengthOut);
184}
185
186
187status_t
188ICULocaleBackend::WcharStringToMultibyte(char* mbDest, size_t mbDestLength,
189	const wchar_t** wcSource, size_t wcSourceLength, mbstate_t* mbState,
190	size_t& lengthOut)
191{
192	ErrnoMaintainer errnoMaintainer;
193
194	return fCtypeData.WcharStringToMultibyte(mbDest, mbDestLength, wcSource,
195		wcSourceLength, mbState, lengthOut);
196}
197
198
199const char*
200ICULocaleBackend::GetLanginfo(int index)
201{
202	ErrnoMaintainer errnoMaintainer;
203
204	switch(index) {
205		case CODESET:
206			return fCtypeData.GetLanginfo(index);
207
208		case D_T_FMT:
209		case D_FMT:
210		case T_FMT:
211		case T_FMT_AMPM:
212		case AM_STR:
213		case PM_STR:
214		case DAY_1:
215		case DAY_2:
216		case DAY_3:
217		case DAY_4:
218		case DAY_5:
219		case DAY_6:
220		case DAY_7:
221		case ABDAY_1:
222		case ABDAY_2:
223		case ABDAY_3:
224		case ABDAY_4:
225		case ABDAY_5:
226		case ABDAY_6:
227		case ABDAY_7:
228		case MON_1:
229		case MON_2:
230		case MON_3:
231		case MON_4:
232		case MON_5:
233		case MON_6:
234		case MON_7:
235		case MON_8:
236		case MON_9:
237		case MON_10:
238		case MON_11:
239		case MON_12:
240		case ABMON_1:
241		case ABMON_2:
242		case ABMON_3:
243		case ABMON_4:
244		case ABMON_5:
245		case ABMON_6:
246		case ABMON_7:
247		case ABMON_8:
248		case ABMON_9:
249		case ABMON_10:
250		case ABMON_11:
251		case ABMON_12:
252			return fTimeData.GetLanginfo(index);
253
254		case ERA:
255		case ERA_D_FMT:
256		case ERA_D_T_FMT:
257		case ERA_T_FMT:
258		case ALT_DIGITS:
259			return fPosixLanginfo[index];
260
261		case RADIXCHAR:
262		case THOUSEP:
263			return fNumericData.GetLanginfo(index);
264
265		case YESEXPR:
266		case NOEXPR:
267			return fMessagesData.GetLanginfo(index);
268
269		case CRNCYSTR:
270			return fMonetaryData.GetLanginfo(index);
271
272		default:
273			return "";
274	}
275}
276
277
278status_t
279ICULocaleBackend::Strcoll(const char* a, const char* b, int& result)
280{
281	ErrnoMaintainer errnoMaintainer;
282
283	return fCollateData.Strcoll(a, b, result);
284}
285
286
287status_t
288ICULocaleBackend::Strxfrm(char* out, const char* in, size_t size,
289	size_t& outSize)
290{
291	ErrnoMaintainer errnoMaintainer;
292
293	return fCollateData.Strxfrm(out, in, size, outSize);
294}
295
296
297status_t
298ICULocaleBackend::Wcscoll(const wchar_t* a, const wchar_t* b, int& result)
299{
300	ErrnoMaintainer errnoMaintainer;
301
302	return fCollateData.Wcscoll(a, b, result);
303}
304
305
306status_t
307ICULocaleBackend::Wcsxfrm(wchar_t* out, const wchar_t* in, size_t size,
308	size_t& outSize)
309{
310	ErrnoMaintainer errnoMaintainer;
311
312	return fCollateData.Wcsxfrm(out, in, size, outSize);
313}
314
315
316status_t
317ICULocaleBackend::TZSet(const char* timeZoneID, const char* tz)
318{
319	ErrnoMaintainer errnoMaintainer;
320
321	return fTimeConversion.TZSet(timeZoneID, tz);
322}
323
324
325status_t
326ICULocaleBackend::Localtime(const time_t* inTime, struct tm* tmOut)
327{
328	ErrnoMaintainer errnoMaintainer;
329
330	return fTimeConversion.Localtime(inTime, tmOut);
331}
332
333
334status_t
335ICULocaleBackend::Gmtime(const time_t* inTime, struct tm* tmOut)
336{
337	ErrnoMaintainer errnoMaintainer;
338
339	return fTimeConversion.Gmtime(inTime, tmOut);
340}
341
342
343status_t
344ICULocaleBackend::Mktime(struct tm* inOutTm, time_t& timeOut)
345{
346	ErrnoMaintainer errnoMaintainer;
347
348	return fTimeConversion.Mktime(inOutTm, timeOut);
349}
350
351
352const char*
353ICULocaleBackend::_QueryLocale(int category)
354{
355	switch (category) {
356		case LC_ALL:
357		{
358			// if all categories have the same locale, return that
359			const char* locale = fCollateData.PosixLocaleName();
360			if (strcmp(locale, fCtypeData.PosixLocaleName()) == 0
361				&& strcmp(locale, fMessagesData.PosixLocaleName()) == 0
362				&& strcmp(locale, fMonetaryData.PosixLocaleName()) == 0
363				&& strcmp(locale, fNumericData.PosixLocaleName()) == 0
364				&& strcmp(locale, fTimeData.PosixLocaleName()) == 0)
365				return locale;
366
367			// build a string with all info (at least glibc does it, too)
368			snprintf(fLocaleDescription, sizeof(fLocaleDescription),
369				"LC_COLLATE=%s;LC_CTYPE=%s;LC_MESSAGES=%s;LC_MONETARY=%s;"
370				"LC_NUMERIC=%s;LC_TIME=%s",
371				fCollateData.PosixLocaleName(), fCtypeData.PosixLocaleName(),
372				fMessagesData.PosixLocaleName(),
373				fMonetaryData.PosixLocaleName(), fNumericData.PosixLocaleName(),
374				fTimeData.PosixLocaleName());
375			return fLocaleDescription;
376		}
377		case LC_COLLATE:
378			return fCollateData.PosixLocaleName();
379		case LC_CTYPE:
380			return fCtypeData.PosixLocaleName();
381		case LC_MESSAGES:
382			return fMessagesData.PosixLocaleName();
383		case LC_MONETARY:
384			return fMonetaryData.PosixLocaleName();
385		case LC_NUMERIC:
386			return fNumericData.PosixLocaleName();
387		case LC_TIME:
388			return fTimeData.PosixLocaleName();
389		default:
390			// unsupported category
391			return NULL;
392	}
393}
394
395
396const char*
397ICULocaleBackend::_SetPosixLocale(int category)
398{
399	switch (category) {
400		case LC_ALL:
401			if (fCollateData.SetToPosix() != B_OK
402				|| fCtypeData.SetToPosix() != B_OK
403				|| fMessagesData.SetToPosix() != B_OK
404				|| fMonetaryData.SetToPosix() != B_OK
405				|| fNumericData.SetToPosix() != B_OK
406				|| fTimeData.SetToPosix() != B_OK)
407				return NULL;
408			break;
409		case LC_COLLATE:
410			if (fCollateData.SetToPosix() != B_OK)
411				return NULL;
412			break;
413		case LC_CTYPE:
414			if (fCtypeData.SetToPosix() != B_OK)
415				return NULL;
416			break;
417		case LC_MESSAGES:
418			if (fMessagesData.SetToPosix() != B_OK)
419				return NULL;
420			break;
421		case LC_MONETARY:
422			if (fMonetaryData.SetToPosix() != B_OK)
423				return NULL;
424			break;
425		case LC_NUMERIC:
426			if (fNumericData.SetToPosix() != B_OK)
427				return NULL;
428			break;
429		case LC_TIME:
430			if (fTimeData.SetToPosix() != B_OK)
431				return NULL;
432			break;
433		default:
434			// unsupported category
435			return NULL;
436	}
437
438	return "POSIX";
439}
440
441
442pthread_key_t
443ICULocaleBackend::_CreateThreadLocalStorageKey()
444{
445	pthread_key_t key;
446
447	pthread_key_create(&key, ICULocaleBackend::_DestroyThreadLocalStorageValue);
448
449	return key;
450}
451
452
453void ICULocaleBackend::_DestroyThreadLocalStorageValue(void* value)
454{
455	ICUThreadLocalStorageValue* tlsValue
456		= static_cast<ICUThreadLocalStorageValue*>(value);
457
458	delete tlsValue;
459}
460
461
462}	// namespace Libroot
463}	// namespace BPrivate
464