1 //
2 //  Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3 //
4 //  Distributed under the Boost Software License, Version 1.0. (See
5 //  accompanying file LICENSE_1_0.txt or copy at
6 //  http://www.boost.org/LICENSE_1_0.txt)
7 //
8 #define BOOST_LOCALE_SOURCE
9 #include <locale>
10 #include <string>
11 #include <ios>
12 #include <boost/locale/formatting.hpp>
13 #include <boost/locale/generator.hpp>
14 #include <boost/locale/encoding.hpp>
15 #include <boost/shared_ptr.hpp>
16 #include <sstream>
17 #include <stdlib.h>
18 #include <time.h>
19 #include <string.h>
20 #include <wctype.h>
21 #include <ctype.h>
22 #include <langinfo.h>
23 #include <monetary.h>
24 #include <errno.h>
25 #include "../util/numeric.hpp"
26 #include "all_generator.hpp"
27 
28 
29 #if defined(__linux) || defined(__APPLE__)
30 #define BOOST_LOCALE_HAVE_WCSFTIME_L
31 #endif
32 
33 namespace boost {
34 namespace locale {
35 namespace impl_posix {
36 
37 template<typename CharType>
38 class num_format : public util::base_num_format<CharType>
39 {
40 public:
41     typedef typename std::num_put<CharType>::iter_type iter_type;
42     typedef std::basic_string<CharType> string_type;
43     typedef CharType char_type;
44 
num_format(boost::shared_ptr<locale_t> lc,size_t refs=0)45     num_format(boost::shared_ptr<locale_t> lc,size_t refs = 0) :
46         util::base_num_format<CharType>(refs),
47         lc_(lc)
48     {
49     }
50 protected:
51 
do_format_currency(bool intl,iter_type out,std::ios_base &,char_type,long double val) const52     virtual iter_type do_format_currency(bool intl,iter_type out,std::ios_base &/*ios*/,char_type /*fill*/,long double val) const
53     {
54         char buf[4]={};
55         char const *format = intl ? "%i" : "%n";
56         errno=0;
57         ssize_t n = strfmon_l(buf,sizeof(buf),*lc_,format,static_cast<double>(val));
58         if(n >= 0)
59             return write_it(out,buf,n);
60 
61         for(std::vector<char> tmp(sizeof(buf)*2);tmp.size() <= 4098;tmp.resize(tmp.size()*2)) {
62             n = strfmon_l(&tmp.front(),tmp.size(),*lc_,format,static_cast<double>(val));
63             if(n >= 0)
64                 return write_it(out,&tmp.front(),n);
65         }
66         return out;
67     }
68 
write_it(std::ostreambuf_iterator<char> out,char const * ptr,size_t n) const69     std::ostreambuf_iterator<char> write_it(std::ostreambuf_iterator<char> out,char const *ptr,size_t n) const
70     {
71         for(size_t i=0;i<n;i++)
72             *out++ = *ptr++;
73         return out;
74     }
75 
write_it(std::ostreambuf_iterator<wchar_t> out,char const * ptr,size_t n) const76     std::ostreambuf_iterator<wchar_t> write_it(std::ostreambuf_iterator<wchar_t> out,char const *ptr,size_t n) const
77     {
78         std::wstring tmp = conv::to_utf<wchar_t>(ptr,ptr+n,nl_langinfo_l(CODESET,*lc_));
79         for(size_t i=0;i<tmp.size();i++)
80             *out++ = tmp[i];
81         return out;
82     }
83 private:
84 
85     boost::shared_ptr<locale_t> lc_;
86 
87 };  /// num_format
88 
89 
90 template<typename CharType>
91 struct ftime_traits;
92 
93 template<>
94 struct ftime_traits<char> {
ftimeboost::locale::impl_posix::ftime_traits95     static std::string ftime(char const *format,const struct tm *t,locale_t lc)
96     {
97         char buf[16];
98         size_t n=strftime_l(buf,sizeof(buf),format,t,lc);
99         if(n == 0) {
100             // should be big enough
101             //
102             // Note standard specifies that in case of the error
103             // the function returns 0, however 0 may be actually
104             // valid output value of for example empty format or an
105             // output of %p in some locales
106             //
107             // Thus we try to guess that 1024 would be enough.
108             std::vector<char> v(1024);
109             n = strftime_l(&v.front(),1024,format,t,lc);
110             return std::string(&v.front(),n);
111         }
112         return std::string(buf,n);
113     }
114 };
115 
116 template<>
117 struct ftime_traits<wchar_t> {
ftimeboost::locale::impl_posix::ftime_traits118     static std::wstring ftime(wchar_t const *format,const struct tm *t,locale_t lc)
119     {
120         #ifdef HAVE_WCSFTIME_L
121             wchar_t buf[16];
122             size_t n=wcsftime_l(buf,sizeof(buf)/sizeof(buf[0]),format,t,lc);
123             if(n == 0) {
124                 // should be big enough
125                 //
126                 // Note standard specifies that in case of the error
127                 // the function returns 0, however 0 may be actually
128                 // valid output value of for example empty format or an
129                 // output of %p in some locales
130                 //
131                 // Thus we try to guess that 1024 would be enough.
132                 std::vector<wchar_t> v(1024);
133                 n = wcsftime_l(&v.front(),1024,format,t,lc);
134             }
135             return std::wstring(&v.front(),n);
136         #else
137             std::string enc = nl_langinfo_l(CODESET,lc);
138             std::string nformat = conv::from_utf<wchar_t>(format,enc);
139             std::string nres = ftime_traits<char>::ftime(nformat.c_str(),t,lc);
140             return conv::to_utf<wchar_t>(nres,enc);
141         #endif
142     }
143 };
144 
145 
146 template<typename CharType>
147 class time_put_posix : public std::time_put<CharType> {
148 public:
time_put_posix(boost::shared_ptr<locale_t> lc,size_t refs=0)149     time_put_posix(boost::shared_ptr<locale_t> lc, size_t refs = 0) :
150         std::time_put<CharType>(refs),
151         lc_(lc)
152     {
153     }
~time_put_posix()154     virtual ~time_put_posix()
155     {
156     }
157     typedef typename std::time_put<CharType>::iter_type iter_type;
158     typedef CharType char_type;
159     typedef std::basic_string<char_type> string_type;
160 
do_put(iter_type out,std::ios_base &,CharType,std::tm const * tm,char format,char modifier) const161     virtual iter_type do_put(iter_type out,std::ios_base &/*ios*/,CharType /*fill*/,std::tm const *tm,char format,char modifier) const
162     {
163         char_type fmt[4] = { '%' , modifier != 0 ? modifier : format , modifier == 0 ? '\0' : format };
164         string_type res = ftime_traits<char_type>::ftime(fmt,tm,*lc_);
165         for(unsigned i=0;i<res.size();i++)
166             *out++ = res[i];
167         return out;
168     }
169 
170 private:
171     boost::shared_ptr<locale_t> lc_;
172 };
173 
174 
175 template<typename CharType>
176 class ctype_posix;
177 
178 template<>
179 class ctype_posix<char> : public std::ctype<char> {
180 public:
181 
ctype_posix(boost::shared_ptr<locale_t> lc)182     ctype_posix(boost::shared_ptr<locale_t> lc)
183     {
184         lc_ = lc;
185     }
186 
do_is(mask m,char c) const187     bool do_is(mask m,char c) const
188     {
189         if((m & space) && isspace_l(c,*lc_))
190             return true;
191         if((m & print) && isprint_l(c,*lc_))
192             return true;
193         if((m & cntrl) && iscntrl_l(c,*lc_))
194             return true;
195         if((m & upper) && isupper_l(c,*lc_))
196             return true;
197         if((m & lower) && islower_l(c,*lc_))
198             return true;
199         if((m & alpha) && isalpha_l(c,*lc_))
200             return true;
201         if((m & digit) && isdigit_l(c,*lc_))
202             return true;
203         if((m & xdigit) && isxdigit_l(c,*lc_))
204             return true;
205         if((m & punct) && ispunct_l(c,*lc_))
206             return true;
207         return false;
208     }
do_is(char const * begin,char const * end,mask * m) const209     char const *do_is(char const *begin,char const *end,mask *m) const
210     {
211         while(begin!=end) {
212             char c= *begin++;
213             int r=0;
214             if(isspace_l(c,*lc_))
215                 r|=space;
216             if(isprint_l(c,*lc_))
217                 r|=cntrl;
218             if(iscntrl_l(c,*lc_))
219                 r|=space;
220             if(isupper_l(c,*lc_))
221                 r|=upper;
222             if(islower_l(c,*lc_))
223                 r|=lower;
224             if(isalpha_l(c,*lc_))
225                 r|=alpha;
226             if(isdigit_l(c,*lc_))
227                 r|=digit;
228             if(isxdigit_l(c,*lc_))
229                 r|=xdigit;
230             if(ispunct_l(c,*lc_))
231                 r|=punct;
232             // r actually should be mask, but some standard
233             // libraries (like STLPort)
234             // do not define operator | properly so using int+cast
235             *m++ = static_cast<mask>(r);
236         }
237         return begin;
238     }
do_scan_is(mask m,char const * begin,char const * end) const239     char const *do_scan_is(mask m,char const *begin,char const *end) const
240     {
241         while(begin!=end)
242             if(do_is(m,*begin))
243                 return begin;
244         return begin;
245     }
do_scan_not(mask m,char const * begin,char const * end) const246     char const *do_scan_not(mask m,char const *begin,char const *end) const
247     {
248         while(begin!=end)
249             if(!do_is(m,*begin))
250                 return begin;
251         return begin;
252     }
toupper(char c) const253     char toupper(char c) const
254     {
255         return toupper_l(c,*lc_);
256     }
toupper(char * begin,char const * end) const257     char const *toupper(char *begin,char const *end) const
258     {
259         for(;begin!=end;begin++)
260             *begin = toupper_l(*begin,*lc_);
261         return begin;
262     }
tolower(char c) const263     char tolower(char c) const
264     {
265         return tolower_l(c,*lc_);
266     }
tolower(char * begin,char const * end) const267     char const *tolower(char *begin,char const *end) const
268     {
269         for(;begin!=end;begin++)
270             *begin = tolower_l(*begin,*lc_);
271         return begin;
272     }
273 private:
274     boost::shared_ptr<locale_t> lc_;
275 };
276 
277 template<>
278 class ctype_posix<wchar_t> : public std::ctype<wchar_t> {
279 public:
ctype_posix(boost::shared_ptr<locale_t> lc)280     ctype_posix(boost::shared_ptr<locale_t> lc)
281     {
282         lc_ = lc;
283     }
284 
do_is(mask m,wchar_t c) const285     bool do_is(mask m,wchar_t c) const
286     {
287         if((m & space) && iswspace_l(c,*lc_))
288             return true;
289         if((m & print) && iswprint_l(c,*lc_))
290             return true;
291         if((m & cntrl) && iswcntrl_l(c,*lc_))
292             return true;
293         if((m & upper) && iswupper_l(c,*lc_))
294             return true;
295         if((m & lower) && iswlower_l(c,*lc_))
296             return true;
297         if((m & alpha) && iswalpha_l(c,*lc_))
298             return true;
299         if((m & digit) && iswdigit_l(c,*lc_))
300             return true;
301         if((m & xdigit) && iswxdigit_l(c,*lc_))
302             return true;
303         if((m & punct) && iswpunct_l(c,*lc_))
304             return true;
305         return false;
306     }
do_is(wchar_t const * begin,wchar_t const * end,mask * m) const307     wchar_t const *do_is(wchar_t const *begin,wchar_t const *end,mask *m) const
308     {
309         while(begin!=end) {
310             wchar_t c= *begin++;
311             int r=0;
312             if(iswspace_l(c,*lc_))
313                 r|=space;
314             if(iswprint_l(c,*lc_))
315                 r|=cntrl;
316             if(iswcntrl_l(c,*lc_))
317                 r|=space;
318             if(iswupper_l(c,*lc_))
319                 r|=upper;
320             if(iswlower_l(c,*lc_))
321                 r|=lower;
322             if(iswalpha_l(c,*lc_))
323                 r|=alpha;
324             if(iswdigit_l(c,*lc_))
325                 r|=digit;
326             if(iswxdigit_l(c,*lc_))
327                 r|=xdigit;
328             if(iswpunct_l(c,*lc_))
329                 r|=punct;
330             // r actually should be mask, but some standard
331             // libraries (like STLPort)
332             // do not define operator | properly so using int+cast
333             *m++ = static_cast<mask>(r);
334         }
335         return begin;
336     }
do_scan_is(mask m,wchar_t const * begin,wchar_t const * end) const337     wchar_t const *do_scan_is(mask m,wchar_t const *begin,wchar_t const *end) const
338     {
339         while(begin!=end)
340             if(do_is(m,*begin))
341                 return begin;
342         return begin;
343     }
do_scan_not(mask m,wchar_t const * begin,wchar_t const * end) const344     wchar_t const *do_scan_not(mask m,wchar_t const *begin,wchar_t const *end) const
345     {
346         while(begin!=end)
347             if(!do_is(m,*begin))
348                 return begin;
349         return begin;
350     }
toupper(wchar_t c) const351     wchar_t toupper(wchar_t c) const
352     {
353         return towupper_l(c,*lc_);
354     }
toupper(wchar_t * begin,wchar_t const * end) const355     wchar_t const *toupper(wchar_t *begin,wchar_t const *end) const
356     {
357         for(;begin!=end;begin++)
358             *begin = towupper_l(*begin,*lc_);
359         return begin;
360     }
tolower(wchar_t c) const361     wchar_t tolower(wchar_t c) const
362     {
363         return tolower_l(c,*lc_);
364     }
tolower(wchar_t * begin,wchar_t const * end) const365     wchar_t const *tolower(wchar_t *begin,wchar_t const *end) const
366     {
367         for(;begin!=end;begin++)
368             *begin = tolower_l(*begin,*lc_);
369         return begin;
370     }
371 private:
372     boost::shared_ptr<locale_t> lc_;
373 };
374 
375 
376 
377 
378 struct basic_numpunct {
379     std::string grouping;
380     std::string thousands_sep;
381     std::string decimal_point;
basic_numpunctboost::locale::impl_posix::basic_numpunct382     basic_numpunct() :
383         decimal_point(".")
384     {
385     }
basic_numpunctboost::locale::impl_posix::basic_numpunct386     basic_numpunct(locale_t lc)
387     {
388     #ifdef __APPLE__
389         lconv *cv = localeconv_l(lc);
390         grouping = cv->grouping;
391         thousands_sep = cv->thousands_sep;
392         decimal_point = cv->decimal_point;
393     #else
394         thousands_sep = nl_langinfo_l(THOUSEP,lc);
395         decimal_point = nl_langinfo_l(RADIXCHAR,lc);
396         #ifdef GROUPING
397         grouping = nl_langinfo_l(GROUPING,lc);
398         #endif
399     #endif
400     }
401 };
402 
403 template<typename CharType>
404 class num_punct_posix : public std::numpunct<CharType> {
405 public:
406     typedef std::basic_string<CharType> string_type;
num_punct_posix(locale_t lc,size_t refs=0)407     num_punct_posix(locale_t lc,size_t refs = 0) :
408         std::numpunct<CharType>(refs)
409     {
410         basic_numpunct np(lc);
411         to_str(np.thousands_sep,thousands_sep_,lc);
412         to_str(np.decimal_point,decimal_point_,lc);
413         grouping_ = np.grouping;
414         if(thousands_sep_.size() > 1)
415             grouping_ = std::string();
416         if(decimal_point_.size() > 1)
417             decimal_point_ = CharType('.');
418     }
to_str(std::string & s1,std::string & s2,locale_t)419     void to_str(std::string &s1,std::string &s2,locale_t /*lc*/)
420     {
421         s2.swap(s1);
422     }
to_str(std::string & s1,std::wstring & s2,locale_t lc)423     void to_str(std::string &s1,std::wstring &s2,locale_t lc)
424     {
425         s2=conv::to_utf<wchar_t>(s1,nl_langinfo_l(CODESET,lc));
426     }
do_decimal_point() const427     virtual CharType do_decimal_point() const
428     {
429         return *decimal_point_.c_str();
430     }
do_thousands_sep() const431     virtual CharType do_thousands_sep() const
432     {
433         return *thousands_sep_.c_str();
434     }
do_grouping() const435     virtual std::string do_grouping() const
436     {
437         return grouping_;
438     }
do_truename() const439     virtual string_type do_truename() const
440     {
441         static const char t[]="true";
442         return string_type(t,t+sizeof(t)-1);
443     }
do_falsename() const444     virtual string_type do_falsename() const
445     {
446         static const char t[]="false";
447         return string_type(t,t+sizeof(t)-1);
448     }
449 private:
450     string_type decimal_point_;
451     string_type thousands_sep_;
452     std::string grouping_;
453 };
454 
455 template<typename CharType>
create_formatting_impl(std::locale const & in,boost::shared_ptr<locale_t> lc)456 std::locale create_formatting_impl(std::locale const &in,boost::shared_ptr<locale_t> lc)
457 {
458     std::locale tmp = std::locale(in,new num_punct_posix<CharType>(*lc));
459     tmp = std::locale(tmp,new ctype_posix<CharType>(lc));
460     tmp = std::locale(tmp,new time_put_posix<CharType>(lc));
461     tmp = std::locale(tmp,new num_format<CharType>(lc));
462     return tmp;
463 }
464 
465 template<typename CharType>
create_parsing_impl(std::locale const & in,boost::shared_ptr<locale_t> lc)466 std::locale create_parsing_impl(std::locale const &in,boost::shared_ptr<locale_t> lc)
467 {
468     std::locale tmp = std::locale(in,new num_punct_posix<CharType>(*lc));
469     tmp = std::locale(tmp,new ctype_posix<CharType>(lc));
470     tmp = std::locale(tmp,new util::base_num_parse<CharType>());
471     return tmp;
472 }
473 
474 
create_formatting(std::locale const & in,boost::shared_ptr<locale_t> lc,character_facet_type type)475 std::locale create_formatting(  std::locale const &in,
476                                 boost::shared_ptr<locale_t> lc,
477                                 character_facet_type type)
478 {
479         switch(type) {
480         case char_facet:
481             return create_formatting_impl<char>(in,lc);
482         case wchar_t_facet:
483             return create_formatting_impl<wchar_t>(in,lc);
484         default:
485             return in;
486         }
487 }
488 
create_parsing(std::locale const & in,boost::shared_ptr<locale_t> lc,character_facet_type type)489 std::locale create_parsing( std::locale const &in,
490                             boost::shared_ptr<locale_t> lc,
491                             character_facet_type type)
492 {
493         switch(type) {
494         case char_facet:
495             return create_parsing_impl<char>(in,lc);
496         case wchar_t_facet:
497             return create_parsing_impl<wchar_t>(in,lc);
498         default:
499             return in;
500         }
501 }
502 
503 
504 
505 } // impl_std
506 } // locale
507 } //boost
508 
509 
510 
511 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
512