Replaced std::auto_ptr<> with std::unique_ptr<>
[supertux.git] / external / tinygettext / tinygettext / iconv.cpp
1 //  tinygettext - A gettext replacement that works directly on .po files
2 //  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmx.de>
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 2
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17
18 #include <ctype.h>
19 #include <assert.h>
20 #include <sstream>
21 #include <errno.h>
22 #include <stdexcept>
23 #include <string.h>
24 #include <stdlib.h>
25
26 #include "iconv.hpp"
27 #include "log_stream.hpp"
28
29 namespace tinygettext {
30
31 #ifndef tinygettext_ICONV_CONST
32 #  define tinygettext_ICONV_CONST 
33 #endif
34
35 IConv::IConv() 
36   : to_charset(),
37     from_charset(),
38     cd(0)
39 {}
40  
41 IConv::IConv(const std::string& from_charset_, const std::string& to_charset_)
42   : to_charset(),
43     from_charset(),
44     cd(0)
45 {
46   set_charsets(from_charset_, to_charset_);
47 }
48  
49 IConv::~IConv()
50 {
51   if (cd)
52     tinygettext_iconv_close(cd);
53 }
54  
55 void
56 IConv::set_charsets(const std::string& from_charset_, const std::string& to_charset_)
57 {
58   if (cd)
59     tinygettext_iconv_close(cd);
60
61   from_charset = from_charset_;
62   to_charset   = to_charset_;
63
64   for(std::string::iterator i = to_charset.begin(); i != to_charset.end(); ++i)
65     *i = static_cast<char>(toupper(*i));
66
67   for(std::string::iterator i = from_charset.begin(); i != from_charset.end(); ++i)
68     *i = static_cast<char>(toupper(*i));
69
70   if (to_charset == from_charset)
71   {
72     cd = 0;
73   }
74   else
75   {
76     cd = tinygettext_iconv_open(to_charset.c_str(), from_charset.c_str());
77     if (cd == reinterpret_cast<tinygettext_iconv_t>(-1))
78     {
79       if(errno == EINVAL)
80       {
81         std::ostringstream str;
82         str << "IConv construction failed: conversion from '" << from_charset
83             << "' to '" << to_charset << "' not available";
84         throw std::runtime_error(str.str());
85       }
86       else
87       {
88         std::ostringstream str;
89         str << "IConv: construction failed: " << strerror(errno);
90         throw std::runtime_error(str.str());
91       }
92     }
93   }
94 }
95
96 /// Convert a string from encoding to another.
97 std::string
98 IConv::convert(const std::string& text)
99 {
100   if (!cd)
101   {
102     return text;
103   }
104   else
105   {
106     size_t inbytesleft  = text.size();
107     size_t outbytesleft = 4*inbytesleft; // Worst case scenario: ASCII -> UTF-32?
108
109     // We try to avoid to much copying around, so we write directly into
110     // a std::string
111     tinygettext_ICONV_CONST char* inbuf = const_cast<char*>(&text[0]);
112     std::string result(outbytesleft, 'X');
113     char* outbuf = &result[0]; 
114   
115     // Try to convert the text.
116     size_t ret = tinygettext_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
117     if (ret == static_cast<size_t>(-1))
118     {
119       if (errno == EILSEQ || errno == EINVAL)
120       { // invalid multibyte sequence
121         tinygettext_iconv(cd, NULL, NULL, NULL, NULL); // reset state
122
123         // FIXME: Could try to skip the invalid byte and continue
124         log_error << "error: tinygettext:iconv: invalid multibyte sequence in:  \"" << text << "\"" << std::endl;
125       }
126       else if (errno == E2BIG)
127       { // output buffer to small
128         assert(!"tinygettext/iconv.cpp: E2BIG: This should never be reached");
129       }
130       else if (errno == EBADF)
131       {
132         assert(!"tinygettext/iconv.cpp: EBADF: This should never be reached");
133       }
134       else
135       {
136         assert(!"tinygettext/iconv.cpp: <unknown>: This should never be reached");
137       }
138     }
139
140     result.resize(4*text.size() - outbytesleft);
141
142     return result;
143   }
144 }
145
146 } // namespace tinygettext
147
148 /* EOF */