iconv.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       iconv.cc
00003 ///             iconv wrapper class, and pluggable interface for records
00004 ///
00005 
00006 /*
00007     Copyright (C) 2008-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "iconv.h"
00023 #include "common.h"
00024 #include "error.h"
00025 #include "config.h"
00026 #include <iconv.h>
00027 #include <iostream>
00028 #include <errno.h>
00029 #include <string>
00030 
00031 using namespace std;
00032 
00033 namespace Barry {
00034 
00035 //////////////////////////////////////////////////////////////////////////////
00036 // IConvHandlePrivate class
00037 class IConvHandlePrivate
00038 {
00039 public:
00040         iconv_t m_handle;
00041 
00042         IConvHandlePrivate()
00043         {
00044         }
00045 };
00046 
00047 //////////////////////////////////////////////////////////////////////////////
00048 // IConvHandle class
00049 
00050 IConvHandle::IConvHandle(const char *fromcode,
00051                          const char *tocode,
00052                          bool throw_on_conv_err)
00053         : m_priv( new IConvHandlePrivate )
00054         , m_throw_on_conv_err(throw_on_conv_err)
00055 {
00056         m_priv->m_handle = iconv_open(tocode, fromcode);
00057         if( m_priv->m_handle == (iconv_t)(-1) ) {
00058                 throw ErrnoError(std::string("iconv_open failed: from ") + fromcode + " to " + tocode, errno);
00059         }
00060 }
00061 
00062 IConvHandle::IConvHandle(const char *fromcode,
00063                          const IConverter &ic,
00064                          bool throw_on_conv_err)
00065         : m_priv( new IConvHandlePrivate )
00066         , m_throw_on_conv_err(throw_on_conv_err)
00067 {
00068         m_priv->m_handle = iconv_open(ic.m_tocode.c_str(), fromcode);
00069         if( m_priv->m_handle == (iconv_t)(-1) ) {
00070                 throw ErrnoError(std::string("iconv_open failed: from ") + fromcode + " to " + ic.m_tocode, errno);
00071         }
00072 }
00073 
00074 IConvHandle::IConvHandle(const IConverter &ic,
00075                          const char *tocode,
00076                          bool throw_on_conv_err)
00077         : m_priv( new IConvHandlePrivate )
00078         , m_throw_on_conv_err(throw_on_conv_err)
00079 {
00080         m_priv->m_handle = iconv_open(tocode, ic.m_tocode.c_str());
00081         if( m_priv->m_handle == (iconv_t)(-1) ) {
00082                 throw ErrnoError(std::string("iconv_open failed: from ") + ic.m_tocode + " to " + tocode, errno);
00083         }
00084 }
00085 
00086 IConvHandle::~IConvHandle()
00087 {
00088         iconv_close(m_priv->m_handle);
00089 }
00090 
00091 std::string IConvHandle::Convert(Data &tmp, const std::string &str) const
00092 {
00093         size_t target = str.size() * 2;
00094         char *out = 0, *outstart = 0;
00095         size_t outbytesleft = 0;
00096         std::string ret;
00097         iconv_t cd = m_priv->m_handle;
00098 
00099         // this loop is for the very odd case that the output string
00100         // needs more than twice the input size
00101         for( int tries = 0; ; tries++ ) {
00102 
00103                 const char *in = str.data();
00104                 size_t inbytesleft = str.size();
00105                 out = outstart = (char*) tmp.GetBuffer(target);
00106                 outbytesleft = tmp.GetBufSize();
00107 
00108                 iconv(cd, NULL, NULL, NULL, NULL);      // reset cd's state
00109                 size_t status = iconv(cd, (ICONV_CONST char**) &in, &inbytesleft, &out, &outbytesleft);
00110 
00111                 if( status == (size_t)(-1) ) {
00112                         if( errno == E2BIG && tries < 2 ) {
00113                                 target += inbytesleft * 2;
00114                                 // try again with more memory...
00115                                 continue;
00116                         }
00117 
00118                         // should never happen :-)
00119                         // but if it does, and we get here, check
00120                         // whether the user wants to be notified by
00121                         // exception... if not, just fall through and
00122                         // store as much converted data as possible
00123                         ErrnoError e(string("iconv failed with string '") + str + "'", errno);
00124                         if( m_throw_on_conv_err ) {
00125                                 throw e;
00126                         }
00127                         else {
00128                                 cerr << e.what();
00129                                 // return the unconverted string
00130                                 return str;
00131                         }
00132                 }
00133                 else {
00134                         // success
00135                         break;
00136                 }
00137         }
00138 
00139         // store any available converted data
00140         ret.assign(outstart, out - outstart);
00141         return ret;
00142 }
00143 
00144 
00145 //////////////////////////////////////////////////////////////////////////////
00146 // IConvHandle class
00147 
00148 IConverter::IConverter(const char *tocode, bool throw_on_conv_err)
00149         : m_from(BLACKBERRY_CHARSET, tocode, throw_on_conv_err)
00150         , m_to(tocode, BLACKBERRY_CHARSET, throw_on_conv_err)
00151         , m_tocode(tocode)
00152 {
00153 }
00154 
00155 IConverter::~IConverter()
00156 {
00157 }
00158 
00159 std::string IConverter::FromBB(const std::string &str) const
00160 {
00161         return m_from.Convert(m_buffer, str);
00162 }
00163 
00164 std::string IConverter::ToBB(const std::string &str) const
00165 {
00166         return m_to.Convert(m_buffer, str);
00167 }
00168 
00169 std::string IConverter::Convert(const IConvHandle &custom, const std::string &str) const
00170 {
00171         return custom.Convert(m_buffer, str);
00172 }
00173 
00174 } // namespace Barry
00175