/* ====================================================================
* 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 <istream>
// apr util
#include <apr_xlate.h>
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
syntax highlighted by Code2HTML, v. 0.9.1