kio Library: kfilterdev.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kfilterdev.cpp

    00001 /* This file is part of the KDE libraries
    00002    Copyright (C) 2000 David Faure <faure@kde.org>
    00003
    00004    This library is free software; you can redistribute it and/or
    00005    modify it under the terms of the GNU Library General Public
    00006    License version 2 as published by the Free Software Foundation.
    00007
    00008    This library is distributed in the hope that it will be useful,
    00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00011    Library General Public License for more details.
    00012
    00013    You should have received a copy of the GNU Library General Public License
    00014    along with this library; see the file COPYING.LIB.  If not, write to
    00015    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00016    Boston, MA 02111-1307, USA.
    00017 */
    00018
    00019 #include "kfilterdev.h"
    00020 #include "kfilterbase.h"
    00021 #include <kdebug.h>
    00022 #include <stdio.h> // for EOF
    00023 #include <stdlib.h>
    00024 #include <assert.h>
    00025 #include <qfile.h>
    00026
    00027 #define BUFFER_SIZE 8*1024
    00028
    00029 class KFilterDev::KFilterDevPrivate
    00030 {
    00031 public:
    00032     KFilterDevPrivate() : bNeedHeader(true), bSkipHeaders(false),
    00033                           autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false),
    00034                           bIgnoreData(false){}
    00035     bool bNeedHeader;
    00036     bool bSkipHeaders;
    00037     bool autoDeleteFilterBase;
    00038     bool bOpenedUnderlyingDevice;
    00039     bool bIgnoreData;
    00040     QByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
    00041     QCString ungetchBuffer;
    00042     QCString origFileName;
    00043     KFilterBase::Result result;
    00044 };
    00045
    00046 KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase )
    00047     : filter(_filter)
    00048 {
    00049     assert(filter);
    00050     d = new KFilterDevPrivate;
    00051     d->autoDeleteFilterBase = autoDeleteFilterBase;
    00052 }
    00053
    00054 KFilterDev::~KFilterDev()
    00055 {
    00056     if ( isOpen() )
    00057         close();
    00058     if ( d->autoDeleteFilterBase )
    00059         delete filter;
    00060     delete d;
    00061 }
    00062
    00063 #ifndef KDE_NO_COMPAT
    00064 //this one is static
    00065 // Cumbersome API. To be removed in KDE 3.0.
    00066 QIODevice* KFilterDev::createFilterDevice(KFilterBase* base, QFile* file)
    00067 {
    00068    if (file==0)
    00069       return 0;
    00070
    00071    //we don't need a filter
    00072    if (base==0)
    00073        return new QFile(file->name()); // A bit strange IMHO. We ask for a QFile but we create another one !?! (DF)
    00074
    00075    base->setDevice(file);
    00076    return new KFilterDev(base);
    00077 }
    00078 #endif
    00079
    00080 //static
    00081 QIODevice * KFilterDev::deviceForFile( const QString & fileName, const QString & mimetype,
    00082                                        bool forceFilter )
    00083 {
    00084     QFile * f = new QFile( fileName );
    00085     KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName )
    00086                          : KFilterBase::findFilterByMimeType( mimetype );
    00087     if ( base )
    00088     {
    00089         base->setDevice(f, true);
    00090         return new KFilterDev(base, true);
    00091     }
    00092     if(!forceFilter)
    00093         return f;
    00094     else
    00095     {
    00096         delete f;
    00097         return 0L;
    00098     }
    00099 }
    00100
    00101 QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype)
    00102 {
    00103     return device( inDevice, mimetype, true );
    00104 }
    00105
    00106 QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype, bool autoDeleteInDevice )
    00107 {
    00108    if (inDevice==0)
    00109       return 0;
    00110    KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype);
    00111    if ( base )
    00112    {
    00113       base->setDevice(inDevice, autoDeleteInDevice);
    00114       return new KFilterDev(base, true /* auto-delete "base" */);
    00115    }
    00116    return 0;
    00117 }
    00118
    00119 bool KFilterDev::open( int mode )
    00120 {
    00121     //kdDebug(7005) << "KFilterDev::open " << mode << endl;
    00122     if ( mode == IO_ReadOnly )
    00123     {
    00124         d->buffer.resize(0);
    00125         d->ungetchBuffer.resize(0);
    00126     }
    00127     else
    00128     {
    00129         d->buffer.resize( BUFFER_SIZE );
    00130         filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
    00131     }
    00132     d->bNeedHeader = !d->bSkipHeaders;
    00133     filter->init( mode );
    00134     d->bOpenedUnderlyingDevice = !filter->device()->isOpen();
    00135     bool ret = d->bOpenedUnderlyingDevice ? filter->device()->open( mode ) : true;
    00136     d->result = KFilterBase::OK;
    00137
    00138     if ( !ret )
    00139         kdWarning(7005) << "KFilterDev::open: Couldn't open underlying device" << endl;
    00140     else
    00141     {
    00142         setState( IO_Open );
    00143         setMode( mode );
    00144     }
    00145     ioIndex = 0;
    00146     return ret;
    00147 }
    00148
    00149 void KFilterDev::close()
    00150 {
    00151     if ( !isOpen() )
    00152         return;
    00153     //kdDebug(7005) << "KFilterDev::close" << endl;
    00154     if ( filter->mode() == IO_WriteOnly )
    00155         writeBlock( 0L, 0 ); // finish writing
    00156     //kdDebug(7005) << "KFilterDev::close. Calling terminate()." << endl;
    00157
    00158     filter->terminate();
    00159     if ( d->bOpenedUnderlyingDevice )
    00160         filter->device()->close();
    00161
    00162     setState( 0 ); // not IO_Open
    00163 }
    00164
    00165 void KFilterDev::flush()
    00166 {
    00167     //kdDebug(7005) << "KFilterDev::flush" << endl;
    00168     filter->device()->flush();
    00169     // Hmm, might not be enough...
    00170 }
    00171
    00172 QIODevice::Offset KFilterDev::size() const
    00173 {
    00174     // Well, hmm, Houston, we have a problem.
    00175     // We can't know the size of the uncompressed data
    00176     // before uncompressing it.......
    00177
    00178     // But readAll, which is not virtual, needs the size.........
    00179
    00180     kdWarning(7005) << "KFilterDev::size - can't be implemented !!!!!!!! Returning -1 " << endl;
    00181     //abort();
    00182     return (uint)-1;
    00183 }
    00184
    00185 QIODevice::Offset KFilterDev::at() const
    00186 {
    00187     return ioIndex;
    00188 }
    00189
    00190 bool KFilterDev::at( QIODevice::Offset pos )
    00191 {
    00192     //kdDebug(7005) << "KFilterDev::at " << pos << "  currently at " << ioIndex << endl;
    00193
    00194     if ( ioIndex == pos )
    00195         return true;
    00196
    00197     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00198
    00199     if ( pos == 0 )
    00200     {
    00201         ioIndex = 0;
    00202         // We can forget about the cached data
    00203         d->ungetchBuffer.resize(0);
    00204         d->bNeedHeader = !d->bSkipHeaders;
    00205         d->result = KFilterBase::OK;
    00206         filter->setInBuffer(0L,0);
    00207         filter->reset();
    00208         return filter->device()->reset();
    00209     }
    00210
    00211     if ( ioIndex < pos ) // we can start from here
    00212         pos = pos - ioIndex;
    00213     else
    00214     {
    00215         // we have to start from 0 ! Ugly and slow, but better than the previous
    00216         // solution (KTarGz was allocating everything into memory)
    00217         if (!at(0)) // sets ioIndex to 0
    00218             return false;
    00219     }
    00220
    00221     //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl;
    00222     QByteArray dummy( QMIN( pos, 3*BUFFER_SIZE ) );
    00223     d->bIgnoreData = true;
    00224     bool result = ( (QIODevice::Offset)readBlock( dummy.data(), pos ) == pos );
    00225     d->bIgnoreData = false;
    00226     return result;
    00227 }
    00228
    00229 bool KFilterDev::atEnd() const
    00230 {
    00231     return filter->device()->atEnd() && (d->result == KFilterBase::END)
    00232                                      && d->ungetchBuffer.isEmpty();
    00233 }
    00234
    00235 Q_LONG KFilterDev::readBlock( char *data, Q_ULONG maxlen )
    00236 {
    00237     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00238     //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl;
    00239
    00240     uint dataReceived = 0;
    00241     if ( !d->ungetchBuffer.isEmpty() )
    00242     {
    00243         uint len = d->ungetchBuffer.length();
    00244         if ( !d->bIgnoreData )
    00245         {
    00246             while ( ( dataReceived < len ) && ( dataReceived < maxlen ) )
    00247             {
    00248                 *data = d->ungetchBuffer[ len - dataReceived - 1 ];
    00249                 data++;
    00250                 dataReceived++;
    00251             }
    00252         }
    00253         else
    00254         {
    00255             dataReceived = QMIN( len, maxlen );
    00256         }
    00257         d->ungetchBuffer.truncate( len - dataReceived );
    00258         ioIndex += dataReceived;
    00259     }
    00260
    00261     // If we came to the end of the stream
    00262     // return what we got from the ungetchBuffer.
    00263     if ( d->result == KFilterBase::END )
    00264         return dataReceived;
    00265
    00266     // If we had an error, return -1.
    00267     if ( d->result != KFilterBase::OK )
    00268         return -1;
    00269
    00270
    00271     Q_ULONG outBufferSize;
    00272     if ( d->bIgnoreData )
    00273     {
    00274         outBufferSize = QMIN( maxlen, 3*BUFFER_SIZE );
    00275     }
    00276     else
    00277     {
    00278         outBufferSize = maxlen;
    00279     }
    00280     outBufferSize -= dataReceived;
    00281     Q_ULONG availOut = outBufferSize;
    00282     filter->setOutBuffer( data, outBufferSize );
    00283
    00284     bool decompressedAll = false;
    00285     while ( dataReceived < maxlen )
    00286     {
    00287         if (filter->inBufferEmpty())
    00288         {
    00289             // Not sure about the best size to set there.
    00290             // For sure, it should be bigger than the header size (see comment in readHeader)
    00291             d->buffer.resize( BUFFER_SIZE );
    00292             // Request data from underlying device
    00293             int size = filter->device()->readBlock( d->buffer.data(),
    00294                                                     d->buffer.size() );
    00295             if ( size )
    00296                 filter->setInBuffer( d->buffer.data(), size );
    00297             else {
    00298                 if ( decompressedAll )
    00299                 {
    00300                     // We decoded everything there was to decode. So -> done.
    00301                     //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl;
    00302                     d->result = KFilterBase::END;
    00303                     break;
    00304                 }
    00305             }
    00306             //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl;
    00307         }
    00308         if (d->bNeedHeader)
    00309         {
    00310             (void) filter->readHeader();
    00311             d->bNeedHeader = false;
    00312         }
    00313
    00314         d->result = filter->uncompress();
    00315
    00316         if (d->result == KFilterBase::ERROR)
    00317         {
    00318             kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl;
    00319             break;
    00320         }
    00321
    00322         // We got that much data since the last time we went here
    00323         uint outReceived = availOut - filter->outBufferAvailable();
    00324         //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl;
    00325         if( availOut < (uint)filter->outBufferAvailable() )
    00326             kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl;
    00327
    00328         dataReceived += outReceived;
    00329         if ( !d->bIgnoreData )  // Move on in the output buffer
    00330         {
    00331             data += outReceived;
    00332             availOut = maxlen - dataReceived;
    00333         }
    00334         else if ( maxlen - dataReceived < outBufferSize )
    00335         {
    00336             availOut = maxlen - dataReceived;
    00337         }
    00338         ioIndex += outReceived;
    00339         if (d->result == KFilterBase::END)
    00340         {
    00341             //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl;
    00342             break; // Finished.
    00343         }
    00344         if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 )
    00345         {
    00346             decompressedAll = true;
    00347         }
    00348         filter->setOutBuffer( data, availOut );
    00349     }
    00350
    00351     return dataReceived;
    00352 }
    00353
    00354 Q_LONG KFilterDev::writeBlock( const char *data /*0 to finish*/, Q_ULONG len )
    00355 {
    00356     Q_ASSERT ( filter->mode() == IO_WriteOnly );
    00357     // If we had an error, return 0.
    00358     if ( d->result != KFilterBase::OK )
    00359         return 0;
    00360
    00361     bool finish = (data == 0L);
    00362     if (!finish)
    00363     {
    00364         filter->setInBuffer( data, len );
    00365         if (d->bNeedHeader)
    00366         {
    00367             (void)filter->writeHeader( d->origFileName );
    00368             d->bNeedHeader = false;
    00369         }
    00370     }
    00371
    00372     uint dataWritten = 0;
    00373     uint availIn = len;
    00374     while ( dataWritten < len || finish )
    00375     {
    00376
    00377         d->result = filter->compress( finish );
    00378
    00379         if (d->result == KFilterBase::ERROR)
    00380         {
    00381             kdWarning(7005) << "KFilterDev: Error when compressing data" << endl;
    00382             // What to do ?
    00383             break;
    00384         }
    00385
    00386         // Wrote everything ?
    00387         if (filter->inBufferEmpty() || (d->result == KFilterBase::END))
    00388         {
    00389             // We got that much data since the last time we went here
    00390             uint wrote = availIn - filter->inBufferAvailable();
    00391
    00392             //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl;
    00393
    00394             // Move on in the input buffer
    00395             data += wrote;
    00396             dataWritten += wrote;
    00397             ioIndex += wrote;
    00398
    00399             availIn = len - dataWritten;
    00400             //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl;
    00401             if ( availIn > 0 ) // Not sure this will ever happen
    00402                 filter->setInBuffer( data, availIn );
    00403         }
    00404
    00405         if (filter->outBufferFull() || (d->result == KFilterBase::END))
    00406         {
    00407             //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl;
    00408             int towrite = d->buffer.size() - filter->outBufferAvailable();
    00409             if ( towrite > 0 )
    00410             {
    00411                 // Write compressed data to underlying device
    00412                 int size = filter->device()->writeBlock( d->buffer.data(), towrite );
    00413                 if ( size != towrite ) {
    00414                     kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl;
    00415                     return 0; // indicate an error (happens on disk full)
    00416                 }
    00417                 //else
    00418                     //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl;
    00419             }
    00420             d->buffer.resize( 8*1024 );
    00421             filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
    00422             if (d->result == KFilterBase::END)
    00423             {
    00424                 //kdDebug(7005) << " KFilterDev::writeBlock END" << endl;
    00425                 Q_ASSERT(finish); // hopefully we don't get end before finishing
    00426                 break;
    00427             }
    00428         }
    00429     }
    00430
    00431     return dataWritten;
    00432 }
    00433
    00434 int KFilterDev::getch()
    00435 {
    00436     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00437     //kdDebug(7005) << "KFilterDev::getch" << endl;
    00438     if ( !d->ungetchBuffer.isEmpty() ) {
    00439         int len = d->ungetchBuffer.length();
    00440         int ch = d->ungetchBuffer[ len-1 ];
    00441         d->ungetchBuffer.truncate( len - 1 );
    00442         ioIndex++;
    00443         //kdDebug(7005) << "KFilterDev::getch from ungetch: " << QString(QChar(ch)) << endl;
    00444         return ch;
    00445     }
    00446     char buf[1];
    00447     int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF;
    00448     //kdDebug(7005) << "KFilterDev::getch ret=" << QString(QChar(ret)) << endl;
    00449     return ret;
    00450 }
    00451
    00452 int KFilterDev::putch( int c )
    00453 {
    00454     //kdDebug(7005) << "KFilterDev::putch" << endl;
    00455     char buf[1];
    00456     buf[0] = c;
    00457     return writeBlock( buf, 1 ) == 1 ? c : -1;
    00458 }
    00459
    00460 int KFilterDev::ungetch( int ch )
    00461 {
    00462     //kdDebug(7005) << "KFilterDev::ungetch " << QString(QChar(ch)) << endl;
    00463     if ( ch == EOF )                            // cannot unget EOF
    00464         return ch;
    00465
    00466     // pipe or similar => we cannot ungetch, so do it manually
    00467     d->ungetchBuffer +=ch;
    00468     ioIndex--;
    00469     return ch;
    00470 }
    00471
    00472 void KFilterDev::setOrigFileName( const QCString & fileName )
    00473 {
    00474     d->origFileName = fileName;
    00475 }
    00476
    00477 void KFilterDev::setSkipHeaders()
    00478 {
    00479     d->bSkipHeaders = true;
    00480 }

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:23:59 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003