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#ifndef BOOST_LOCALE_IMPL_UTIL_NUMERIC_HPP
8#define BOOST_LOCALE_IMPL_UTIL_NUMERIC_HPP
9#include <boost/locale/formatting.hpp>
10#include <boost/locale/info.hpp>
11#include <boost/predef/os.h>
12#include <algorithm>
13#include <cerrno>
14#include <cstdlib>
15#include <ctime>
16#include <ios>
17#include <limits>
18#include <locale>
19#include <sstream>
20#include <string>
21#include <vector>
22
23#include "timezone.hpp"
24
25namespace boost { namespace locale { namespace util {
26
27 inline bool try_to_int(const std::string& s, int& res)
28 {
29 if(s.empty())
30 return false;
31 errno = 0;
32 char* end_char{};
33 const auto v = std::strtol(nptr: s.c_str(), endptr: &end_char, base: 10);
34 if(errno == ERANGE || end_char != s.c_str() + s.size())
35 return false;
36 if(v < std::numeric_limits<int>::min() || v > std::numeric_limits<int>::max())
37 return false;
38 res = v;
39 return true;
40 }
41
42 template<typename CharType>
43 struct formatting_size_traits {
44 static size_t size(const std::basic_string<CharType>& s, const std::locale& /*l*/) { return s.size(); }
45 };
46
47 template<>
48 struct formatting_size_traits<char> {
49 static size_t size(const std::string& s, const std::locale& l)
50 {
51 if(!std::has_facet<info>(loc: l))
52 return s.size();
53 if(!std::use_facet<info>(loc: l).utf8())
54 return s.size();
55 // count code points, poor man's text size
56 return std::count_if(first: s.begin(), last: s.end(), pred: [](const unsigned char c) {
57 return (c <= 127) // ASCII
58 || ((c & 0xC0) == 0xC0); // first UTF-8 byte
59 });
60 }
61 };
62
63 template<typename CharType>
64 class base_num_format : public std::num_put<CharType> {
65 public:
66 typedef typename std::num_put<CharType>::iter_type iter_type;
67 typedef std::basic_string<CharType> string_type;
68
69 base_num_format(size_t refs = 0) : std::num_put<CharType>(refs) {}
70
71 protected:
72 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long val) const override
73 {
74 return do_real_put(out, ios, fill, val);
75 }
76 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, unsigned long val) const override
77 {
78 return do_real_put(out, ios, fill, val);
79 }
80 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, double val) const override
81 {
82 return do_real_put(out, ios, fill, val);
83 }
84 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long double val) const override
85 {
86 return do_real_put(out, ios, fill, val);
87 }
88
89 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long long val) const override
90 {
91 return do_real_put(out, ios, fill, val);
92 }
93 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, unsigned long long val) const override
94 {
95 return do_real_put(out, ios, fill, val);
96 }
97
98 private:
99 template<typename ValueType>
100 iter_type do_real_put(iter_type out, std::ios_base& ios, CharType fill, ValueType val) const
101 {
102 typedef std::num_put<CharType> super;
103
104 ios_info& info = ios_info::get(ios);
105
106 switch(info.display_flags()) {
107 case flags::posix: {
108 typedef std::basic_ostringstream<CharType> sstream_type;
109 sstream_type ss;
110 ss.imbue(std::locale::classic());
111 ss.flags(ios.flags());
112 ss.precision(ios.precision());
113 ss.width(ios.width());
114 iter_type ret_ptr = super::do_put(out, ss, fill, val);
115 ios.width(wide: 0);
116 return ret_ptr;
117 }
118 case flags::date: return format_time(out, ios, fill, static_cast<std::time_t>(val), 'x');
119 case flags::time: return format_time(out, ios, fill, static_cast<std::time_t>(val), 'X');
120 case flags::datetime: return format_time(out, ios, fill, static_cast<std::time_t>(val), 'c');
121 case flags::strftime:
122 return format_time(out,
123 ios,
124 fill,
125 static_cast<std::time_t>(val),
126 info.date_time_pattern<CharType>());
127 case flags::currency: {
128 bool nat = info.currency_flags() == flags::currency_default
129 || info.currency_flags() == flags::currency_national;
130 bool intl = !nat;
131 return do_format_currency(intl, out, ios, fill, val: static_cast<long double>(val));
132 }
133
134 case flags::number:
135 case flags::percent:
136 case flags::spellout:
137 case flags::ordinal: break;
138 }
139 return super::do_put(out, ios, fill, val);
140 }
141
142 virtual iter_type
143 do_format_currency(bool intl, iter_type out, std::ios_base& ios, CharType fill, long double val) const
144 {
145 if(intl)
146 return format_currency<true>(out, ios, fill, val);
147 else
148 return format_currency<false>(out, ios, fill, val);
149 }
150
151 template<bool intl>
152 iter_type format_currency(iter_type out, std::ios_base& ios, CharType fill, long double val) const
153 {
154 std::locale loc = ios.getloc();
155 int digits = std::use_facet<std::moneypunct<CharType, intl>>(loc).frac_digits();
156 while(digits > 0) {
157 val *= 10;
158 digits--;
159 }
160 std::ios_base::fmtflags f = ios.flags();
161 ios.flags(fmtfl: f | std::ios_base::showbase);
162 out = std::use_facet<std::money_put<CharType>>(loc).put(out, intl, ios, fill, val);
163 ios.flags(fmtfl: f);
164 return out;
165 }
166
167 iter_type format_time(iter_type out, std::ios_base& ios, CharType fill, std::time_t time, char c) const
168 {
169 string_type fmt;
170 fmt += CharType('%');
171 fmt += CharType(c);
172 return format_time(out, ios, fill, time, fmt);
173 }
174
175 iter_type
176 format_time(iter_type out, std::ios_base& ios, CharType fill, std::time_t time, const string_type& format) const
177 {
178 std::string tz = ios_info::get(ios).time_zone();
179 std::tm tm;
180#if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__)
181 std::vector<char> tmp_buf(tz.c_str(), tz.c_str() + tz.size() + 1);
182#endif
183 if(tz.empty()) {
184#ifdef BOOST_WINDOWS
185 // Windows uses TLS
186 tm = *localtime(&time);
187#else
188 localtime_r(timer: &time, tp: &tm);
189#endif
190 } else {
191 int gmtoff = parse_tz(tz);
192 time += gmtoff;
193#ifdef BOOST_WINDOWS
194 // Windows uses TLS
195 tm = *gmtime(&time);
196#else
197 gmtime_r(timer: &time, tp: &tm);
198#endif
199
200#if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__)
201 // These have extra fields to specify timezone
202 if(gmtoff != 0) {
203 // bsd and apple want tm_zone be non-const
204 tm.tm_zone = tmp_buf.data();
205 tm.tm_gmtoff = gmtoff;
206 }
207#endif
208 }
209 std::basic_ostringstream<CharType> tmp_out;
210 std::use_facet<std::time_put<CharType>>(ios.getloc())
211 .put(tmp_out, tmp_out, fill, &tm, format.c_str(), format.c_str() + format.size());
212 string_type str = tmp_out.str();
213 std::streamsize on_left = 0, on_right = 0;
214 std::streamsize points = formatting_size_traits<CharType>::size(str, ios.getloc());
215 if(points < ios.width()) {
216 std::streamsize n = ios.width() - points;
217
218 std::ios_base::fmtflags flags = ios.flags() & std::ios_base::adjustfield;
219
220 // we do not really know internal point, so we assume that it does not
221 // exist. so according to the standard field should be right aligned
222 if(flags != std::ios_base::left)
223 on_left = n;
224 on_right = n - on_left;
225 }
226 while(on_left > 0) {
227 *out++ = fill;
228 on_left--;
229 }
230 std::copy(str.begin(), str.end(), out);
231 while(on_right > 0) {
232 *out++ = fill;
233 on_right--;
234 }
235 ios.width(wide: 0);
236 return out;
237 }
238
239 }; // num_format
240
241 template<typename CharType>
242 class base_num_parse : public std::num_get<CharType> {
243 public:
244 base_num_parse(size_t refs = 0) : std::num_get<CharType>(refs) {}
245
246 protected:
247 typedef typename std::num_get<CharType>::iter_type iter_type;
248 typedef std::basic_string<CharType> string_type;
249
250 iter_type
251 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, long& val) const override
252 {
253 return do_real_get(in, end, ios, err, val);
254 }
255
256 iter_type do_get(iter_type in,
257 iter_type end,
258 std::ios_base& ios,
259 std::ios_base::iostate& err,
260 unsigned short& val) const override
261 {
262 return do_real_get(in, end, ios, err, val);
263 }
264
265 iter_type do_get(iter_type in,
266 iter_type end,
267 std::ios_base& ios,
268 std::ios_base::iostate& err,
269 unsigned int& val) const override
270 {
271 return do_real_get(in, end, ios, err, val);
272 }
273
274 iter_type do_get(iter_type in,
275 iter_type end,
276 std::ios_base& ios,
277 std::ios_base::iostate& err,
278 unsigned long& val) const override
279 {
280 return do_real_get(in, end, ios, err, val);
281 }
282
283 iter_type
284 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, float& val) const override
285 {
286 return do_real_get(in, end, ios, err, val);
287 }
288
289 iter_type
290 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, double& val) const override
291 {
292 return do_real_get(in, end, ios, err, val);
293 }
294
295 iter_type do_get(iter_type in,
296 iter_type end,
297 std::ios_base& ios,
298 std::ios_base::iostate& err,
299 long double& val) const override
300 {
301 return do_real_get(in, end, ios, err, val);
302 }
303
304 iter_type do_get(iter_type in,
305 iter_type end,
306 std::ios_base& ios,
307 std::ios_base::iostate& err,
308 long long& val) const override
309 {
310 return do_real_get(in, end, ios, err, val);
311 }
312
313 iter_type do_get(iter_type in,
314 iter_type end,
315 std::ios_base& ios,
316 std::ios_base::iostate& err,
317 unsigned long long& val) const override
318 {
319 return do_real_get(in, end, ios, err, val);
320 }
321
322 private:
323 template<typename ValueType>
324 iter_type
325 do_real_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, ValueType& val) const
326 {
327 typedef std::num_get<CharType> super;
328
329 ios_info& info = ios_info::get(ios);
330
331 switch(info.display_flags()) {
332 case flags::posix: {
333 std::stringstream ss;
334 ss.imbue(loc: std::locale::classic());
335 ss.flags(fmtfl: ios.flags());
336 ss.precision(prec: ios.precision());
337 return super::do_get(in, end, ss, err, val);
338 }
339 case flags::currency: {
340 long double ret_val = 0;
341 if(info.currency_flags() == flags::currency_default
342 || info.currency_flags() == flags::currency_national)
343 in = parse_currency<false>(in, end, ios, err, ret_val);
344 else
345 in = parse_currency<true>(in, end, ios, err, ret_val);
346 if(!(err & std::ios_base::failbit))
347 val = static_cast<ValueType>(ret_val);
348 return in;
349 }
350
351 // date-time parsing is not supported
352 // due to buggy standard
353 case flags::date:
354 case flags::time:
355 case flags::datetime:
356 case flags::strftime:
357
358 case flags::number:
359 case flags::percent:
360 case flags::spellout:
361 case flags::ordinal: break;
362 }
363 return super::do_get(in, end, ios, err, val);
364 }
365
366 template<bool intl>
367 iter_type parse_currency(iter_type in,
368 iter_type end,
369 std::ios_base& ios,
370 std::ios_base::iostate& err,
371 long double& val) const
372 {
373 std::locale loc = ios.getloc();
374 int digits = std::use_facet<std::moneypunct<CharType, intl>>(loc).frac_digits();
375 long double rval;
376 in = std::use_facet<std::money_get<CharType>>(loc).get(in, end, intl, ios, err, rval);
377 if(!(err & std::ios::failbit)) {
378 while(digits > 0) {
379 rval /= 10;
380 digits--;
381 }
382 val = rval;
383 }
384 return in;
385 }
386 };
387
388}}} // namespace boost::locale::util
389
390#endif
391

source code of boost/libs/locale/src/boost/locale/util/numeric.hpp