1// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2// 2006, 2007, 2008, 2009
3// Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library.  This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23// <http://www.gnu.org/licenses/>.
24
25#include <clocale>
26#include <cstring>
27#include <cstdlib>
28#include <locale>
29
30_GLIBCXX_BEGIN_NAMESPACE(std)
31
32  using namespace __gnu_cxx;
33
34  locale::locale(const char* __s) : _M_impl(0)
35  {
36    if (__s)
37      {
38	_S_initialize();
39	if (std::strcmp(__s, "C") == 0 || std::strcmp(__s, "POSIX") == 0)
40	  (_M_impl = _S_classic)->_M_add_reference();
41	else if (std::strcmp(__s, "") != 0)
42	  _M_impl = new _Impl(__s, 1);
43	else
44	  {
45	    // Get it from the environment.
46	    char* __env = std::getenv("LC_ALL");
47	    // If LC_ALL is set we are done.
48	    if (__env && std::strcmp(__env, "") != 0)
49	      {
50		if (std::strcmp(__env, "C") == 0
51		    || std::strcmp(__env, "POSIX") == 0)
52		  (_M_impl = _S_classic)->_M_add_reference();
53		else
54		  _M_impl = new _Impl(__env, 1);
55	      }
56	    else
57	      {
58		// LANG may set a default different from "C".
59		string __lang;
60		__env = std::getenv("LANG");
61		if (!__env || std::strcmp(__env, "") == 0
62		    || std::strcmp(__env, "C") == 0
63		    || std::strcmp(__env, "POSIX") == 0)
64		  __lang = "C";
65		else
66		  __lang = __env;
67
68		// Scan the categories looking for the first one
69		// different from LANG.
70		size_t __i = 0;
71		if (__lang == "C")
72		  for (; __i < _S_categories_size; ++__i)
73		    {
74		      __env = std::getenv(_S_categories[__i]);
75		      if (__env && std::strcmp(__env, "") != 0
76			  && std::strcmp(__env, "C") != 0
77			  && std::strcmp(__env, "POSIX") != 0)
78			break;
79		    }
80		else
81		  for (; __i < _S_categories_size; ++__i)
82		    {
83		      __env = std::getenv(_S_categories[__i]);
84		      if (__env && std::strcmp(__env, "") != 0
85			  && __lang != __env)
86			break;
87		    }
88
89		// If one is found, build the complete string of
90		// the form LC_CTYPE=xxx;LC_NUMERIC=yyy; and so on...
91		if (__i < _S_categories_size)
92		  {
93		    string __str;
94		    __str.reserve(128);
95		    for (size_t __j = 0; __j < __i; ++__j)
96		      {
97			__str += _S_categories[__j];
98			__str += '=';
99			__str += __lang;
100			__str += ';';
101		      }
102		    __str += _S_categories[__i];
103		    __str += '=';
104		    __str += __env;
105		    __str += ';';
106		    ++__i;
107		    for (; __i < _S_categories_size; ++__i)
108		      {
109			__env = std::getenv(_S_categories[__i]);
110			__str += _S_categories[__i];
111			if (!__env || std::strcmp(__env, "") == 0)
112			  {
113			    __str += '=';
114			    __str += __lang;
115			    __str += ';';
116			  }
117			else if (std::strcmp(__env, "C") == 0
118				 || std::strcmp(__env, "POSIX") == 0)
119			  __str += "=C;";
120			else
121			  {
122			    __str += '=';
123			    __str += __env;
124			    __str += ';';
125			  }
126		      }
127		    __str.erase(__str.end() - 1);
128		    _M_impl = new _Impl(__str.c_str(), 1);
129		  }
130		// ... otherwise either an additional instance of
131		// the "C" locale or LANG.
132		else if (__lang == "C")
133		  (_M_impl = _S_classic)->_M_add_reference();
134		else
135		  _M_impl = new _Impl(__lang.c_str(), 1);
136	      }
137	  }
138      }
139    else
140      __throw_runtime_error(__N("locale::locale NULL not valid"));
141  }
142
143  locale::locale(const locale& __base, const char* __s, category __cat)
144  : _M_impl(0)
145  {
146    // NB: There are complicated, yet more efficient ways to do
147    // this. Building up locales on a per-category way is tedious, so
148    // let's do it this way until people complain.
149    locale __add(__s);
150    _M_coalesce(__base, __add, __cat);
151  }
152
153  locale::locale(const locale& __base, const locale& __add, category __cat)
154  : _M_impl(0)
155  { _M_coalesce(__base, __add, __cat); }
156
157  void
158  locale::_M_coalesce(const locale& __base, const locale& __add,
159		      category __cat)
160  {
161    __cat = _S_normalize_category(__cat);
162    _M_impl = new _Impl(*__base._M_impl, 1);
163
164    __try
165      { _M_impl->_M_replace_categories(__add._M_impl, __cat); }
166    __catch(...)
167      {
168	_M_impl->_M_remove_reference();
169	__throw_exception_again;
170      }
171  }
172
173  // Construct named _Impl.
174  locale::_Impl::
175  _Impl(const char* __s, size_t __refs)
176  : _M_refcount(__refs), _M_facets(0), _M_facets_size(_GLIBCXX_NUM_FACETS),
177    _M_caches(0), _M_names(0)
178  {
179    // Initialize the underlying locale model, which also checks to
180    // see if the given name is valid.
181    __c_locale __cloc;
182    locale::facet::_S_create_c_locale(__cloc, __s);
183    __c_locale __clocm = __cloc;
184
185    __try
186      {
187	_M_facets = new const facet*[_M_facets_size];
188	for (size_t __i = 0; __i < _M_facets_size; ++__i)
189	  _M_facets[__i] = 0;
190	_M_caches = new const facet*[_M_facets_size];
191	for (size_t __j = 0; __j < _M_facets_size; ++__j)
192	  _M_caches[__j] = 0;
193	_M_names = new char*[_S_categories_size];
194	for (size_t __k = 0; __k < _S_categories_size; ++__k)
195	  _M_names[__k] = 0;
196
197	// Name the categories.
198	const char* __smon = __s;
199	const size_t __len = std::strlen(__s);
200	if (!std::memchr(__s, ';', __len))
201	  {
202	    _M_names[0] = new char[__len + 1];
203	    std::memcpy(_M_names[0], __s, __len + 1);
204	  }
205	else
206	  {
207	    const char* __end = __s;
208	    bool __found_ctype = false;
209	    bool __found_monetary = false;
210	    size_t __ci = 0, __mi = 0;
211	    for (size_t __i = 0; __i < _S_categories_size; ++__i)
212	      {
213		const char* __beg = std::strchr(__end + 1, '=') + 1;
214		__end = std::strchr(__beg, ';');
215		if (!__end)
216		  __end = __s + __len;
217		_M_names[__i] = new char[__end - __beg + 1];
218		std::memcpy(_M_names[__i], __beg, __end - __beg);
219		_M_names[__i][__end - __beg] = '\0';
220		if (!__found_ctype
221		    && *(__beg - 2) == 'E' && *(__beg - 3) == 'P')
222		  {
223		    __found_ctype = true;
224		    __ci = __i;
225		  }
226		else if (!__found_monetary && *(__beg - 2) == 'Y')
227		  {
228		    __found_monetary = true;
229		    __mi = __i;
230		  }
231	      }
232
233	    if (std::strcmp(_M_names[__ci], _M_names[__mi]))
234	      {
235		__smon = _M_names[__mi];
236		__clocm = locale::facet::_S_lc_ctype_c_locale(__cloc,
237							      __smon);
238	      }
239	  }
240
241	// Construct all standard facets and add them to _M_facets.
242	_M_init_facet(new std::ctype<char>(__cloc, 0, false));
243	_M_init_facet(new codecvt<char, char, mbstate_t>(__cloc));
244	_M_init_facet(new numpunct<char>(__cloc));
245	_M_init_facet(new num_get<char>);
246	_M_init_facet(new num_put<char>);
247	_M_init_facet(new std::collate<char>(__cloc));
248	_M_init_facet(new moneypunct<char, false>(__cloc, 0));
249	_M_init_facet(new moneypunct<char, true>(__cloc, 0));
250	_M_init_facet(new money_get<char>);
251	_M_init_facet(new money_put<char>);
252	_M_init_facet(new __timepunct<char>(__cloc, __s));
253	_M_init_facet(new time_get<char>);
254	_M_init_facet(new time_put<char>);
255	_M_init_facet(new std::messages<char>(__cloc, __s));
256
257#ifdef  _GLIBCXX_USE_WCHAR_T
258	_M_init_facet(new std::ctype<wchar_t>(__cloc));
259	_M_init_facet(new codecvt<wchar_t, char, mbstate_t>(__cloc));
260	_M_init_facet(new numpunct<wchar_t>(__cloc));
261	_M_init_facet(new num_get<wchar_t>);
262	_M_init_facet(new num_put<wchar_t>);
263	_M_init_facet(new std::collate<wchar_t>(__cloc));
264	_M_init_facet(new moneypunct<wchar_t, false>(__clocm, __smon));
265	_M_init_facet(new moneypunct<wchar_t, true>(__clocm, __smon));
266	_M_init_facet(new money_get<wchar_t>);
267	_M_init_facet(new money_put<wchar_t>);
268	_M_init_facet(new __timepunct<wchar_t>(__cloc, __s));
269	_M_init_facet(new time_get<wchar_t>);
270	_M_init_facet(new time_put<wchar_t>);
271	_M_init_facet(new std::messages<wchar_t>(__cloc, __s));
272#endif
273	locale::facet::_S_destroy_c_locale(__cloc);
274	if (__clocm != __cloc)
275	  locale::facet::_S_destroy_c_locale(__clocm);
276      }
277    __catch(...)
278      {
279	locale::facet::_S_destroy_c_locale(__cloc);
280	if (__clocm != __cloc)
281	  locale::facet::_S_destroy_c_locale(__clocm);
282	this->~_Impl();
283	__throw_exception_again;
284      }
285  }
286
287  void
288  locale::_Impl::
289  _M_replace_categories(const _Impl* __imp, category __cat)
290  {
291    category __mask = 1;
292    if (!_M_names[0] || !__imp->_M_names[0])
293      {
294	if (_M_names[0])
295	  {
296	    delete [] _M_names[0];
297	    _M_names[0] = 0;   // Unnamed.
298	  }
299
300	for (size_t __ix = 0; __ix < _S_categories_size; ++__ix, __mask <<= 1)
301	  {
302	    if (__mask & __cat)
303	      // Need to replace entry in _M_facets with other locale's info.
304	      _M_replace_category(__imp, _S_facet_categories[__ix]);
305	  }
306      }
307    else
308      {
309	if (!_M_names[1])
310	  {
311	    // A full set of _M_names must be prepared, all identical
312	    // to _M_names[0] to begin with. Then, below, a few will
313	    // be replaced by the corresponding __imp->_M_names. I.e.,
314	    // not a "simple" locale anymore (see locale::operator==).
315	    const size_t __len = std::strlen(_M_names[0]) + 1;
316	    for (size_t __i = 1; __i < _S_categories_size; ++__i)
317	      {
318		_M_names[__i] = new char[__len];
319		std::memcpy(_M_names[__i], _M_names[0], __len);
320	      }
321	  }
322
323	for (size_t __ix = 0; __ix < _S_categories_size; ++__ix, __mask <<= 1)
324	  {
325	    if (__mask & __cat)
326	      {
327		// Need to replace entry in _M_facets with other locale's info.
328		_M_replace_category(__imp, _S_facet_categories[__ix]);
329
330		// FIXME: Hack for libstdc++/29217: the numerical encodings
331		// of the time and collate categories are swapped vs the
332		// order of the names in locale::_S_categories.  We'd like to
333		// adjust the former (the latter is dictated by compatibility
334		// with glibc) but we can't for binary compatibility.
335		size_t __ix_name = __ix;
336		if (__ix == 2 || __ix == 3)
337		  __ix_name = 5 - __ix;
338
339		char* __src = __imp->_M_names[__ix_name] ?
340		              __imp->_M_names[__ix_name] : __imp->_M_names[0];
341		const size_t __len = std::strlen(__src) + 1;
342		char* __new = new char[__len];
343		std::memcpy(__new, __src, __len);
344		delete [] _M_names[__ix_name];
345		_M_names[__ix_name] = __new;
346	      }
347	  }
348      }
349  }
350
351_GLIBCXX_END_NAMESPACE
352