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 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 //kdDebug(7005) << "KFilterDev::at " << pos << " currently at " << ioIndex << endl; 00187 00188 if ( ioIndex == pos ) 00189 return true; 00190 00191 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00192 00193 if ( pos == 0 ) 00194 { 00195 ioIndex = 0; 00196 // We can forget about the cached data 00197 d->ungetchBuffer.resize(0); 00198 d->bNeedHeader = !d->bSkipHeaders; 00199 d->result = KFilterBase::OK; 00200 filter->setInBuffer(0L,0); 00201 filter->reset(); 00202 return filter->device()->reset(); 00203 } 00204 00205 if ( ioIndex < pos ) // we can start from here 00206 pos = pos - ioIndex; 00207 else 00208 { 00209 // we have to start from 0 ! Ugly and slow, but better than the previous 00210 // solution (KTarGz was allocating everything into memory) 00211 if (!at(0)) // sets ioIndex to 0 00212 return false; 00213 } 00214 00215 //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl; 00216 // #### Slow, and allocate a huge block of memory (potentially) 00217 // Maybe we could have a flag in the class to know we don't care about the 00218 // actual data 00219 QByteArray dummy( pos ); 00220 return ( (QIODevice::Offset)readBlock( dummy.data(), pos ) == pos ) ; 00221 } 00222 00223 bool KFilterDev::atEnd() const 00224 { 00225 return filter->device()->atEnd() && (d->result == KFilterBase::END); 00226 } 00227 00228 Q_LONG KFilterDev::readBlock( char *data, Q_ULONG maxlen ) 00229 { 00230 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00231 //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl; 00232 // If we came to the end of the stream, return 0. 00233 if ( d->result == KFilterBase::END ) 00234 return 0; 00235 // If we had an error, return -1. 00236 if ( d->result != KFilterBase::OK ) 00237 return -1; 00238 00239 filter->setOutBuffer( data, maxlen ); 00240 00241 bool decompressedAll = false; 00242 uint dataReceived = 0; 00243 uint availOut = maxlen; 00244 while ( dataReceived < maxlen ) 00245 { 00246 if (filter->inBufferEmpty()) 00247 { 00248 // Not sure about the best size to set there. 00249 // For sure, it should be bigger than the header size (see comment in readHeader) 00250 d->buffer.resize( 8*1024 ); 00251 // Request data from underlying device 00252 int size = filter->device()->readBlock( d->buffer.data(), 00253 d->buffer.size() ); 00254 if ( size ) 00255 filter->setInBuffer( d->buffer.data(), size ); 00256 else { 00257 if ( decompressedAll ) 00258 { 00259 // We decoded everything there was to decode. So -> done. 00260 //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl; 00261 d->result = KFilterBase::END; 00262 break; 00263 } 00264 } 00265 //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl; 00266 } 00267 if (d->bNeedHeader) 00268 { 00269 (void) filter->readHeader(); 00270 d->bNeedHeader = false; 00271 } 00272 00273 d->result = filter->uncompress(); 00274 00275 if (d->result == KFilterBase::ERROR) 00276 { 00277 kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl; 00278 break; 00279 } 00280 00281 // We got that much data since the last time we went here 00282 uint outReceived = availOut - filter->outBufferAvailable(); 00283 //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl; 00284 if( availOut < (uint)filter->outBufferAvailable() ) 00285 kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl; 00286 00287 // Move on in the output buffer 00288 data += outReceived; 00289 dataReceived += outReceived; 00290 ioIndex += outReceived; 00291 if (d->result == KFilterBase::END) 00292 { 00293 //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl; 00294 break; // Finished. 00295 } 00296 if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 ) 00297 { 00298 decompressedAll = true; 00299 } 00300 availOut = maxlen - dataReceived; 00301 filter->setOutBuffer( data, availOut ); 00302 } 00303 00304 return dataReceived; 00305 } 00306 00307 Q_LONG KFilterDev::writeBlock( const char *data /*0 to finish*/, Q_ULONG len ) 00308 { 00309 Q_ASSERT ( filter->mode() == IO_WriteOnly ); 00310 // If we had an error, return 0. 00311 if ( d->result != KFilterBase::OK ) 00312 return 0; 00313 00314 bool finish = (data == 0L); 00315 if (!finish) 00316 { 00317 filter->setInBuffer( data, len ); 00318 if (d->bNeedHeader) 00319 { 00320 (void)filter->writeHeader( d->origFileName ); 00321 d->bNeedHeader = false; 00322 } 00323 } 00324 00325 uint dataWritten = 0; 00326 uint availIn = len; 00327 while ( dataWritten < len || finish ) 00328 { 00329 00330 d->result = filter->compress( finish ); 00331 00332 if (d->result == KFilterBase::ERROR) 00333 { 00334 kdWarning(7005) << "KFilterDev: Error when compressing data" << endl; 00335 // What to do ? 00336 break; 00337 } 00338 00339 // Wrote everything ? 00340 if (filter->inBufferEmpty() || (d->result == KFilterBase::END)) 00341 { 00342 // We got that much data since the last time we went here 00343 uint wrote = availIn - filter->inBufferAvailable(); 00344 00345 //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl; 00346 00347 // Move on in the input buffer 00348 data += wrote; 00349 dataWritten += wrote; 00350 ioIndex += wrote; 00351 00352 availIn = len - dataWritten; 00353 //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl; 00354 if ( availIn > 0 ) // Not sure this will ever happen 00355 filter->setInBuffer( data, availIn ); 00356 } 00357 00358 if (filter->outBufferFull() || (d->result == KFilterBase::END)) 00359 { 00360 //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl; 00361 int towrite = d->buffer.size() - filter->outBufferAvailable(); 00362 if ( towrite > 0 ) 00363 { 00364 // Write compressed data to underlying device 00365 int size = filter->device()->writeBlock( d->buffer.data(), towrite ); 00366 if ( size != towrite ) { 00367 kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl; 00368 return 0; // indicate an error (happens on disk full) 00369 } 00370 //else 00371 //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl; 00372 } 00373 d->buffer.resize( 8*1024 ); 00374 filter->setOutBuffer( d->buffer.data(), d->buffer.size() ); 00375 if (d->result == KFilterBase::END) 00376 { 00377 //kdDebug(7005) << " KFilterDev::writeBlock END" << endl; 00378 Q_ASSERT(finish); // hopefully we don't get end before finishing 00379 break; 00380 } 00381 } 00382 } 00383 00384 return dataWritten; 00385 } 00386 00387 int KFilterDev::getch() 00388 { 00389 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00390 //kdDebug(7005) << "KFilterDev::getch" << endl; 00391 if ( !d->ungetchBuffer.isEmpty() ) { 00392 int len = d->ungetchBuffer.length(); 00393 int ch = d->ungetchBuffer[ len-1 ]; 00394 d->ungetchBuffer.truncate( len - 1 ); 00395 //kdDebug(7005) << "KFilterDev::getch from ungetch: " << QString(QChar(ch)) << endl; 00396 return ch; 00397 } 00398 char buf[1]; 00399 int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF; 00400 //kdDebug(7005) << "KFilterDev::getch ret=" << QString(QChar(ret)) << endl; 00401 return ret; 00402 } 00403 00404 int KFilterDev::putch( int c ) 00405 { 00406 //kdDebug(7005) << "KFilterDev::putch" << endl; 00407 char buf[1]; 00408 buf[0] = c; 00409 return writeBlock( buf, 1 ) == 1 ? c : -1; 00410 } 00411 00412 int KFilterDev::ungetch( int ch ) 00413 { 00414 //kdDebug(7005) << "KFilterDev::ungetch " << QString(QChar(ch)) << endl; 00415 if ( ch == EOF ) // cannot unget EOF 00416 return ch; 00417 00418 // pipe or similar => we cannot ungetch, so do it manually 00419 d->ungetchBuffer +=ch; 00420 return ch; 00421 } 00422 00423 void KFilterDev::setOrigFileName( const QCString & fileName ) 00424 { 00425 d->origFileName = fileName; 00426 } 00427 00428 void KFilterDev::setSkipHeaders() 00429 { 00430 d->bSkipHeaders = true; 00431 }
KDE Logo
This file is part of the documentation for kio Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 12 15:08:44 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003