1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3//
4// Distributed under the Boost Software License, Version 1.0.
5// https://www.boost.org/LICENSE_1_0.txt
6
7#if defined(__FreeBSD__)
8# include <xlocale.h>
9#endif
10#include <boost/locale/encoding.hpp>
11#include <boost/locale/formatting.hpp>
12#include <boost/locale/generator.hpp>
13#include <boost/predef/os.h>
14#include <algorithm>
15#include <cctype>
16#include <cerrno>
17#include <cstdlib>
18#include <cstring>
19#include <ctime>
20#include <ios>
21#include <langinfo.h>
22#include <locale>
23#include <memory>
24#include <monetary.h>
25#include <sstream>
26#include <string>
27#include <vector>
28#include <wctype.h>
29
30#include "boost/locale/posix/all_generator.hpp"
31#include "boost/locale/util/numeric.hpp"
32
33namespace boost { namespace locale { namespace impl_posix {
34
35 template<typename CharType>
36 class num_format : public util::base_num_format<CharType> {
37 public:
38 typedef typename std::num_put<CharType>::iter_type iter_type;
39 typedef std::basic_string<CharType> string_type;
40
41 num_format(std::shared_ptr<locale_t> lc, size_t refs = 0) :
42 util::base_num_format<CharType>(refs), lc_(std::move(lc))
43 {}
44
45 protected:
46 iter_type do_format_currency(bool intl,
47 iter_type out,
48 std::ios_base& /*ios*/,
49 CharType /*fill*/,
50 long double val) const override
51 {
52 char buf[4] = {};
53 const char* format = intl ? "%i" : "%n";
54 errno = 0;
55 ssize_t n = strfmon_l(s: buf, maxsize: sizeof(buf), loc: *lc_, format: format, static_cast<double>(val));
56 if(n >= 0)
57 return write_it(out, buf, n);
58
59 for(std::vector<char> tmp(sizeof(buf) * 2); tmp.size() <= 4098; tmp.resize(new_size: tmp.size() * 2)) {
60 n = strfmon_l(s: tmp.data(), maxsize: tmp.size(), loc: *lc_, format: format, static_cast<double>(val));
61 if(n >= 0)
62 return write_it(out, tmp.data(), n);
63 }
64 return out;
65 }
66
67 std::ostreambuf_iterator<char> write_it(std::ostreambuf_iterator<char> out, const char* ptr, size_t n) const
68 {
69 return std::copy_n(first: ptr, n: n, result: out);
70 }
71
72 std::ostreambuf_iterator<wchar_t>
73 write_it(std::ostreambuf_iterator<wchar_t> out, const char* ptr, size_t n) const
74 {
75 const std::wstring tmp = conv::to_utf<wchar_t>(begin: ptr, end: ptr + n, charset: nl_langinfo_l(CODESET, l: *lc_));
76 return std::copy(first: tmp.begin(), last: tmp.end(), result: out);
77 }
78
79 private:
80 std::shared_ptr<locale_t> lc_;
81
82 }; // num_format
83
84 namespace {
85 std::string do_ftime(const char* format, const struct tm* t, locale_t lc)
86 {
87 char buf[16];
88 size_t n = strftime_l(s: buf, maxsize: sizeof(buf), format: format, tp: t, loc: lc);
89 if(n == 0) {
90 // Note standard specifies that in case of error the function returns 0,
91 // however 0 may be actually valid output value of for example empty format
92 // or an output of %p in some locales
93 //
94 // Thus we try to guess that 1024 would be enough.
95 std::vector<char> v(1024);
96 n = strftime_l(s: v.data(), maxsize: 1024, format: format, tp: t, loc: lc);
97 return std::string(v.data(), n);
98 }
99 return std::string(buf, n);
100 }
101 template<typename CharType>
102 std::basic_string<CharType> do_ftime(const CharType* format, const struct tm* t, locale_t lc)
103 {
104 const std::string encoding = nl_langinfo_l(CODESET, l: lc);
105 const std::string nformat = conv::from_utf(format, encoding);
106 const std::string nres = do_ftime(format: nformat.c_str(), t, lc);
107 return conv::to_utf<CharType>(nres, encoding);
108 }
109 } // namespace
110
111 template<typename CharType>
112 class time_put_posix : public std::time_put<CharType> {
113 public:
114 time_put_posix(std::shared_ptr<locale_t> lc, size_t refs = 0) :
115 std::time_put<CharType>(refs), lc_(std::move(lc))
116 {}
117 typedef typename std::time_put<CharType>::iter_type iter_type;
118 typedef std::basic_string<CharType> string_type;
119
120 iter_type do_put(iter_type out,
121 std::ios_base& /*ios*/,
122 CharType /*fill*/,
123 const std::tm* tm,
124 char format,
125 char modifier) const override
126 {
127 CharType fmt[4] = {'%',
128 static_cast<CharType>(modifier != 0 ? modifier : format),
129 static_cast<CharType>(modifier == 0 ? '\0' : format)};
130 string_type res = do_ftime(fmt, tm, *lc_);
131 return std::copy(res.begin(), res.end(), out);
132 }
133
134 private:
135 std::shared_ptr<locale_t> lc_;
136 };
137
138 template<typename CharType>
139 class ctype_posix;
140
141 template<>
142 class ctype_posix<char> : public std::ctype<char> {
143 public:
144 ctype_posix(std::shared_ptr<locale_t> lc) : lc_(std::move(lc)) {}
145
146 bool do_is(mask m, char c) const
147 {
148 if((m & space) && isspace_l(c, *lc_))
149 return true;
150 if((m & print) && isprint_l(c, *lc_))
151 return true;
152 if((m & cntrl) && iscntrl_l(c, *lc_))
153 return true;
154 if((m & upper) && isupper_l(c, *lc_))
155 return true;
156 if((m & lower) && islower_l(c, *lc_))
157 return true;
158 if((m & alpha) && isalpha_l(c, *lc_))
159 return true;
160 if((m & digit) && isdigit_l(c, *lc_))
161 return true;
162 if((m & xdigit) && isxdigit_l(c, *lc_))
163 return true;
164 if((m & punct) && ispunct_l(c, *lc_))
165 return true;
166 return false;
167 }
168 const char* do_is(const char* begin, const char* end, mask* m) const
169 {
170 while(begin != end) {
171 char c = *begin++;
172 int r = 0;
173 if(isspace_l(c, *lc_))
174 r |= space;
175 if(isprint_l(c, *lc_))
176 r |= cntrl;
177 if(iscntrl_l(c, *lc_))
178 r |= space;
179 if(isupper_l(c, *lc_))
180 r |= upper;
181 if(islower_l(c, *lc_))
182 r |= lower;
183 if(isalpha_l(c, *lc_))
184 r |= alpha;
185 if(isdigit_l(c, *lc_))
186 r |= digit;
187 if(isxdigit_l(c, *lc_))
188 r |= xdigit;
189 if(ispunct_l(c, *lc_))
190 r |= punct;
191 // r actually should be mask, but some standard
192 // libraries (like STLPort)
193 // do not define operator | properly so using int+cast
194 *m++ = static_cast<mask>(r);
195 }
196 return begin;
197 }
198 const char* do_scan_is(mask m, const char* begin, const char* end) const
199 {
200 while(begin != end)
201 if(do_is(m, c: *begin))
202 return begin;
203 return begin;
204 }
205 const char* do_scan_not(mask m, const char* begin, const char* end) const
206 {
207 while(begin != end)
208 if(!do_is(m, c: *begin))
209 return begin;
210 return begin;
211 }
212 char toupper(char c) const { return toupper_l(c: c, l: *lc_); }
213 const char* toupper(char* begin, const char* end) const
214 {
215 for(; begin != end; begin++)
216 *begin = toupper_l(c: *begin, l: *lc_);
217 return begin;
218 }
219 char tolower(char c) const { return tolower_l(c: c, l: *lc_); }
220 const char* tolower(char* begin, const char* end) const
221 {
222 for(; begin != end; begin++)
223 *begin = tolower_l(c: *begin, l: *lc_);
224 return begin;
225 }
226
227 private:
228 std::shared_ptr<locale_t> lc_;
229 };
230
231 template<>
232 class ctype_posix<wchar_t> : public std::ctype<wchar_t> {
233 public:
234 ctype_posix(std::shared_ptr<locale_t> lc) : lc_(std::move(lc)) {}
235
236 bool do_is(mask m, wchar_t c) const
237 {
238 if((m & space) && iswspace_l(wc: c, locale: *lc_))
239 return true;
240 if((m & print) && iswprint_l(wc: c, locale: *lc_))
241 return true;
242 if((m & cntrl) && iswcntrl_l(wc: c, locale: *lc_))
243 return true;
244 if((m & upper) && iswupper_l(wc: c, locale: *lc_))
245 return true;
246 if((m & lower) && iswlower_l(wc: c, locale: *lc_))
247 return true;
248 if((m & alpha) && iswalpha_l(wc: c, locale: *lc_))
249 return true;
250 if((m & digit) && iswdigit_l(wc: c, locale: *lc_))
251 return true;
252 if((m & xdigit) && iswxdigit_l(wc: c, locale: *lc_))
253 return true;
254 if((m & punct) && iswpunct_l(wc: c, locale: *lc_))
255 return true;
256 return false;
257 }
258 const wchar_t* do_is(const wchar_t* begin, const wchar_t* end, mask* m) const
259 {
260 while(begin != end) {
261 wchar_t c = *begin++;
262 int r = 0;
263 if(iswspace_l(wc: c, locale: *lc_))
264 r |= space;
265 if(iswprint_l(wc: c, locale: *lc_))
266 r |= cntrl;
267 if(iswcntrl_l(wc: c, locale: *lc_))
268 r |= space;
269 if(iswupper_l(wc: c, locale: *lc_))
270 r |= upper;
271 if(iswlower_l(wc: c, locale: *lc_))
272 r |= lower;
273 if(iswalpha_l(wc: c, locale: *lc_))
274 r |= alpha;
275 if(iswdigit_l(wc: c, locale: *lc_))
276 r |= digit;
277 if(iswxdigit_l(wc: c, locale: *lc_))
278 r |= xdigit;
279 if(iswpunct_l(wc: c, locale: *lc_))
280 r |= punct;
281 // r actually should be mask, but some standard
282 // libraries (like STLPort)
283 // do not define operator | properly so using int+cast
284 *m++ = static_cast<mask>(r);
285 }
286 return begin;
287 }
288 const wchar_t* do_scan_is(mask m, const wchar_t* begin, const wchar_t* end) const
289 {
290 while(begin != end)
291 if(do_is(m, c: *begin))
292 return begin;
293 return begin;
294 }
295 const wchar_t* do_scan_not(mask m, const wchar_t* begin, const wchar_t* end) const
296 {
297 while(begin != end)
298 if(!do_is(m, c: *begin))
299 return begin;
300 return begin;
301 }
302 wchar_t toupper(wchar_t c) const { return towupper_l(wc: c, locale: *lc_); }
303 const wchar_t* toupper(wchar_t* begin, const wchar_t* end) const
304 {
305 for(; begin != end; begin++)
306 *begin = towupper_l(wc: *begin, locale: *lc_);
307 return begin;
308 }
309 wchar_t tolower(wchar_t c) const { return tolower_l(c: c, l: *lc_); }
310 const wchar_t* tolower(wchar_t* begin, const wchar_t* end) const
311 {
312 for(; begin != end; begin++)
313 *begin = tolower_l(c: *begin, l: *lc_);
314 return begin;
315 }
316
317 private:
318 std::shared_ptr<locale_t> lc_;
319 };
320
321 struct basic_numpunct {
322 std::string grouping;
323 std::string thousands_sep;
324 std::string decimal_point;
325 basic_numpunct() : decimal_point(".") {}
326 basic_numpunct(locale_t lc)
327 {
328#if defined(__APPLE__) || defined(__FreeBSD__)
329 lconv* cv = localeconv_l(lc);
330 grouping = cv->grouping;
331 thousands_sep = cv->thousands_sep;
332 decimal_point = cv->decimal_point;
333#else
334 thousands_sep = nl_langinfo_l(THOUSEP, l: lc);
335 decimal_point = nl_langinfo_l(RADIXCHAR, l: lc);
336# ifdef GROUPING
337 grouping = nl_langinfo_l(GROUPING, l: lc);
338# endif
339#endif
340 }
341 };
342
343 template<typename CharType>
344 class num_punct_posix : public std::numpunct<CharType> {
345 public:
346 typedef std::basic_string<CharType> string_type;
347 num_punct_posix(locale_t lc, size_t refs = 0) : std::numpunct<CharType>(refs)
348 {
349 basic_numpunct np(lc);
350 to_str(np.thousands_sep, thousands_sep_, lc);
351 to_str(np.decimal_point, decimal_point_, lc);
352 grouping_ = np.grouping;
353 if(thousands_sep_.size() > 1)
354 grouping_ = std::string();
355 if(decimal_point_.size() > 1)
356 decimal_point_ = CharType('.');
357 }
358 void to_str(std::string& s1, std::string& s2, locale_t /*lc*/) { s2.swap(s&: s1); }
359 void to_str(std::string& s1, std::wstring& s2, locale_t lc)
360 {
361 s2 = conv::to_utf<wchar_t>(text: s1, charset: nl_langinfo_l(CODESET, l: lc));
362 }
363 CharType do_decimal_point() const override { return *decimal_point_.c_str(); }
364 CharType do_thousands_sep() const override { return *thousands_sep_.c_str(); }
365 std::string do_grouping() const override { return grouping_; }
366 string_type do_truename() const override
367 {
368 static const char t[] = "true";
369 return string_type(t, t + sizeof(t) - 1);
370 }
371 string_type do_falsename() const override
372 {
373 static const char t[] = "false";
374 return string_type(t, t + sizeof(t) - 1);
375 }
376
377 private:
378 string_type decimal_point_;
379 string_type thousands_sep_;
380 std::string grouping_;
381 };
382
383 template<typename CharType>
384 std::locale create_formatting_impl(const std::locale& in, std::shared_ptr<locale_t> lc)
385 {
386 std::locale tmp = std::locale(in, new num_punct_posix<CharType>(*lc));
387 tmp = std::locale(tmp, new ctype_posix<CharType>(lc));
388 tmp = std::locale(tmp, new time_put_posix<CharType>(lc));
389 tmp = std::locale(tmp, new num_format<CharType>(std::move(lc)));
390 return tmp;
391 }
392
393 template<typename CharType>
394 std::locale create_parsing_impl(const std::locale& in, std::shared_ptr<locale_t> lc)
395 {
396 std::locale tmp = std::locale(in, new num_punct_posix<CharType>(*lc));
397 tmp = std::locale(tmp, new ctype_posix<CharType>(std::move(lc)));
398 tmp = std::locale(tmp, new util::base_num_parse<CharType>());
399 return tmp;
400 }
401
402 std::locale create_formatting(const std::locale& in, std::shared_ptr<locale_t> lc, char_facet_t type)
403 {
404 switch(type) {
405 case char_facet_t::nochar: break;
406 case char_facet_t::char_f: return create_formatting_impl<char>(in, lc: std::move(lc));
407 case char_facet_t::wchar_f: return create_formatting_impl<wchar_t>(in, lc: std::move(lc));
408#ifdef __cpp_char8_t
409 case char_facet_t::char8_f: break; // std-facet not available (yet)
410#endif
411#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
412 case char_facet_t::char16_f: return create_formatting_impl<char16_t>(in, lc);
413#endif
414#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
415 case char_facet_t::char32_f: return create_formatting_impl<char32_t>(in, lc);
416#endif
417 }
418 return in;
419 }
420
421 std::locale create_parsing(const std::locale& in, std::shared_ptr<locale_t> lc, char_facet_t type)
422 {
423 switch(type) {
424 case char_facet_t::nochar: break;
425 case char_facet_t::char_f: return create_parsing_impl<char>(in, lc: std::move(lc));
426 case char_facet_t::wchar_f: return create_parsing_impl<wchar_t>(in, lc: std::move(lc));
427#ifdef __cpp_char8_t
428 case char_facet_t::char8_f: break; // std-facet not available (yet)
429#endif
430#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
431 case char_facet_t::char16_f: return create_parsing_impl<char16_t>(in, lc);
432#endif
433#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
434 case char_facet_t::char32_f: return create_parsing_impl<char32_t>(in, lc);
435#endif
436 }
437 return in;
438 }
439
440}}} // namespace boost::locale::impl_posix
441

source code of boost/libs/locale/src/boost/locale/posix/numeric.cpp