You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
15 KiB
485 lines
15 KiB
/* This file is part of the KDE libraries
|
|
Copyright (C) 2000 David Faure <faure@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "kfilterdev.h"
|
|
#include "kfilterbase.h"
|
|
#include <kdebug.h>
|
|
#include <stdio.h> // for EOF
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <tqfile.h>
|
|
|
|
#define BUFFER_SIZE 8*1024
|
|
|
|
class KFilterDev::KFilterDevPrivate
|
|
{
|
|
public:
|
|
KFilterDevPrivate() : bNeedHeader(true), bSkipHeaders(false),
|
|
autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false),
|
|
bIgnoreData(false){}
|
|
bool bNeedHeader;
|
|
bool bSkipHeaders;
|
|
bool autoDeleteFilterBase;
|
|
bool bOpenedUnderlyingDevice;
|
|
bool bIgnoreData;
|
|
TQByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
|
|
TQCString ungetchBuffer;
|
|
TQCString origFileName;
|
|
KFilterBase::Result result;
|
|
};
|
|
|
|
KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase )
|
|
: filter(_filter)
|
|
{
|
|
assert(filter);
|
|
d = new KFilterDevPrivate;
|
|
d->autoDeleteFilterBase = autoDeleteFilterBase;
|
|
}
|
|
|
|
KFilterDev::~KFilterDev()
|
|
{
|
|
if ( isOpen() )
|
|
close();
|
|
if ( d->autoDeleteFilterBase )
|
|
delete filter;
|
|
delete d;
|
|
}
|
|
|
|
#ifndef KDE_NO_COMPAT
|
|
//this one is static
|
|
// Cumbersome API. To be removed in KDE 3.0.
|
|
TQIODevice* KFilterDev::createFilterDevice(KFilterBase* base, TQFile* file)
|
|
{
|
|
if (file==0)
|
|
return 0;
|
|
|
|
//we don't need a filter
|
|
if (base==0)
|
|
return TQT_TQIODEVICE(new TQFile(file->name())); // A bit strange IMHO. We ask for a TQFile but we create another one !?! (DF)
|
|
|
|
base->setDevice(TQT_TQIODEVICE(file));
|
|
return new KFilterDev(base);
|
|
}
|
|
#endif
|
|
|
|
//static
|
|
TQIODevice * KFilterDev::deviceForFile( const TQString & fileName, const TQString & mimetype,
|
|
bool forceFilter )
|
|
{
|
|
TQFile * f = new TQFile( fileName );
|
|
KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName )
|
|
: KFilterBase::findFilterByMimeType( mimetype );
|
|
if ( base )
|
|
{
|
|
base->setDevice(TQT_TQIODEVICE(f), true);
|
|
return new KFilterDev(base, true);
|
|
}
|
|
if(!forceFilter)
|
|
return TQT_TQIODEVICE(f);
|
|
else
|
|
{
|
|
delete f;
|
|
return 0L;
|
|
}
|
|
}
|
|
|
|
TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & mimetype)
|
|
{
|
|
return device( inDevice, mimetype, true );
|
|
}
|
|
|
|
TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & mimetype, bool autoDeleteInDevice )
|
|
{
|
|
if (inDevice==0)
|
|
return 0;
|
|
KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype);
|
|
if ( base )
|
|
{
|
|
base->setDevice(inDevice, autoDeleteInDevice);
|
|
return new KFilterDev(base, true /* auto-delete "base" */);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool KFilterDev::open( TQ_OpenMode mode )
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::open " << mode << endl;
|
|
if ( mode == IO_ReadOnly )
|
|
{
|
|
d->buffer.resize(0);
|
|
d->ungetchBuffer.resize(0);
|
|
}
|
|
else
|
|
{
|
|
d->buffer.resize( BUFFER_SIZE );
|
|
filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
|
|
}
|
|
d->bNeedHeader = !d->bSkipHeaders;
|
|
filter->init( mode );
|
|
d->bOpenedUnderlyingDevice = !filter->device()->isOpen();
|
|
bool ret = d->bOpenedUnderlyingDevice ? filter->device()->open( (TQ_OpenMode)mode ) : true;
|
|
d->result = KFilterBase::OK;
|
|
|
|
if ( !ret )
|
|
kdWarning(7005) << "KFilterDev::open: Couldn't open underlying device" << endl;
|
|
else
|
|
{
|
|
setState( IO_Open );
|
|
setMode( mode );
|
|
}
|
|
TQIODevice::at(0);
|
|
return ret;
|
|
}
|
|
|
|
void KFilterDev::close()
|
|
{
|
|
if ( !isOpen() )
|
|
return;
|
|
//kdDebug(7005) << "KFilterDev::close" << endl;
|
|
if ( filter->mode() == IO_WriteOnly )
|
|
writeBlock( 0L, 0 ); // finish writing
|
|
//kdDebug(7005) << "KFilterDev::close. Calling terminate()." << endl;
|
|
|
|
filter->terminate();
|
|
if ( d->bOpenedUnderlyingDevice )
|
|
filter->device()->close();
|
|
|
|
setState( 0 ); // not IO_Open
|
|
}
|
|
|
|
void KFilterDev::flush()
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::flush" << endl;
|
|
filter->device()->flush();
|
|
// Hmm, might not be enough...
|
|
}
|
|
|
|
#ifdef USE_QT4
|
|
qint64 KFilterDev::size() const
|
|
#else // USE_QT4
|
|
TQIODevice::Offset KFilterDev::size() const
|
|
#endif // USE_QT4
|
|
{
|
|
// Well, hmm, Houston, we have a problem.
|
|
// We can't know the size of the uncompressed data
|
|
// before uncompressing it.......
|
|
|
|
// But readAll, which is not virtual, needs the size.........
|
|
|
|
kdDebug(7005) << "KFilterDev::size - can't be implemented, returning -1" << endl;
|
|
//abort();
|
|
return (uint)-1;
|
|
}
|
|
|
|
TQIODevice::Offset KFilterDev::at() const
|
|
{
|
|
return TQIODevice::at();
|
|
}
|
|
|
|
bool KFilterDev::at( TQIODevice::Offset pos )
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::at " << pos << " currently at " << TQIODevice::at() << endl;
|
|
|
|
if ( TQIODevice::at() == pos )
|
|
return true;
|
|
|
|
Q_ASSERT ( filter->mode() == IO_ReadOnly );
|
|
|
|
if ( pos == 0 )
|
|
{
|
|
TQIODevice::at(0);
|
|
// We can forget about the cached data
|
|
d->ungetchBuffer.resize(0);
|
|
d->bNeedHeader = !d->bSkipHeaders;
|
|
d->result = KFilterBase::OK;
|
|
filter->setInBuffer(0L,0);
|
|
filter->reset();
|
|
return filter->device()->reset();
|
|
}
|
|
|
|
if ( TQIODevice::at() < pos ) // we can start from here
|
|
pos = pos - TQIODevice::at();
|
|
else
|
|
{
|
|
// we have to start from 0 ! Ugly and slow, but better than the previous
|
|
// solution (KTarGz was allocating everything into memory)
|
|
if (!at(0)) // sets ioIndex to 0
|
|
return false;
|
|
}
|
|
|
|
//kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl;
|
|
TQByteArray dummy( TQMIN( pos, 3*BUFFER_SIZE ) );
|
|
d->bIgnoreData = true;
|
|
bool result = ( (TQIODevice::Offset)readBlock( dummy.data(), pos ) == pos );
|
|
d->bIgnoreData = false;
|
|
return result;
|
|
}
|
|
|
|
bool KFilterDev::atEnd() const
|
|
{
|
|
return filter->device()->atEnd() && (d->result == KFilterBase::END)
|
|
&& d->ungetchBuffer.isEmpty();
|
|
}
|
|
|
|
TQT_TQIO_LONG KFilterDev::tqreadBlock( char *data, TQT_TQIO_ULONG maxlen )
|
|
{
|
|
Q_ASSERT ( filter->mode() == IO_ReadOnly );
|
|
//kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl;
|
|
|
|
uint dataReceived = 0;
|
|
if ( !d->ungetchBuffer.isEmpty() )
|
|
{
|
|
uint len = d->ungetchBuffer.length();
|
|
if ( !d->bIgnoreData )
|
|
{
|
|
while ( ( dataReceived < len ) && ( dataReceived < maxlen ) )
|
|
{
|
|
*data = d->ungetchBuffer[ len - dataReceived - 1 ];
|
|
data++;
|
|
dataReceived++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dataReceived = TQMIN( len, maxlen );
|
|
}
|
|
d->ungetchBuffer.truncate( len - dataReceived );
|
|
TQIODevice::at(TQIODevice::at() + dataReceived);
|
|
}
|
|
|
|
// If we came to the end of the stream
|
|
// return what we got from the ungetchBuffer.
|
|
if ( d->result == KFilterBase::END )
|
|
return dataReceived;
|
|
|
|
// If we had an error, return -1.
|
|
if ( d->result != KFilterBase::OK )
|
|
return -1;
|
|
|
|
|
|
TQ_ULONG outBufferSize;
|
|
if ( d->bIgnoreData )
|
|
{
|
|
outBufferSize = TQMIN( maxlen, 3*BUFFER_SIZE );
|
|
}
|
|
else
|
|
{
|
|
outBufferSize = maxlen;
|
|
}
|
|
outBufferSize -= dataReceived;
|
|
TQ_ULONG availOut = outBufferSize;
|
|
filter->setOutBuffer( data, outBufferSize );
|
|
|
|
bool decompressedAll = false;
|
|
while ( dataReceived < maxlen )
|
|
{
|
|
if (filter->inBufferEmpty())
|
|
{
|
|
// Not sure about the best size to set there.
|
|
// For sure, it should be bigger than the header size (see comment in readHeader)
|
|
d->buffer.resize( BUFFER_SIZE );
|
|
// Request data from underlying device
|
|
int size = filter->device()->readBlock( d->buffer.data(),
|
|
d->buffer.size() );
|
|
if ( size )
|
|
filter->setInBuffer( d->buffer.data(), size );
|
|
else {
|
|
if ( decompressedAll )
|
|
{
|
|
// We decoded everything there was to decode. So -> done.
|
|
//kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl;
|
|
d->result = KFilterBase::END;
|
|
break;
|
|
}
|
|
}
|
|
//kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl;
|
|
}
|
|
if (d->bNeedHeader)
|
|
{
|
|
(void) filter->readHeader();
|
|
d->bNeedHeader = false;
|
|
}
|
|
|
|
d->result = filter->uncompress();
|
|
|
|
if (d->result == KFilterBase::ERROR)
|
|
{
|
|
kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl;
|
|
break;
|
|
}
|
|
|
|
// We got that much data since the last time we went here
|
|
uint outReceived = availOut - filter->outBufferAvailable();
|
|
//kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl;
|
|
if( availOut < (uint)filter->outBufferAvailable() )
|
|
kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl;
|
|
|
|
dataReceived += outReceived;
|
|
if ( !d->bIgnoreData ) // Move on in the output buffer
|
|
{
|
|
data += outReceived;
|
|
availOut = maxlen - dataReceived;
|
|
}
|
|
else if ( maxlen - dataReceived < outBufferSize )
|
|
{
|
|
availOut = maxlen - dataReceived;
|
|
}
|
|
TQIODevice::at(TQIODevice::at() + outReceived);
|
|
if (d->result == KFilterBase::END)
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl;
|
|
break; // Finished.
|
|
}
|
|
if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 )
|
|
{
|
|
decompressedAll = true;
|
|
}
|
|
filter->setOutBuffer( data, availOut );
|
|
}
|
|
|
|
return dataReceived;
|
|
}
|
|
|
|
TQT_TQIO_LONG KFilterDev::tqwriteBlock( const char *data /*0 to finish*/, TQT_TQIO_ULONG len )
|
|
{
|
|
Q_ASSERT ( filter->mode() == IO_WriteOnly );
|
|
// If we had an error, return 0.
|
|
if ( d->result != KFilterBase::OK )
|
|
return 0;
|
|
|
|
bool finish = (data == 0L);
|
|
if (!finish)
|
|
{
|
|
filter->setInBuffer( data, len );
|
|
if (d->bNeedHeader)
|
|
{
|
|
(void)filter->writeHeader( d->origFileName );
|
|
d->bNeedHeader = false;
|
|
}
|
|
}
|
|
|
|
uint dataWritten = 0;
|
|
uint availIn = len;
|
|
while ( dataWritten < len || finish )
|
|
{
|
|
|
|
d->result = filter->compress( finish );
|
|
|
|
if (d->result == KFilterBase::ERROR)
|
|
{
|
|
kdWarning(7005) << "KFilterDev: Error when compressing data" << endl;
|
|
// What to do ?
|
|
break;
|
|
}
|
|
|
|
// Wrote everything ?
|
|
if (filter->inBufferEmpty() || (d->result == KFilterBase::END))
|
|
{
|
|
// We got that much data since the last time we went here
|
|
uint wrote = availIn - filter->inBufferAvailable();
|
|
|
|
//kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl;
|
|
|
|
// Move on in the input buffer
|
|
data += wrote;
|
|
dataWritten += wrote;
|
|
TQIODevice::at(TQIODevice::at() + wrote);
|
|
|
|
availIn = len - dataWritten;
|
|
//kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl;
|
|
if ( availIn > 0 ) // Not sure this will ever happen
|
|
filter->setInBuffer( data, availIn );
|
|
}
|
|
|
|
if (filter->outBufferFull() || (d->result == KFilterBase::END))
|
|
{
|
|
//kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl;
|
|
int towrite = d->buffer.size() - filter->outBufferAvailable();
|
|
if ( towrite > 0 )
|
|
{
|
|
// Write compressed data to underlying device
|
|
int size = filter->device()->writeBlock( d->buffer.data(), towrite );
|
|
if ( size != towrite ) {
|
|
kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl;
|
|
return 0; // indicate an error (happens on disk full)
|
|
}
|
|
//else
|
|
//kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl;
|
|
}
|
|
d->buffer.resize( 8*1024 );
|
|
filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
|
|
if (d->result == KFilterBase::END)
|
|
{
|
|
//kdDebug(7005) << " KFilterDev::writeBlock END" << endl;
|
|
Q_ASSERT(finish); // hopefully we don't get end before finishing
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dataWritten;
|
|
}
|
|
|
|
int KFilterDev::getch()
|
|
{
|
|
Q_ASSERT ( filter->mode() == IO_ReadOnly );
|
|
//kdDebug(7005) << "KFilterDev::getch" << endl;
|
|
if ( !d->ungetchBuffer.isEmpty() ) {
|
|
int len = d->ungetchBuffer.length();
|
|
int ch = d->ungetchBuffer[ len-1 ];
|
|
d->ungetchBuffer.truncate( len - 1 );
|
|
TQIODevice::at(TQIODevice::at() + 1);
|
|
//kdDebug(7005) << "KFilterDev::getch from ungetch: " << TQString(TQChar(ch)) << endl;
|
|
return ch;
|
|
}
|
|
char buf[1];
|
|
int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF;
|
|
//kdDebug(7005) << "KFilterDev::getch ret=" << TQString(TQChar(ret)) << endl;
|
|
return ret;
|
|
}
|
|
|
|
int KFilterDev::putch( int c )
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::putch" << endl;
|
|
char buf[1];
|
|
buf[0] = c;
|
|
return writeBlock( buf, 1 ) == 1 ? c : -1;
|
|
}
|
|
|
|
int KFilterDev::ungetch( int ch )
|
|
{
|
|
//kdDebug(7005) << "KFilterDev::ungetch " << TQString(TQChar(ch)) << endl;
|
|
if ( ch == EOF ) // cannot unget EOF
|
|
return ch;
|
|
|
|
// pipe or similar => we cannot ungetch, so do it manually
|
|
d->ungetchBuffer +=ch;
|
|
TQIODevice::at(TQIODevice::at() - 1);
|
|
return ch;
|
|
}
|
|
|
|
void KFilterDev::setOrigFileName( const TQCString & fileName )
|
|
{
|
|
d->origFileName = fileName;
|
|
}
|
|
|
|
void KFilterDev::setSkipHeaders()
|
|
{
|
|
d->bSkipHeaders = true;
|
|
}
|