xref: /haiku/src/system/libroot/posix/locale/setlocale.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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 
18 using BPrivate::Libroot::gLocaleBackend;
19 using BPrivate::Libroot::LocaleBackend;
20 
21 
22 static status_t
23 GetLocalesFromEnvironment(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 
69 extern "C" char*
70 setlocale(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