kio Library API Documentation

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     //kdDebug(7005) << "KFilterDev::close" << endl;
00152     if ( filter->mode() == IO_WriteOnly )
00153         writeBlock( 0L, 0 ); // finish writing
00154     //kdDebug(7005) << "KFilterDev::close. Calling terminate()." << endl;
00155 
00156     filter->terminate();
00157     if ( d->bOpenedUnderlyingDevice )
00158         filter->device()->close();
00159 
00160     setState( 0 ); // not IO_Open
00161 }
00162 
00163 void KFilterDev::flush()
00164 {
00165     //kdDebug(7005) << "KFilterDev::flush" << endl;
00166     filter->device()->flush();
00167     // Hmm, might not be enough...
00168 }
00169 
00170 QIODevice::Offset KFilterDev::size() const
00171 {
00172     // Well, hmm, Houston, we have a problem.
00173     // We can't know the size of the uncompressed data
00174     // before uncompressing it.......
00175 
00176     // But readAll, which is not virtual, needs the size.........
00177 
00178     kdWarning(7005) << "KFilterDev::size - can't be implemented !!!!!!!! Returning -1 " << endl;
00179     //abort();
00180     return (uint)-1;
00181 }
00182 
00183 QIODevice::Offset KFilterDev::at() const
00184 {
00185     return ioIndex;
00186 }
00187 
00188 bool KFilterDev::at( QIODevice::Offset pos )
00189 {
00190     //kdDebug(7005) << "KFilterDev::at " << pos << "  currently at " << ioIndex << endl;
00191 
00192     if ( ioIndex == pos )
00193         return true;
00194 
00195     Q_ASSERT ( filter->mode() == IO_ReadOnly );
00196 
00197     if ( pos == 0 )
00198     {
00199         ioIndex = 0;
00200         // We can forget about the cached data
00201         d->ungetchBuffer.resize(0);
00202         d->bNeedHeader = !d->bSkipHeaders;
00203         d->result = KFilterBase::OK;
00204         filter->setInBuffer(0L,0);
00205         filter->reset();
00206         return filter->device()->reset();
00207     }
00208 
00209     if ( ioIndex < pos ) // we can start from here
00210         pos = pos - ioIndex;
00211     else
00212     {
00213         // we have to start from 0 ! Ugly and slow, but better than the previous
00214         // solution (KTarGz was allocating everything into memory)
00215         if (!at(0)) // sets ioIndex to 0
00216             return false;
00217     }
00218 
00219     //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl;
00220     QByteArray dummy( QMIN( pos, 3*BUFFER_SIZE ) );
00221     d->bIgnoreData = true;
00222     bool result = ( (QIODevice::Offset)readBlock( dummy.data(), pos ) == pos );
00223     d->bIgnoreData = false;
00224     return result;
00225 }
00226 
00227 bool KFilterDev::atEnd() const
00228 {
00229     return filter->device()->atEnd() && (d->result == KFilterBase::END)
00230                                      && d->ungetchBuffer.isEmpty();
00231 }
00232 
00233 Q_LONG KFilterDev::readBlock( char *data, Q_ULONG maxlen )
00234 {
00235     Q_ASSERT ( filter->mode() == IO_ReadOnly );
00236     //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl;
00237 
00238     uint dataReceived = 0;
00239     if ( !d->ungetchBuffer.isEmpty() )
00240     {
00241         Q_ULONG len = d->ungetchBuffer.length();
00242         if ( !d->bIgnoreData )
00243         {
00244             while ( ( dataReceived < len ) && ( dataReceived < maxlen ) )
00245             {
00246                 *data = d->ungetchBuffer[ len - dataReceived - 1 ];
00247                 data++;
00248                 dataReceived++;
00249             }
00250         }
00251         else
00252         {
00253             dataReceived = QMIN( len, maxlen );
00254         }
00255         d->ungetchBuffer.truncate( len - dataReceived );
00256         ioIndex += dataReceived;
00257     }
00258 
00259     // If we came to the end of the stream
00260     // return what we got from the ungetchBuffer.
00261     if ( d->result == KFilterBase::END )
00262         return dataReceived;
00263 
00264     // If we had an error, return -1.
00265     if ( d->result != KFilterBase::OK )
00266         return -1;
00267 
00268 
00269     Q_ULONG outBufferSize;
00270     if ( d->bIgnoreData )
00271     {
00272         outBufferSize = QMIN( maxlen, 3*BUFFER_SIZE );
00273     }
00274     else
00275     {
00276         outBufferSize = maxlen;
00277     }
00278     outBufferSize -= dataReceived;
00279     Q_ULONG availOut = outBufferSize;
00280     filter->setOutBuffer( data, outBufferSize );
00281 
00282     bool decompressedAll = false;
00283     while ( dataReceived < maxlen )
00284     {
00285         if (filter->inBufferEmpty())
00286         {
00287             // Not sure about the best size to set there.
00288             // For sure, it should be bigger than the header size (see comment in readHeader)
00289             d->buffer.resize( BUFFER_SIZE );
00290             // Request data from underlying device
00291             int size = filter->device()->readBlock( d->buffer.data(),
00292                                                     d->buffer.size() );
00293             if ( size )
00294                 filter->setInBuffer( d->buffer.data(), size );
00295             else {
00296                 if ( decompressedAll )
00297                 {
00298                     // We decoded everything there was to decode. So -> done.
00299                     //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl;
00300                     d->result = KFilterBase::END;
00301                     break;
00302                 }
00303             }
00304             //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl;
00305         }
00306         if (d->bNeedHeader)
00307         {
00308             (void) filter->readHeader();
00309             d->bNeedHeader = false;
00310         }
00311 
00312         d->result = filter->uncompress();
00313 
00314         if (d->result == KFilterBase::ERROR)
00315         {
00316             kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl;
00317             break;
00318         }
00319 
00320         // We got that much data since the last time we went here
00321         uint outReceived = availOut - filter->outBufferAvailable();
00322         //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl;
00323         if( availOut < (uint)filter->outBufferAvailable() )
00324             kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl;
00325 
00326         dataReceived += outReceived;
00327         if ( !d->bIgnoreData )  // Move on in the output buffer
00328         {
00329             data += outReceived;
00330             availOut = maxlen - dataReceived;
00331         }
00332         else if ( maxlen - dataReceived < outBufferSize )
00333         {
00334             availOut = maxlen - dataReceived;
00335         }
00336         ioIndex += outReceived;
00337         if (d->result == KFilterBase::END)
00338         {
00339             //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl;
00340             break; // Finished.
00341         }
00342         if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 )
00343         {
00344             decompressedAll = true;
00345         }
00346         filter->setOutBuffer( data, availOut );
00347     }
00348 
00349     return dataReceived;
00350 }
00351 
00352 Q_LONG KFilterDev::writeBlock( const char *data /*0 to finish*/, Q_ULONG len )
00353 {
00354     Q_ASSERT ( filter->mode() == IO_WriteOnly );
00355     // If we had an error, return 0.
00356     if ( d->result != KFilterBase::OK )
00357         return 0;
00358 
00359     bool finish = (data == 0L);
00360     if (!finish)
00361     {
00362         filter->setInBuffer( data, len );
00363         if (d->bNeedHeader)
00364         {
00365             (void)filter->writeHeader( d->origFileName );
00366             d->bNeedHeader = false;
00367         }
00368     }
00369 
00370     uint dataWritten = 0;
00371     uint availIn = len;
00372     while ( dataWritten < len || finish )
00373     {
00374 
00375         d->result = filter->compress( finish );
00376 
00377         if (d->result == KFilterBase::ERROR)
00378         {
00379             kdWarning(7005) << "KFilterDev: Error when compressing data" << endl;
00380             // What to do ?
00381             break;
00382         }
00383 
00384         // Wrote everything ?
00385         if (filter->inBufferEmpty() || (d->result == KFilterBase::END))
00386         {
00387             // We got that much data since the last time we went here
00388             uint wrote = availIn - filter->inBufferAvailable();
00389 
00390             //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl;
00391 
00392             // Move on in the input buffer
00393             data += wrote;
00394             dataWritten += wrote;
00395             ioIndex += wrote;
00396 
00397             availIn = len - dataWritten;
00398             //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl;
00399             if ( availIn > 0 ) // Not sure this will ever happen
00400                 filter->setInBuffer( data, availIn );
00401         }
00402 
00403         if (filter->outBufferFull() || (d->result == KFilterBase::END))
00404         {
00405             //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl;
00406             int towrite = d->buffer.size() - filter->outBufferAvailable();
00407             if ( towrite > 0 )
00408             {
00409                 // Write compressed data to underlying device
00410                 int size = filter->device()->writeBlock( d->buffer.data(), towrite );
00411                 if ( size != towrite ) {
00412                     kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl;
00413                     return 0; // indicate an error (happens on disk full)
00414                 }
00415                 //else
00416                     //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl;
00417             }
00418             d->buffer.resize( 8*1024 );
00419             filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
00420             if (d->result == KFilterBase::END)
00421             {
00422                 //kdDebug(7005) << " KFilterDev::writeBlock END" << endl;
00423                 Q_ASSERT(finish); // hopefully we don't get end before finishing
00424                 break;
00425             }
00426         }
00427     }
00428 
00429     return dataWritten;
00430 }
00431 
00432 int KFilterDev::getch()
00433 {
00434     Q_ASSERT ( filter->mode() == IO_ReadOnly );
00435     //kdDebug(7005) << "KFilterDev::getch" << endl;
00436     if ( !d->ungetchBuffer.isEmpty() ) {
00437         int len = d->ungetchBuffer.length();
00438         int ch = d->ungetchBuffer[ len-1 ];
00439         d->ungetchBuffer.truncate( len - 1 );
00440         ioIndex++;
00441         //kdDebug(7005) << "KFilterDev::getch from ungetch: " << QString(QChar(ch)) << endl;
00442         return ch;
00443     }
00444     char buf[1];
00445     int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF;
00446     //kdDebug(7005) << "KFilterDev::getch ret=" << QString(QChar(ret)) << endl;
00447     return ret;
00448 }
00449 
00450 int KFilterDev::putch( int c )
00451 {
00452     //kdDebug(7005) << "KFilterDev::putch" << endl;
00453     char buf[1];
00454     buf[0] = c;
00455     return writeBlock( buf, 1 ) == 1 ? c : -1;
00456 }
00457 
00458 int KFilterDev::ungetch( int ch )
00459 {
00460     //kdDebug(7005) << "KFilterDev::ungetch " << QString(QChar(ch)) << endl;
00461     if ( ch == EOF )                            // cannot unget EOF
00462         return ch;
00463 
00464     // pipe or similar => we cannot ungetch, so do it manually
00465     d->ungetchBuffer +=ch;
00466     ioIndex--;
00467     return ch;
00468 }
00469 
00470 void KFilterDev::setOrigFileName( const QCString & fileName )
00471 {
00472     d->origFileName = fileName;
00473 }
00474 
00475 void KFilterDev::setSkipHeaders()
00476 {
00477     d->bSkipHeaders = true;
00478 }
KDE Logo
This file is part of the documentation for kio Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Jan 15 13:33:25 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003