1 //
2 //  Copyright (c) 2009-2015 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 #define BOOST_DETAIL_NO_CONTAINER_FWD
10 #include <boost/config.hpp>
11 #include <boost/version.hpp>
12 #include <boost/locale/message.hpp>
13 #include <boost/locale/gnu_gettext.hpp>
14 #include <boost/shared_ptr.hpp>
15 #include <boost/locale/hold_ptr.hpp>
16 #include <boost/locale/encoding.hpp>
17 #ifdef BOOST_MSVC
18 #  pragma warning(disable : 4996)
19 #endif
20 
21 
22 #if BOOST_VERSION >= 103600
23 #define BOOST_LOCALE_UNORDERED_CATALOG
24 #endif
25 
26 #ifdef BOOST_LOCALE_UNORDERED_CATALOG
27 #include <boost/unordered_map.hpp>
28 #else
29 #include <map>
30 #endif
31 
32 #include <iostream>
33 
34 
35 #include "mo_hash.hpp"
36 #include "mo_lambda.hpp"
37 
38 #include <stdio.h>
39 
40 #include <string.h>
41 
42 namespace boost {
43     namespace locale {
44         namespace gnu_gettext {
45 
46             class c_file {
47                 c_file(c_file const &);
48                 void operator=(c_file const &);
49             public:
50 
51                 FILE *file;
52 
c_file()53                 c_file() :
54                     file(0)
55                 {
56                 }
~c_file()57                 ~c_file()
58                 {
59                     close();
60                 }
61 
close()62                 void close()
63                 {
64                     if(file) {
65                         fclose(file);
66                         file=0;
67                     }
68                 }
69 
70                 #if defined(BOOST_WINDOWS)
71 
open(std::string const & file_name,std::string const & encoding)72                 bool open(std::string const &file_name,std::string const &encoding)
73                 {
74                     close();
75 
76                     //
77                     // Under windows we have to use "_wfopen" to get
78                     // access to path's with Unicode in them
79                     //
80                     // As not all standard C++ libraries support nonstandard std::istream::open(wchar_t const *)
81                     // we would use old and good stdio and _wfopen CRTL functions
82                     //
83 
84                     std::wstring wfile_name = conv::to_utf<wchar_t>(file_name,encoding);
85                     file = _wfopen(wfile_name.c_str(),L"rb");
86 
87                     return file!=0;
88                 }
89 
90                 #else // POSIX systems do not have all this Wide API crap, as native codepages are UTF-8
91 
92                 // We do not use encoding as we use native file name encoding
93 
open(std::string const & file_name,std::string const &)94                 bool open(std::string const &file_name,std::string const &/* encoding */)
95                 {
96                     close();
97 
98                     file = fopen(file_name.c_str(),"rb");
99 
100                     return file!=0;
101                 }
102 
103                 #endif
104 
105             };
106 
107             class mo_file {
108             public:
109                 typedef std::pair<char const *,char const *> pair_type;
110 
mo_file(std::vector<char> & file)111                 mo_file(std::vector<char> &file) :
112                     native_byteorder_(true),
113                     size_(0)
114                 {
115                     load_file(file);
116                     init();
117                 }
118 
mo_file(FILE * file)119                 mo_file(FILE *file) :
120                     native_byteorder_(true),
121                     size_(0)
122                 {
123                     load_file(file);
124                     init();
125                 }
126 
find(char const * context_in,char const * key_in) const127                 pair_type find(char const *context_in,char const *key_in) const
128                 {
129                     pair_type null_pair((char const *)0,(char const *)0);
130                     if(hash_size_==0)
131                         return null_pair;
132                     uint32_t hkey = 0;
133                     if(context_in == 0)
134                         hkey = pj_winberger_hash_function(key_in);
135                     else {
136                         pj_winberger_hash::state_type st = pj_winberger_hash::initial_state;
137                         st = pj_winberger_hash::update_state(st,context_in);
138                         st = pj_winberger_hash::update_state(st,'\4'); // EOT
139                         st = pj_winberger_hash::update_state(st,key_in);
140                         hkey = st;
141                     }
142                     uint32_t incr = 1 + hkey % (hash_size_-2);
143                     hkey %= hash_size_;
144                     uint32_t orig=hkey;
145 
146 
147                     do {
148                         uint32_t idx = get(hash_offset_ + 4*hkey);
149                         /// Not found
150                         if(idx == 0)
151                             return null_pair;
152                         /// If equal values return translation
153                         if(key_equals(key(idx-1),context_in,key_in))
154                             return value(idx-1);
155                         /// Rehash
156                         hkey=(hkey + incr) % hash_size_;
157                     } while(hkey!=orig);
158                     return null_pair;
159                 }
160 
key_equals(char const * real_key,char const * cntx,char const * key)161                 static bool key_equals(char const *real_key,char const *cntx,char const *key)
162                 {
163                     if(cntx == 0)
164                         return strcmp(real_key,key) == 0;
165                     else {
166                         size_t real_len = strlen(real_key);
167                         size_t cntx_len = strlen(cntx);
168                         size_t key_len = strlen(key);
169                         if(cntx_len + 1 + key_len != real_len)
170                             return false;
171                         return
172                             memcmp(real_key,cntx,cntx_len) == 0
173                             && real_key[cntx_len] == '\4'
174                             && memcmp(real_key + cntx_len + 1 ,key,key_len) == 0;
175                     }
176                 }
177 
key(int id) const178                 char const *key(int id) const
179                 {
180                     uint32_t off = get(keys_offset_ + id*8 + 4);
181                     return data_ + off;
182                 }
183 
value(int id) const184                 pair_type value(int id) const
185                 {
186                     uint32_t len = get(translations_offset_ + id*8);
187                     uint32_t off = get(translations_offset_ + id*8 + 4);
188                     if(off >= file_size_ || off + len >= file_size_)
189                         throw std::runtime_error("Bad mo-file format");
190                     return pair_type(&data_[off],&data_[off]+len);
191                 }
192 
has_hash() const193                 bool has_hash() const
194                 {
195                     return hash_size_ != 0;
196                 }
197 
size() const198                 size_t size() const
199                 {
200                     return size_;
201                 }
202 
empty()203                 bool empty()
204                 {
205                     return size_ == 0;
206                 }
207 
208             private:
init()209                 void init()
210                 {
211                     // Read all format sizes
212                     size_=get(8);
213                     keys_offset_=get(12);
214                     translations_offset_=get(16);
215                     hash_size_=get(20);
216                     hash_offset_=get(24);
217                 }
218 
load_file(std::vector<char> & data)219                 void load_file(std::vector<char> &data)
220                 {
221                     vdata_.swap(data);
222                     file_size_ = vdata_.size();
223                     data_ = &vdata_[0];
224                     if(file_size_ < 4 )
225                         throw std::runtime_error("invalid 'mo' file format - the file is too short");
226                     uint32_t magic=0;
227                     memcpy(&magic,data_,4);
228                     if(magic == 0x950412de)
229                         native_byteorder_ = true;
230                     else if(magic == 0xde120495)
231                         native_byteorder_ = false;
232                     else
233                         throw std::runtime_error("Invalid file format - invalid magic number");
234                 }
235 
load_file(FILE * file)236                 void load_file(FILE *file)
237                 {
238                     uint32_t magic=0;
239                     // if the size is wrong magic would be wrong
240                     // ok to ingnore fread result
241                     size_t four_bytes = fread(&magic,4,1,file);
242                     (void)four_bytes; // shut GCC
243 
244                     if(magic == 0x950412de)
245                         native_byteorder_ = true;
246                     else if(magic == 0xde120495)
247                         native_byteorder_ = false;
248                     else
249                         throw std::runtime_error("Invalid file format");
250 
251                     fseek(file,0,SEEK_END);
252                     long len=ftell(file);
253                     if(len < 0) {
254                         throw std::runtime_error("Wrong file object");
255                     }
256                     fseek(file,0,SEEK_SET);
257                     vdata_.resize(len+1,0); // +1 to make sure the vector is not empty
258                     if(fread(&vdata_.front(),1,len,file)!=unsigned(len))
259                         throw std::runtime_error("Failed to read file");
260                     data_ = &vdata_[0];
261                     file_size_ = len;
262                 }
263 
get(unsigned offset) const264                 uint32_t get(unsigned offset) const
265                 {
266                     uint32_t tmp;
267                     if(offset > file_size_ - 4) {
268                         throw std::runtime_error("Bad mo-file format");
269                     }
270                     memcpy(&tmp,data_ + offset,4);
271                     convert(tmp);
272                     return tmp;
273                 }
274 
convert(uint32_t & v) const275                 void convert(uint32_t &v) const
276                 {
277                     if(native_byteorder_)
278                         return;
279                     v =   ((v & 0xFF) << 24)
280                         | ((v & 0xFF00) << 8)
281                         | ((v & 0xFF0000) >> 8)
282                         | ((v & 0xFF000000) >> 24);
283                 }
284 
285                 uint32_t keys_offset_;
286                 uint32_t translations_offset_;
287                 uint32_t hash_size_;
288                 uint32_t hash_offset_;
289 
290                 char const *data_;
291                 size_t file_size_;
292                 std::vector<char> vdata_;
293                 bool native_byteorder_;
294                 size_t size_;
295             };
296 
297             template<typename CharType>
298             struct mo_file_use_traits {
299                 static const bool in_use = false;
300                 typedef CharType char_type;
301                 typedef std::pair<char_type const *,char_type const *> pair_type;
useboost::locale::gnu_gettext::mo_file_use_traits302                 static pair_type use(mo_file const &/*mo*/,char_type const * /*context*/,char_type const * /*key*/)
303                 {
304                     return pair_type((char_type const *)(0),(char_type const *)(0));
305                 }
306             };
307 
308             template<>
309             struct mo_file_use_traits<char> {
310                 static const bool in_use = true;
311                 typedef char char_type;
312                 typedef std::pair<char_type const *,char_type const *> pair_type;
useboost::locale::gnu_gettext::mo_file_use_traits313                 static pair_type use(mo_file const &mo,char const *context,char const *key)
314                 {
315                     return mo.find(context,key);
316                 }
317             };
318 
319             template<typename CharType>
320             class converter {
321             public:
converter(std::string,std::string in_enc)322                 converter(std::string /*out_enc*/,std::string in_enc) :
323                     in_(in_enc)
324                 {
325                 }
326 
operator ()(char const * begin,char const * end)327                 std::basic_string<CharType> operator()(char const *begin,char const *end)
328                 {
329                     return conv::to_utf<CharType>(begin,end,in_,conv::stop);
330                 }
331 
332             private:
333                 std::string in_;
334             };
335 
336             template<>
337             class converter<char> {
338             public:
converter(std::string out_enc,std::string in_enc)339                 converter(std::string out_enc,std::string in_enc) :
340                     out_(out_enc),
341                     in_(in_enc)
342                 {
343                 }
344 
operator ()(char const * begin,char const * end)345                 std::string operator()(char const *begin,char const *end)
346                 {
347                     return conv::between(begin,end,out_,in_,conv::stop);
348                 }
349 
350             private:
351                 std::string out_,in_;
352             };
353 
354             template<typename CharType>
355             struct message_key {
356                 typedef CharType char_type;
357                 typedef std::basic_string<char_type> string_type;
358 
359 
message_keyboost::locale::gnu_gettext::message_key360                 message_key(string_type const &c = string_type()) :
361                     c_context_(0),
362                     c_key_(0)
363                 {
364                     size_t pos = c.find(char_type(4));
365                     if(pos == string_type::npos) {
366                         key_ = c;
367                     }
368                     else {
369                         context_ = c.substr(0,pos);
370                         key_ = c.substr(pos+1);
371                     }
372                 }
message_keyboost::locale::gnu_gettext::message_key373                 message_key(char_type const *c,char_type const *k) :
374                     c_key_(k)
375                 {
376                     static const char_type empty = 0;
377                     if(c!=0)
378                         c_context_ = c;
379                     else
380                         c_context_ = &empty;
381                 }
operator <boost::locale::gnu_gettext::message_key382                 bool operator < (message_key const &other) const
383                 {
384                     int cc = compare(context(),other.context());
385                     if(cc != 0)
386                         return cc < 0;
387                     return compare(key(),other.key()) < 0;
388                 }
operator ==boost::locale::gnu_gettext::message_key389                 bool operator==(message_key const &other) const
390                 {
391                     return compare(context(),other.context()) == 0
392                             && compare(key(),other.key())==0;
393                 }
operator !=boost::locale::gnu_gettext::message_key394                 bool operator!=(message_key const &other) const
395                 {
396                     return !(*this==other);
397                 }
contextboost::locale::gnu_gettext::message_key398                 char_type const *context() const
399                 {
400                     if(c_context_)
401                         return c_context_;
402                     return context_.c_str();
403                 }
keyboost::locale::gnu_gettext::message_key404                 char_type const *key() const
405                 {
406                     if(c_key_)
407                         return c_key_;
408                     return key_.c_str();
409                 }
410             private:
compareboost::locale::gnu_gettext::message_key411                 static int compare(char_type const *l,char_type const *r)
412                 {
413                     typedef std::char_traits<char_type> traits_type;
414                     for(;;) {
415                         char_type cl = *l++;
416                         char_type cr = *r++;
417                         if(cl == 0 && cr == 0)
418                             return 0;
419                         if(traits_type::lt(cl,cr))
420                             return -1;
421                         if(traits_type::lt(cr,cl))
422                             return 1;
423                     }
424                 }
425                 string_type context_;
426                 string_type key_;
427                 char_type const *c_context_;
428                 char_type const *c_key_;
429             };
430 
431             template<typename CharType>
432             struct hash_function {
operator ()boost::locale::gnu_gettext::hash_function433                 size_t operator()(message_key<CharType> const &msg) const
434                 {
435                     pj_winberger_hash::state_type state = pj_winberger_hash::initial_state;
436                     CharType const *p = msg.context();
437                     if(*p != 0) {
438                         CharType const *e = p;
439                         while(*e)
440                             e++;
441                         state = pj_winberger_hash::update_state(state,
442                                     reinterpret_cast<char const *>(p),
443                                     reinterpret_cast<char const *>(e));
444                         state = pj_winberger_hash::update_state(state,'\4');
445                     }
446                     p = msg.key();
447                     CharType const *e = p;
448                     while(*e)
449                         e++;
450                     state = pj_winberger_hash::update_state(state,
451                                 reinterpret_cast<char const *>(p),
452                                 reinterpret_cast<char const *>(e));
453                     return state;
454                 }
455             };
456 
457 
458             // By default for wide types the conversion is not requiredyy
459             template<typename CharType>
runtime_conversion(CharType const * msg,std::basic_string<CharType> &,bool,std::string const &,std::string const &)460             CharType const *runtime_conversion(CharType const *msg,
461                                                 std::basic_string<CharType> &/*buffer*/,
462                                                 bool /*do_conversion*/,
463                                                 std::string const &/*locale_encoding*/,
464                                                 std::string const &/*key_encoding*/)
465             {
466                 return msg;
467             }
468 
469             // But still need to specialize for char
470             template<>
runtime_conversion(char const * msg,std::string & buffer,bool do_conversion,std::string const & locale_encoding,std::string const & key_encoding)471             char const *runtime_conversion( char const *msg,
472                                             std::string &buffer,
473                                             bool do_conversion,
474                                             std::string const &locale_encoding,
475                                             std::string const &key_encoding)
476             {
477                 if(!do_conversion)
478                     return msg;
479                 if(details::is_us_ascii_string(msg))
480                     return msg;
481                 std::string tmp = conv::between(msg,locale_encoding,key_encoding,conv::skip);
482                 buffer.swap(tmp);
483                 return buffer.c_str();
484             }
485 
486             template<typename CharType>
487             class mo_message : public message_format<CharType> {
488 
489                 typedef CharType char_type;
490                 typedef std::basic_string<CharType> string_type;
491                 typedef message_key<CharType> key_type;
492                 #ifdef BOOST_LOCALE_UNORDERED_CATALOG
493                 typedef boost::unordered_map<key_type,string_type,hash_function<CharType> > catalog_type;
494                 #else
495                 typedef std::map<key_type,string_type> catalog_type;
496                 #endif
497                 typedef std::vector<catalog_type> catalogs_set_type;
498                 typedef std::map<std::string,int> domains_map_type;
499             public:
500 
501                 typedef std::pair<CharType const *,CharType const *> pair_type;
502 
get(int domain_id,char_type const * context,char_type const * id) const503                 virtual char_type const *get(int domain_id,char_type const *context,char_type const *id) const
504                 {
505                     return get_string(domain_id,context,id).first;
506                 }
507 
get(int domain_id,char_type const * context,char_type const * single_id,int n) const508                 virtual char_type const *get(int domain_id,char_type const *context,char_type const *single_id,int n) const
509                 {
510                     pair_type ptr = get_string(domain_id,context,single_id);
511                     if(!ptr.first)
512                         return 0;
513                     int form=0;
514                     if(plural_forms_.at(domain_id))
515                         form = (*plural_forms_[domain_id])(n);
516                     else
517                         form = n == 1 ? 0 : 1; // Fallback to english plural form
518 
519                     CharType const *p=ptr.first;
520                     for(int i=0;p < ptr.second && i<form;i++) {
521                         p=std::find(p,ptr.second,0);
522                         if(p==ptr.second)
523                             return 0;
524                         ++p;
525                     }
526                     if(p>=ptr.second)
527                         return 0;
528                     return p;
529                 }
530 
domain(std::string const & domain) const531                 virtual int domain(std::string const &domain) const
532                 {
533                     domains_map_type::const_iterator p=domains_.find(domain);
534                     if(p==domains_.end())
535                         return -1;
536                     return p->second;
537                 }
538 
mo_message(messages_info const & inf)539                 mo_message(messages_info const &inf)
540                 {
541                     std::string language = inf.language;
542                     std::string variant = inf.variant;
543                     std::string country = inf.country;
544                     std::string encoding = inf.encoding;
545                     std::string lc_cat = inf.locale_category;
546                     std::vector<messages_info::domain> const &domains = inf.domains;
547                     std::vector<std::string> const &search_paths = inf.paths;
548 
549                     //
550                     // List of fallbacks: en_US@euro, en@euro, en_US, en.
551                     //
552                     std::vector<std::string> paths;
553 
554 
555                     if(!variant.empty() && !country.empty())
556                         paths.push_back(language + "_" + country + "@" + variant);
557 
558                     if(!variant.empty())
559                         paths.push_back(language + "@" + variant);
560 
561                     if(!country.empty())
562                         paths.push_back(language + "_" + country);
563 
564                     paths.push_back(language);
565 
566                     catalogs_.resize(domains.size());
567                     mo_catalogs_.resize(domains.size());
568                     plural_forms_.resize(domains.size());
569 
570 
571                     for(unsigned id=0;id<domains.size();id++) {
572                         std::string domain=domains[id].name;
573                         std::string key_encoding = domains[id].encoding;
574                         domains_[domain]=id;
575 
576 
577                         bool found=false;
578                         for(unsigned j=0;!found && j<paths.size();j++) {
579                             for(unsigned i=0;!found && i<search_paths.size();i++) {
580                                 std::string full_path = search_paths[i]+"/"+paths[j]+"/" + lc_cat + "/"+domain+".mo";
581                                 found = load_file(full_path,encoding,key_encoding,id,inf.callback);
582                             }
583                         }
584                     }
585                 }
586 
convert(char_type const * msg,string_type & buffer) const587                 char_type const *convert(char_type const *msg,string_type &buffer) const
588                 {
589                     return runtime_conversion<char_type>(msg,buffer,key_conversion_required_,locale_encoding_,key_encoding_);
590                 }
591 
~mo_message()592                 virtual ~mo_message()
593                 {
594                 }
595 
596             private:
compare_encodings(std::string const & left,std::string const & right)597                 int compare_encodings(std::string const &left,std::string const &right)
598                 {
599                     return convert_encoding_name(left).compare(convert_encoding_name(right));
600                 }
601 
convert_encoding_name(std::string const & in)602                 std::string convert_encoding_name(std::string const &in)
603                 {
604                     std::string result;
605                     for(unsigned i=0;i<in.size();i++) {
606                         char c=in[i];
607                         if('A' <= c && c<='Z')
608                             c=c-'A' + 'a';
609                         else if(('a' <= c && c<='z') || ('0' <= c && c<='9'))
610                             ;
611                         else
612                             continue;
613                         result+=c;
614                     }
615                     return result;
616                 }
617 
618 
load_file(std::string const & file_name,std::string const & locale_encoding,std::string const & key_encoding,int id,messages_info::callback_type const & callback)619                 bool load_file( std::string const &file_name,
620                                 std::string const &locale_encoding,
621                                 std::string const &key_encoding,
622                                 int id,
623                                 messages_info::callback_type const &callback)
624                 {
625                     locale_encoding_ = locale_encoding;
626                     key_encoding_ = key_encoding;
627 
628                     key_conversion_required_ =  sizeof(CharType) == 1
629                                                 && compare_encodings(locale_encoding,key_encoding)!=0;
630 
631                     boost::shared_ptr<mo_file> mo;
632 
633                     if(callback) {
634                         std::vector<char> vfile = callback(file_name,locale_encoding);
635                         if(vfile.empty())
636                             return false;
637                         mo.reset(new mo_file(vfile));
638                     }
639                     else {
640                         c_file the_file;
641                         the_file.open(file_name,locale_encoding);
642                         if(!the_file.file)
643                             return false;
644                         mo.reset(new mo_file(the_file.file));
645                     }
646 
647                     std::string plural = extract(mo->value(0).first,"plural=","\r\n;");
648 
649                     std::string mo_encoding = extract(mo->value(0).first,"charset="," \r\n;");
650 
651                     if(mo_encoding.empty())
652                         throw std::runtime_error("Invalid mo-format, encoding is not specified");
653 
654                     if(!plural.empty()) {
655                         plural_forms_[id] = lambda::compile(plural.c_str());;
656                     }
657 
658                     if( mo_useable_directly(mo_encoding,*mo) )
659                     {
660                         mo_catalogs_[id]=mo;
661                     }
662                     else {
663                         converter<CharType> cvt_value(locale_encoding,mo_encoding);
664                         converter<CharType> cvt_key(key_encoding,mo_encoding);
665                         for(unsigned i=0;i<mo->size();i++) {
666                             char const *ckey = mo->key(i);
667                             string_type skey = cvt_key(ckey,ckey+strlen(ckey));
668                             key_type key(skey);
669 
670                             mo_file::pair_type tmp = mo->value(i);
671                             string_type value = cvt_value(tmp.first,tmp.second);
672                             catalogs_[id][key].swap(value);
673                         }
674                     }
675                     return true;
676 
677                 }
678 
679                 // Check if the mo file as-is is useful
680                 // 1. It is char and not wide character
681                 // 2. The locale encoding and mo encoding is same
682                 // 3. The source strings encoding and mo encoding is same or all
683                 //    mo key strings are US-ASCII
mo_useable_directly(std::string const & mo_encoding,mo_file const & mo)684                 bool mo_useable_directly(   std::string const &mo_encoding,
685                                             mo_file const &mo)
686                 {
687                     if(sizeof(CharType) != 1)
688                         return false;
689                     if(!mo.has_hash())
690                         return false;
691                     if(compare_encodings(mo_encoding,locale_encoding_)!=0)
692                         return false;
693                     if(compare_encodings(mo_encoding,key_encoding_)==0) {
694                         return true;
695                     }
696                     for(unsigned i=0;i<mo.size();i++) {
697                         if(!details::is_us_ascii_string(mo.key(i))) {
698                             return false;
699                         }
700                     }
701                     return true;
702                 }
703 
704 
705 
extract(std::string const & meta,std::string const & key,char const * separator)706                 static std::string extract(std::string const &meta,std::string const &key,char const *separator)
707                 {
708                     size_t pos=meta.find(key);
709                     if(pos == std::string::npos)
710                         return "";
711                     pos+=key.size(); /// size of charset=
712                     size_t end_pos = meta.find_first_of(separator,pos);
713                     return meta.substr(pos,end_pos - pos);
714                 }
715 
716 
717 
718 
get_string(int domain_id,char_type const * context,char_type const * in_id) const719                 pair_type get_string(int domain_id,char_type const *context,char_type const *in_id) const
720                 {
721                     pair_type null_pair((CharType const *)0,(CharType const *)0);
722                     if(domain_id < 0 || size_t(domain_id) >= catalogs_.size())
723                         return null_pair;
724                     if(mo_file_use_traits<char_type>::in_use && mo_catalogs_[domain_id]) {
725                         return mo_file_use_traits<char_type>::use(*mo_catalogs_[domain_id],context,in_id);
726                     }
727                     else {
728                         key_type key(context,in_id);
729                         catalog_type const &cat = catalogs_[domain_id];
730                         typename catalog_type::const_iterator p = cat.find(key);
731                         if(p==cat.end()) {
732                             return null_pair;
733                         }
734                         return pair_type(p->second.data(),p->second.data()+p->second.size());
735                     }
736                 }
737 
738                 catalogs_set_type catalogs_;
739                 std::vector<boost::shared_ptr<mo_file> > mo_catalogs_;
740                 std::vector<boost::shared_ptr<lambda::plural> > plural_forms_;
741                 domains_map_type domains_;
742 
743                 std::string locale_encoding_;
744                 std::string key_encoding_;
745                 bool key_conversion_required_;
746             };
747 
748             template<>
create_messages_facet(messages_info const & info)749             message_format<char> *create_messages_facet(messages_info const &info)
750             {
751                 return new mo_message<char>(info);
752             }
753 
754             template<>
create_messages_facet(messages_info const & info)755             message_format<wchar_t> *create_messages_facet(messages_info const &info)
756             {
757                 return new mo_message<wchar_t>(info);
758             }
759 
760             #ifdef BOOST_LOCALE_ENABLE_CHAR16_T
761 
762             template<>
create_messages_facet(messages_info const & info)763             message_format<char16_t> *create_messages_facet(messages_info const &info)
764             {
765                 return new mo_message<char16_t>(info);
766             }
767             #endif
768 
769             #ifdef BOOST_LOCALE_ENABLE_CHAR32_T
770 
771             template<>
create_messages_facet(messages_info const & info)772             message_format<char32_t> *create_messages_facet(messages_info const &info)
773             {
774                 return new mo_message<char32_t>(info);
775             }
776             #endif
777 
778 
779         } /// gnu_gettext
780 
781     } // locale
782 } // boost
783 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
784 
785