| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | 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 class KFilterDev::KFilterDevPrivate
    00028 {
    00029 public:
    00030     KFilterDevPrivate() : bNeedHeader(true), bSkipHeaders(false),
    00031                           autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false) {}
    00032     bool bNeedHeader;
    00033     bool bSkipHeaders;
    00034     bool autoDeleteFilterBase;
    00035     bool bOpenedUnderlyingDevice;
    00036     QByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
    00037     QCString ungetchBuffer;
    00038     QCString origFileName;
    00039     KFilterBase::Result result;
    00040 };
    00041
    00042 KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase )
    00043     : filter(_filter)
    00044 {
    00045     assert(filter);
    00046     d = new KFilterDevPrivate;
    00047     d->autoDeleteFilterBase = autoDeleteFilterBase;
    00048 }
    00049
    00050 KFilterDev::~KFilterDev()
    00051 {
    00052     if ( isOpen() )
    00053         close();
    00054     if ( d->autoDeleteFilterBase )
    00055         delete filter;
    00056     delete d;
    00057 }
    00058
    00059 #ifndef KDE_NO_COMPAT
    00060 //this one is static
    00061 // Cumbersome API. To be removed in KDE 3.0.
    00062 QIODevice* KFilterDev::createFilterDevice(KFilterBase* base, QFile* file)
    00063 {
    00064    if (file==0)
    00065       return 0;
    00066
    00067    //we don't need a filter
    00068    if (base==0)
    00069        return new QFile(file->name()); // A bit strange IMHO. We ask for a QFile but we create another one !?! (DF)
    00070
    00071    base->setDevice(file);
    00072    return new KFilterDev(base);
    00073 }
    00074 #endif
    00075
    00076 //static
    00077 QIODevice * KFilterDev::deviceForFile( const QString & fileName, const QString & mimetype,
    00078                                        bool forceFilter )
    00079 {
    00080     QFile * f = new QFile( fileName );
    00081     KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName )
    00082                          : KFilterBase::findFilterByMimeType( mimetype );
    00083     if ( base )
    00084     {
    00085         base->setDevice(f, true);
    00086         return new KFilterDev(base, true);
    00087     }
    00088     if(!forceFilter)
    00089         return f;
    00090     else
    00091     {
    00092         delete f;
    00093         return 0L;
    00094     }
    00095 }
    00096
    00097 QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype)
    00098 {
    00099     return device( inDevice, mimetype, true );
    00100 }
    00101
    00102 QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype, bool autoDeleteInDevice )
    00103 {
    00104    if (inDevice==0)
    00105       return 0;
    00106    KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype);
    00107    if ( base )
    00108    {
    00109       base->setDevice(inDevice, autoDeleteInDevice);
    00110       return new KFilterDev(base, true /* auto-delete "base" */);
    00111    }
    00112    return 0;
    00113 }
    00114
    00115 bool KFilterDev::open( int mode )
    00116 {
    00117     //kdDebug(7005) << "KFilterDev::open " << mode << endl;
    00118     if ( mode == IO_ReadOnly )
    00119     {
    00120         d->buffer.resize(0);
    00121         d->ungetchBuffer.resize(0);
    00122     }
    00123     else
    00124     {
    00125         d->buffer.resize( 8*1024 );
    00126         filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
    00127     }
    00128     d->bNeedHeader = !d->bSkipHeaders;
    00129     filter->init( mode );
    00130     d->bOpenedUnderlyingDevice = !filter->device()->isOpen();
    00131     bool ret = d->bOpenedUnderlyingDevice ? filter->device()->open( mode ) : true;
    00132     d->result = KFilterBase::OK;
    00133
    00134     if ( !ret )
    00135         kdWarning(7005) << "KFilterDev::open: Couldn't open underlying device" << endl;
    00136     else
    00137     {
    00138         setState( IO_Open );
    00139         setMode( mode );
    00140     }
    00141     ioIndex = 0;
    00142     return ret;
    00143 }
    00144
    00145 void KFilterDev::close()
    00146 {
    00147     //kdDebug(7005) << "KFilterDev::close" << endl;
    00148     if ( filter->mode() == IO_WriteOnly )
    00149         writeBlock( 0L, 0 ); // finish writing
    00150     //kdDebug(7005) << "KFilterDev::close. Calling terminate()." << endl;
    00151
    00152     filter->terminate();
    00153     if ( d->bOpenedUnderlyingDevice )
    00154         filter->device()->close();
    00155
    00156     setState( 0 ); // not IO_Open
    00157 }
    00158
    00159 void KFilterDev::flush()
    00160 {
    00161     //kdDebug(7005) << "KFilterDev::flush" << endl;
    00162     filter->device()->flush();
    00163     // Hmm, might not be enough...
    00164 }
    00165
    00166 QIODevice::Offset KFilterDev::size() const
    00167 {
    00168     // Well, hmm, Houston, we have a problem.
    00169     // We can't know the size of the uncompressed data
    00170     // before uncompressing it.......
    00171
    00172     // But readAll, which is not virtual, needs the size.........
    00173
    00174     kdWarning(7005) << "KFilterDev::size - can't be implemented !!!!!!!! Returning -1 " << endl;
    00175     //abort();
    00176     return (uint)-1;
    00177 }
    00178
    00179 QIODevice::Offset KFilterDev::at() const
    00180 {
    00181     return ioIndex;
    00182 }
    00183
    00184 bool KFilterDev::at( QIODevice::Offset pos )
    00185 {
    00186     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00187     //kdDebug(7005) << "KFilterDev::at " << pos << "  currently at " << ioIndex << endl;
    00188
    00189     if ( ioIndex == pos )
    00190         return true;
    00191
    00192     if ( pos == 0 )
    00193     {
    00194         ioIndex = 0;
    00195         // We can forget about the cached data
    00196         d->ungetchBuffer.resize(0);
    00197         d->bNeedHeader = !d->bSkipHeaders;
    00198         d->result = KFilterBase::OK;
    00199         filter->setInBuffer(0L,0);
    00200         filter->reset();
    00201         return filter->device()->reset();
    00202     }
    00203
    00204     if ( ioIndex < pos ) // we can start from here
    00205         pos = pos - ioIndex;
    00206     else
    00207     {
    00208         // we have to start from 0 ! Ugly and slow, but better than the previous
    00209         // solution (KTarGz was allocating everything into memory)
    00210         if (!at(0)) // sets ioIndex to 0
    00211             return false;
    00212     }
    00213
    00214     //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl;
    00215     // #### Slow, and allocate a huge block of memory (potentially)
    00216     // Maybe we could have a flag in the class to know we don't care about the
    00217     // actual data
    00218     QByteArray dummy( pos );
    00219     return ( (QIODevice::Offset)readBlock( dummy.data(), pos ) == pos ) ;
    00220 }
    00221
    00222 bool KFilterDev::atEnd() const
    00223 {
    00224     return filter->device()->atEnd() && (d->result == KFilterBase::END);
    00225 }
    00226
    00227 Q_LONG KFilterDev::readBlock( char *data, Q_ULONG maxlen )
    00228 {
    00229     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00230     //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl;
    00231     // If we came to the end of the stream, return 0.
    00232     if ( d->result == KFilterBase::END )
    00233         return 0;
    00234     // If we had an error, return -1.
    00235     if ( d->result != KFilterBase::OK )
    00236         return -1;
    00237
    00238     filter->setOutBuffer( data, maxlen );
    00239
    00240     bool readEverything = false;
    00241     uint dataReceived = 0;
    00242     uint availOut = maxlen;
    00243     while ( dataReceived < maxlen )
    00244     {
    00245         if (filter->inBufferEmpty())
    00246         {
    00247             // Not sure about the best size to set there.
    00248             // For sure, it should be bigger than the header size (see comment in readHeader)
    00249             d->buffer.resize( 8*1024 );
    00250             // Request data from underlying device
    00251             int size = filter->device()->readBlock( d->buffer.data(),
    00252                                                     d->buffer.size() );
    00253             if ( size )
    00254                 filter->setInBuffer( d->buffer.data(), size );
    00255             else
    00256                 readEverything = true;
    00257             //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl;
    00258         }
    00259         if (d->bNeedHeader)
    00260         {
    00261             (void) filter->readHeader();
    00262             d->bNeedHeader = false;
    00263         }
    00264
    00265         d->result = filter->uncompress();
    00266
    00267         if (d->result == KFilterBase::ERROR)
    00268         {
    00269             kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl;
    00270             break;
    00271         }
    00272
    00273         // We got that much data since the last time we went here
    00274         uint outReceived = availOut - filter->outBufferAvailable();
    00275         //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl;
    00276         if( availOut < (uint)filter->outBufferAvailable() )
    00277             kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl;
    00278
    00279         // Move on in the output buffer
    00280         data += outReceived;
    00281         dataReceived += outReceived;
    00282         ioIndex += outReceived;
    00283         if (d->result == KFilterBase::END)
    00284         {
    00285             //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl;
    00286             break; // Finished.
    00287         }
    00288         if (readEverything && filter->inBufferEmpty() && filter->outBufferAvailable() != 0 )
    00289         {
    00290             // We decoded everything there was to decode. So -> done.
    00291             //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl;
    00292             d->result = KFilterBase::END;
    00293             break;
    00294         }
    00295         availOut = maxlen - dataReceived;
    00296         filter->setOutBuffer( data, availOut );
    00297     }
    00298
    00299     return dataReceived;
    00300 }
    00301
    00302 Q_LONG KFilterDev::writeBlock( const char *data /*0 to finish*/, Q_ULONG len )
    00303 {
    00304     Q_ASSERT ( filter->mode() == IO_WriteOnly );
    00305     // If we had an error, return 0.
    00306     if ( d->result != KFilterBase::OK )
    00307         return 0;
    00308
    00309     bool finish = (data == 0L);
    00310     if (!finish)
    00311     {
    00312         filter->setInBuffer( data, len );
    00313         if (d->bNeedHeader)
    00314         {
    00315             (void)filter->writeHeader( d->origFileName );
    00316             d->bNeedHeader = false;
    00317         }
    00318     }
    00319
    00320     uint dataWritten = 0;
    00321     uint availIn = len;
    00322     while ( dataWritten < len || finish )
    00323     {
    00324
    00325         d->result = filter->compress( finish );
    00326
    00327         if (d->result == KFilterBase::ERROR)
    00328         {
    00329             kdWarning(7005) << "KFilterDev: Error when compressing data" << endl;
    00330             // What to do ?
    00331             break;
    00332         }
    00333
    00334         // Wrote everything ?
    00335         if (filter->inBufferEmpty() || (d->result == KFilterBase::END))
    00336         {
    00337             // We got that much data since the last time we went here
    00338             uint wrote = availIn - filter->inBufferAvailable();
    00339
    00340             //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl;
    00341
    00342             // Move on in the input buffer
    00343             data += wrote;
    00344             dataWritten += wrote;
    00345             ioIndex += wrote;
    00346
    00347             availIn = len - dataWritten;
    00348             //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl;
    00349             if ( availIn > 0 ) // Not sure this will ever happen
    00350                 filter->setInBuffer( data, availIn );
    00351         }
    00352
    00353         if (filter->outBufferFull() || (d->result == KFilterBase::END))
    00354         {
    00355             //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl;
    00356             int towrite = d->buffer.size() - filter->outBufferAvailable();
    00357             if ( towrite > 0 )
    00358             {
    00359                 // Write compressed data to underlying device
    00360                 int size = filter->device()->writeBlock( d->buffer.data(), towrite );
    00361                 if ( size != towrite )
    00362                     kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl;
    00363                 //else
    00364                     //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl;
    00365             }
    00366             d->buffer.resize( 8*1024 );
    00367             filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
    00368             if (d->result == KFilterBase::END)
    00369             {
    00370                 //kdDebug(7005) << " KFilterDev::writeBlock END" << endl;
    00371                 Q_ASSERT(finish); // hopefully we don't get end before finishing
    00372                 break;
    00373             }
    00374         }
    00375     }
    00376
    00377     return dataWritten;
    00378 }
    00379
    00380 int KFilterDev::getch()
    00381 {
    00382     Q_ASSERT ( filter->mode() == IO_ReadOnly );
    00383     //kdDebug(7005) << "KFilterDev::getch" << endl;
    00384     if ( !d->ungetchBuffer.isEmpty() ) {
    00385         int len = d->ungetchBuffer.length();
    00386         int ch = d->ungetchBuffer[ len-1 ];
    00387         d->ungetchBuffer.truncate( len - 1 );
    00388         //kdDebug(7005) << "KFilterDev::getch from ungetch: " << QString(QChar(ch)) << endl;
    00389         return ch;
    00390     }
    00391     char buf[1];
    00392     int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF;
    00393     //kdDebug(7005) << "KFilterDev::getch ret=" << QString(QChar(ret)) << endl;
    00394     return ret;
    00395 }
    00396
    00397 int KFilterDev::putch( int c )
    00398 {
    00399     //kdDebug(7005) << "KFilterDev::putch" << endl;
    00400     char buf[1];
    00401     buf[0] = c;
    00402     return writeBlock( buf, 1 ) == 1 ? c : -1;
    00403 }
    00404
    00405 int KFilterDev::ungetch( int ch )
    00406 {
    00407     //kdDebug(7005) << "KFilterDev::ungetch " << QString(QChar(ch)) << endl;
    00408     if ( ch == EOF )                            // cannot unget EOF
    00409         return ch;
    00410
    00411     // pipe or similar => we cannot ungetch, so do it manually
    00412     d->ungetchBuffer +=ch;
    00413     return ch;
    00414 }
    00415
    00416 void KFilterDev::setOrigFileName( const QCString & fileName )
    00417 {
    00418     d->origFileName = fileName;
    00419 }
    00420
    00421 void KFilterDev::setSkipHeaders()
    00422 {
    00423     d->bSkipHeaders = true;
    00424 }

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:13:36 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001