/* ==================================================================== * Copyright (c) 2003-2006, Martin Hauner * http://subcommander.tigris.org * * Subcommander is licensed as described in the file doc/COPYING, which * you should have received as part of this distribution. * ==================================================================== */ #ifndef _ICONV_STREAM_H #define _ICONV_STREAM_H // sc #include "types.h" #include "apr.h" #include "ErrorCodes.h" // sys #include // apr util #include class iconvstreambuf : public std::streambuf { static const sc::Size CSize = 2052; static const sc::Size CPutBack = 4; public: iconvstreambuf( std::istream& input, const char* cpIn, const char* cpOut ) : _in(input), _inoff(0), _pool(0), _xlate(0), _error(0) { apr_status_t status = apr_pool_create( &_pool, 0 ); if( status != APR_SUCCESS ) { _error = createErrorApr(status); return; } const char* cpIn2 = cpIn; const char* cpOut2 = cpOut; if( *cpIn2 == '*' ) cpIn2 = APR_LOCALE_CHARSET; if( *cpOut2 == '*' ) cpOut2 = APR_LOCALE_CHARSET; status = apr_xlate_open( &_xlate, cpOut2, cpIn2, _pool ); if( status != APR_SUCCESS ) { sc::String msg = sc::strError(sc::ErrEncoding); msg += " ("; msg += cpIn; msg += "/"; msg += cpOut; msg += ")"; _error = createError( sc::ErrEncoding, msg, createErrorApr(status) ); return; } setg( _buf+CPutBack, _buf+CPutBack, _buf+CPutBack ); } ~iconvstreambuf() { if( _xlate ) { apr_status_t status = apr_xlate_close(_xlate); if( status != APR_SUCCESS ) { // nothing we could do here.. } } if( _pool ) { apr_pool_destroy(_pool); } } public: bool ok() const { return _error == sc::Success; } const sc::Error* getError() const { return _error; } void setParent( std::istream* parent ) { _parent = parent; } protected: int_type underflow() { // adjust put back buffer int pb = (int)(gptr() - eback()); if( pb > (int)CPutBack ) { pb = CPutBack; } ::memmove( _buf+(CPutBack-pb), gptr()-pb, pb ); // read from input _in.read( _inbuf+_inoff, (std::streamsize)(CSize-CPutBack-_inoff) ); sc::Size got = _in.gcount() + _inoff; // we didn't read anything and we don't have anything (left) in _inbuf? if( _in.gcount() == 0 && _inoff == 0 ) { return EOF; } // before the xlate call this is set to the size of the _inbuf (src) // and _buf (dst) // after the xlate call this contains the unused bytes in _inbuf (src) // and _buf (dst) apr_size_t srcLen = got; apr_size_t dstLen = CSize-CPutBack; // this fails in most cases, because the xlate won't fit byte-to-byte // into _buf, so we ignore the result and check the returned xLen values // instead. apr_status_t status = apr_xlate_conv_buffer( _xlate, _inbuf, &srcLen, _buf+CPutBack, &dstLen ); ::memmove( _inbuf, _inbuf+got-srcLen, srcLen ); _inoff = srcLen; // nothing xlate'd? that looks wrong if( srcLen == got ) { _error = createErrorApr(status); _parent->setstate( std::ios::badbit ); return EOF; } setg( _buf+(CPutBack-pb), _buf+CPutBack, _buf+CPutBack + (CSize-CPutBack-dstLen) ); return (unsigned char)(*gptr()); } private: std::istream& _in; std::istream* _parent; char _buf[CSize]; // target for xlated characters char _inbuf[CSize-CPutBack]; // source of xlated characters sc::Size _inoff; apr_pool_t* _pool; apr_xlate_t* _xlate; const sc::Error* _error; }; ///////////////////////////////////////////////////////////////////// class iconvstreambuf2 : public std::streambuf { static const int CSize = 5; public: iconvstreambuf2( std::ostream& output, const char* cpIn, const char* cpOut ) : _out(output), /*_outoff(0),*/ _pool(0), _xlate(0), _error(0) { _c1 = _c2 = _c3 = 0; apr_status_t status = apr_pool_create( &_pool, 0 ); if( status != APR_SUCCESS ) { _error = createErrorApr(status); return; } if( *cpIn == '*' ) cpIn = APR_LOCALE_CHARSET; if( *cpOut == '*' ) cpOut = APR_LOCALE_CHARSET; status = apr_xlate_open( &_xlate, cpOut, cpIn, _pool ); if( status != APR_SUCCESS ) { sc::String msg = sc::strError(sc::ErrEncoding); msg += " (*, locale charset)"; _error = createError( sc::ErrEncoding, msg, createErrorApr(status) ); return; } setp( _buf, _buf+CSize-1 ); } ~iconvstreambuf2() { sync(); if( _xlate ) { apr_status_t status = apr_xlate_close(_xlate); if( status != APR_SUCCESS ) { // nothing we could do here.. } } if( _pool ) { apr_pool_destroy(_pool); } } public: bool ok() const { return _error == sc::Success; } const sc::Error* getError() const { return _error; } protected: int_type overflow( int_type c ) { if( c != EOF ) { // buffer c *pptr() = c; pbump(1); } if( flush() == EOF ) { return EOF; } return c; } int sync() { if( flush() == EOF ) { return -1; } return 0; } int flush() { sc::Size write = pptr() - pbase(); apr_size_t srcLen = write; apr_size_t dstLen = CSize; // this fails in most cases, because the xlate won't fit byte-to-byte apr_status_t status = apr_xlate_conv_buffer( _xlate, _buf, &srcLen, _outbuf, &dstLen ); if( status != APR_SUCCESS ) { // so we ignore the result and check the returned xLen values instead. } // check failure, return EOF if( dstLen == (apr_size_t)CSize ) { //_error = createAprError(status); return EOF; } _out.write( _outbuf, (std::streamsize)(CSize-dstLen) ); // copy anything we didn't write to the beginning of the buffer ::memmove( _buf, _buf+write-srcLen, srcLen ); pbump(-(int)(write-srcLen)); return (int)(CSize-dstLen); } private: char _c1; char _buf[CSize]; char _c2; char _outbuf[CSize]; char _c3; std::ostream& _out; apr_pool_t* _pool; apr_xlate_t* _xlate; const sc::Error* _error; }; ///////////////////////////////////////////////////////////////////// class iconvistream : public std::istream { public: iconvistream( std::istream& in, const char* cpIn, const char* cpOut ) : std::istream(0), _buf(in,cpIn,cpOut) { // required to propagate errors _buf.setParent(this); // iconv setup was succesfull? if( ! _buf.ok() ) { //no setstate( std::ios::badbit ); } // because _buf is a member it is initialized after std::istream! // add _buf to the stream after it is initialized. init(&_buf); } const sc::Error* getError() const { return _buf.getError(); } iconvstreambuf _buf; }; ///////////////////////////////////////////////////////////////////// class ostreambuf { protected: ostreambuf( std::ostream& out, const char* cpIn, const char* cpOut ) : _buf(out,cpIn,cpOut) {} iconvstreambuf2 _buf; }; class iconvostream : private ostreambuf, public std::ostream { public: iconvostream( std::ostream& out, const char* cpIn, const char* cpOut ) : ostreambuf(out,cpIn,cpOut), std::ostream(&_buf) { if( ! _buf.ok() ) { setstate( std::ios::badbit ); } } const sc::Error* getError() const { return _buf.getError(); } // if _buf is a member it is initialized after std::ostream! }; #endif // _ICONV_STREAM_H