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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:41:05 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003