/////////////////////////////////////////////////////////////////////////////
// Name: cpp/streams.cpp
// Purpose: implementation for streams.h
// Author: Mattia Barbon
// Modified by:
// Created: 30/03/2001
// RCS-ID: $Id: streams.cpp 3128 2011-11-21 20:54:27Z mdootson $
// Copyright: (c) 2001-2002, 2004, 2006-2007, 2009 Mattia Barbon
// Licence: This program is free software; you can redistribute it and/or
// modify it under the same terms as Perl itself
/////////////////////////////////////////////////////////////////////////////
#if WXPERL_W_VERSION_GE( 2, 5, 3 )
typedef wxFileOffset wxPliFileOffset;
#else
typedef off_t wxPliFileOffset;
#endif
// thread KO
const char sub_read[] = "sub { read $_[0], $_[1], $_[2] }";
const char sub_seek[] = "sub { seek $_[0], $_[1], $_[2]; tell $_[0] }";
const char sub_tell[] = "sub { tell $_[0] }";
const char sub_write[] = "sub { print { $_[0] } $_[1] }";
// for an open filehandle to a real file, fileno returns a filenumber
// and we use stat to get the file length.
// If fileno returns undef, $fh may be a scalar tied via IO::Scalar
// or IO::String. We test for that and return length of tied scalar.
// If fileno returns -1, then $fh maybe a handle provided by PerlIO
// scalar layer so we see if we can get the length with seek & tell
const char sub_length[] = "sub { \
local $@; \
my $rval = -1; \
my $fn = eval { fileno( $_[0] ) }; \
if( !defined($fn) ) { \
eval { \
if( $_[0]->can('sref') ) { \
use bytes; \
$rval = length( ${ $_[0]->sref } ); \
} \
}; \
} elsif( $fn != -1 ) { \
$rval = (stat $_[0])[7]; \
} else { \
eval { \
my $curpos = tell($_[0]); \
if( ( $curpos != -1) && seek($_[0],0,2) ) { \
$rval = tell($_[0]); \
seek($_[0],$curpos,0); \
} \
}; \
} \
return $rval; \
}";
SV* sg_read;
SV* sg_seek;
SV* sg_tell;
SV* sg_write;
SV* sg_length;
class wxPliStreamInitializer
{
public:
wxPliStreamInitializer()
{
dTHX;
sg_read = eval_pv( CHAR_P sub_read, 1 );
sg_seek = eval_pv( CHAR_P sub_seek, 1 );
sg_tell = eval_pv( CHAR_P sub_tell, 1 );
sg_write = eval_pv( CHAR_P sub_write, 1 );
sg_length = eval_pv( CHAR_P sub_length, 1 );
SvREFCNT_inc( sg_read );
SvREFCNT_inc( sg_seek );
SvREFCNT_inc( sg_tell );
SvREFCNT_inc( sg_write );
SvREFCNT_inc( sg_length );
}
~wxPliStreamInitializer()
{
// SvREFCNT_dec( sg_read );
// SvREFCNT_dec( sg_seek );
// SvREFCNT_dec( sg_tell );
// SvREFCNT_dec( sg_write );
}
};
wxPliStreamInitializer dummy;
// helpers
wxPliFileOffset stream_seek( wxStreamBase* stream, SV* fh, wxPliFileOffset seek, wxSeekMode mode );
wxPliFileOffset stream_tell( const wxStreamBase* stream, SV* fh );
wxPliFileOffset stream_length( const wxStreamBase* stream, SV* fh );
// input stream
wxPliInputStream* wxPliInputStream_ctor( SV* sv )
{
return new wxPliInputStream( sv );
}
wxPliOutputStream* wxPliOutputStream_ctor( SV* sv )
{
return new wxPliOutputStream( sv );
}
wxPliInputStream::wxPliInputStream( SV* fh )
:m_fh( fh )
{
dTHX;
SvREFCNT_inc( m_fh );
}
wxPliInputStream::wxPliInputStream( const wxPliInputStream& stream )
:m_fh( stream.m_fh )
{
dTHX;
SvREFCNT_inc( m_fh );
}
const wxPliInputStream& wxPliInputStream::operator =
( const wxPliInputStream& stream )
{
dTHX;
if( m_fh ) SvREFCNT_dec( m_fh );
m_fh = stream.m_fh;
SvREFCNT_inc( m_fh );
return *this;
}
wxPliInputStream::~wxPliInputStream()
{
dTHX;
SvREFCNT_dec( m_fh );
}
size_t wxPliInputStream::OnSysRead( void* buffer, size_t size )
{
//FIXME// need a ( safe ) way to: create an SV, set ( NOT copy )
//FIXME// buffer into it, then call sg_read
dTHX;
dSP;
ENTER;
SAVETMPS;
SV* target = sv_2mortal( newSVsv( &PL_sv_undef ) );
PUSHMARK( SP );
XPUSHs( m_fh );
XPUSHs( target );
XPUSHs( sv_2mortal( newSVuv( size ) ) );
PUTBACK;
call_sv( sg_read, G_SCALAR );
SPAGAIN;
SV* sv_read_count = POPs;
size_t read_count = 0;
m_lasterror = wxSTREAM_NO_ERROR;
if( !SvOK( sv_read_count ) )
m_lasterror = wxSTREAM_READ_ERROR;
else
{
read_count = SvOK( target ) ? SvUV( sv_read_count ) : 0;
if( !read_count )
m_lasterror = wxSTREAM_EOF;
}
PUTBACK;
if( read_count )
memcpy( buffer, SvPV_nolen( target ), read_count );
FREETMPS;
LEAVE;
return read_count;
}
wxPliFileOffset wxPliInputStream::OnSysSeek( wxPliFileOffset seek, wxSeekMode mode )
{
return stream_seek( this, m_fh, seek, mode );
}
wxPliFileOffset wxPliInputStream::OnSysTell() const
{
return stream_tell( this, m_fh );
}
wxFileOffset wxPliInputStream::GetLength() const
{
return stream_length( this, m_fh );
}
size_t wxPliInputStream::GetSize() const
{
wxFileOffset length = stream_length( this, m_fh );
if ( length == (wxFileOffset)wxInvalidOffset )
return 0;
const size_t len = wx_truncate_cast(size_t, length);
return len;
}
// output stream
wxPliOutputStream::wxPliOutputStream( SV* fh )
:m_fh( fh )
{
dTHX;
SvREFCNT_inc( m_fh );
}
wxPliOutputStream::wxPliOutputStream( const wxPliOutputStream& stream )
:m_fh( stream.m_fh )
{
dTHX;
SvREFCNT_inc( m_fh );
}
const wxPliOutputStream& wxPliOutputStream::operator =
( const wxPliOutputStream& stream )
{
dTHX;
if( m_fh ) SvREFCNT_dec( m_fh );
m_fh = stream.m_fh;
SvREFCNT_inc( m_fh );
return *this;
}
wxPliOutputStream::~wxPliOutputStream()
{
dTHX;
SvREFCNT_dec( m_fh );
}
size_t wxPliOutputStream::OnSysWrite( const void* buffer, size_t size )
{
//FIXME// need a ( safe ) way to: create an SV, set ( NOT copy )
//FIXME// buffer into it, then call sg_write
// printf( "OnSysWrite: %x %d = ", buffer, size );
dTHX;
dSP;
ENTER;
SAVETMPS;
SV* target = sv_2mortal( newSVpvn( CHAR_P ( const char*)buffer, size ) );
PUSHMARK( SP );
XPUSHs( m_fh );
XPUSHs( target );
XPUSHs( sv_2mortal( newSVuv( size ) ) );
PUTBACK;
call_sv( sg_write, G_SCALAR );
SPAGAIN;
SV* sv_write_count = POPs;
size_t write_count = 0;
m_lasterror = wxSTREAM_NO_ERROR;
if( !SvOK( sv_write_count ) )
m_lasterror = wxSTREAM_WRITE_ERROR;
else
{
write_count = SvUV( sv_write_count );
}
PUTBACK;
FREETMPS;
LEAVE;
// printf( "%d\n", write_count );fflush( stdout );
// OnSysSeek( 0, wxFromCurrent );
return write_count;
}
wxPliFileOffset wxPliOutputStream::OnSysSeek( wxPliFileOffset seek, wxSeekMode mode )
{
return stream_seek( this, m_fh, seek, mode );
}
wxPliFileOffset wxPliOutputStream::OnSysTell() const
{
return stream_tell( this, m_fh );
}
wxFileOffset wxPliOutputStream::GetLength() const
{
return stream_length( this, m_fh );
}
size_t wxPliOutputStream::GetSize() const
{
wxFileOffset length = stream_length( this, m_fh );
if ( length == (wxFileOffset)wxInvalidOffset )
return 0;
const size_t len = wx_truncate_cast(size_t, length);
return len;
}
// helpers
wxPliFileOffset stream_seek( wxStreamBase* stream, SV* fh, wxPliFileOffset seek, wxSeekMode mode )
{
IV pl_act;
switch( mode )
{
case wxFromStart:
pl_act = 0;
break;
case wxFromCurrent:
pl_act = 1;
break;
case wxFromEnd:
pl_act = 2;
break;
default:
return -1;
}
dTHX;
dSP;
ENTER;
SAVETMPS;
PUSHMARK( SP );
XPUSHs( fh );
XPUSHs( sv_2mortal( newSViv( seek ) ) );
XPUSHs( sv_2mortal( newSViv( pl_act ) ) );
PUTBACK;
call_sv( sg_seek, G_SCALAR );
SPAGAIN;
IV ret = POPi;
PUTBACK;
FREETMPS;
LEAVE;
return ret;
}
wxPliFileOffset stream_tell( const wxStreamBase* stream, SV* fh )
{
dTHX;
dSP;
ENTER;
SAVETMPS;
PUSHMARK( SP );
XPUSHs( fh );
PUTBACK;
call_sv( sg_tell, G_SCALAR );
SPAGAIN;
IV ret = POPi;
PUTBACK;
FREETMPS;
LEAVE;
return ret;
}
wxPliFileOffset stream_length( const wxStreamBase* stream, SV* fh )
{
dTHX;
dSP;
ENTER;
SAVETMPS;
PUSHMARK( SP );
XPUSHs( fh );
PUTBACK;
call_sv( sg_length, G_SCALAR );
SPAGAIN;
IV ret = POPi;
PUTBACK;
FREETMPS;
LEAVE;
return ret == -1 ? ~0 : ret;
}