1/*
2 * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de
3 * Copyright 2010, Oliver Tappe, zooey@hirschkaefer.de
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7
8#include <ctype.h>
9#include <locale.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <ErrnoMaintainer.h>
14
15#include "LocaleBackend.h"
16
17
18using BPrivate::Libroot::gLocaleBackend;
19using BPrivate::Libroot::LocaleBackend;
20
21
22static status_t
23GetLocalesFromEnvironment(int category, const char** locales)
24{
25	const char* locale = getenv("LC_ALL");
26	if (locale && *locale)
27		locales[category] = locale;
28	else {
29		// the order of the names must match the one specified in locale.h
30		const char* categoryNames[] = {
31			"LC_ALL",
32			"LC_COLLATE",
33			"LC_CTYPE",
34			"LC_MONETARY",
35			"LC_NUMERIC",
36			"LC_TIME",
37			"LC_MESSAGES"
38		};
39		int from, to;
40		if (category == LC_ALL) {
41			// we need to check each real category if all of them should be set
42			from = 1;
43			to = LC_LAST;
44		} else
45			from = to = category;
46		bool haveDifferentLocales = false;
47		locale = NULL;
48		for (int lc = from; lc <= to; lc++) {
49			const char* lastLocale = locale;
50			locale = getenv(categoryNames[lc]);
51			if (!locale || *locale == '\0')
52				locale = getenv("LANG");
53			if (!locale || *locale == '\0')
54				locale = "POSIX";
55			locales[lc] = locale;
56			if (lastLocale && strcasecmp(locale, lastLocale) != 0)
57				haveDifferentLocales = true;
58		}
59		if (!haveDifferentLocales) {
60			// we can set all locales at once
61			locales[LC_ALL] = locale;
62		}
63	}
64
65	return B_OK;
66}
67
68
69extern "C" char*
70setlocale(int category, const char* locale)
71{
72	BPrivate::ErrnoMaintainer errnoMaintainer;
73
74	if (category < 0 || category > LC_LAST)
75		return NULL;
76
77	if (locale == NULL) {
78		// query the locale of the given category
79		if (gLocaleBackend != NULL)
80			return const_cast<char*>(gLocaleBackend->SetLocale(category, NULL));
81		else
82			return const_cast<char*>("POSIX");
83	}
84
85	// we may have to invoke SetLocale once for each category, so we use an
86	// array to collect the locale per category
87	const char* locales[LC_LAST + 1];
88	for (int lc = 0; lc <= LC_LAST; lc++)
89		locales[lc] = NULL;
90
91	if (*locale == '\0')
92		GetLocalesFromEnvironment(category, locales);
93	else
94		locales[category] = locale;
95
96	if (!gLocaleBackend) {
97		// for any locale other than POSIX/C, we try to activate the ICU
98		// backend
99		bool needBackend = false;
100		for (int lc = 0; lc <= LC_LAST; lc++) {
101			if (locales[lc] != NULL && strcasecmp(locales[lc], "POSIX") != 0
102					&& strcasecmp(locales[lc], "C") != 0) {
103				needBackend = true;
104				break;
105			}
106		}
107		if (needBackend && LocaleBackend::LoadBackend() != B_OK)
108			return NULL;
109	}
110
111	if (gLocaleBackend != NULL) {
112		for (int lc = 0; lc <= LC_LAST; lc++) {
113			if (locales[lc] != NULL) {
114				locale = gLocaleBackend->SetLocale(lc, locales[lc]);
115				if (lc == LC_ALL) {
116					// skip the rest, LC_ALL overrides
117					return const_cast<char*>(locale);
118				}
119			}
120		}
121		return const_cast<char*>(gLocaleBackend->SetLocale(category, NULL));
122	}
123
124	return const_cast<char*>("POSIX");
125}
126