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 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 }
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:20:25 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003