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