// tinygettext - A gettext replacement that works directly on .po files // Copyright (c) 2009 Ingo Ruhnke // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgement in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. #include "precompiled.h" #include #include #include #include #include #include #include #include "tinygettext/iconv.hpp" #include "tinygettext/log_stream.hpp" namespace tinygettext { IConv::IConv() : to_charset(), from_charset(), cd(nullptr) {} IConv::IConv(const std::string& from_charset_, const std::string& to_charset_) : to_charset(), from_charset(), cd(nullptr) { set_charsets(from_charset_, to_charset_); } IConv::~IConv() { if (cd) iconv_close(cd); } void IConv::set_charsets(const std::string& from_charset_, const std::string& to_charset_) { if (cd) iconv_close(cd); from_charset = from_charset_; to_charset = to_charset_; for(std::string::iterator i = to_charset.begin(); i != to_charset.end(); ++i) *i = static_cast(toupper(*i)); for(std::string::iterator i = from_charset.begin(); i != from_charset.end(); ++i) *i = static_cast(toupper(*i)); if (to_charset == from_charset) { cd = nullptr; } else { cd = iconv_open(to_charset.c_str(), from_charset.c_str()); if (cd == reinterpret_cast(-1)) { if(errno == EINVAL) { std::ostringstream str; str << "IConv construction failed: conversion from '" << from_charset << "' to '" << to_charset << "' not available"; throw std::runtime_error(str.str()); } else { std::ostringstream str; str << "IConv: construction failed: " << strerror(errno); throw std::runtime_error(str.str()); } } } } /// Convert a string from encoding to another. std::string IConv::convert(const std::string& text) { if (!cd) { return text; } else { size_t inbytesleft = text.size(); size_t outbytesleft = 4*inbytesleft; // Worst case scenario: ASCII -> UTF-32? // We try to avoid to much copying around, so we write directly into // a std::string const char* inbuf = &text[0]; std::string result(outbytesleft, 'X'); char* outbuf = &result[0]; // Try to convert the text. size_t ret = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); if (ret == static_cast(-1)) { if (errno == EILSEQ || errno == EINVAL) { // invalid multibyte sequence iconv(cd, nullptr, nullptr, nullptr, nullptr); // reset state // FIXME: Could try to skip the invalid byte and continue log_error << "error: tinygettext:iconv: invalid multibyte sequence in: \"" << text << "\"" << std::endl; } else if (errno == E2BIG) { // output buffer to small assert(false && "tinygettext/iconv.cpp: E2BIG: This should never be reached"); } else if (errno == EBADF) { assert(false && "tinygettext/iconv.cpp: EBADF: This should never be reached"); } else { assert(false && "tinygettext/iconv.cpp: : This should never be reached"); } } result.resize(4*text.size() - outbytesleft); return result; } } } // namespace tinygettext /* EOF */