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., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, 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 <tqfile.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 TQByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing 00041 TQCString ungetchBuffer; 00042 TQCString 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 TQIODevice* KFilterDev::createFilterDevice(KFilterBase* base, TQFile* file) 00067 { 00068 if (file==0) 00069 return 0; 00070 00071 //we don't need a filter 00072 if (base==0) 00073 return TQT_TQIODEVICE(new TQFile(file->name())); // A bit strange IMHO. We ask for a TQFile but we create another one !?! (DF) 00074 00075 base->setDevice(TQT_TQIODEVICE(file)); 00076 return new KFilterDev(base); 00077 } 00078 #endif 00079 00080 //static 00081 TQIODevice * KFilterDev::deviceForFile( const TQString & fileName, const TQString & mimetype, 00082 bool forceFilter ) 00083 { 00084 TQFile * f = new TQFile( fileName ); 00085 KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName ) 00086 : KFilterBase::findFilterByMimeType( mimetype ); 00087 if ( base ) 00088 { 00089 base->setDevice(TQT_TQIODEVICE(f), true); 00090 return new KFilterDev(base, true); 00091 } 00092 if(!forceFilter) 00093 return TQT_TQIODEVICE(f); 00094 else 00095 { 00096 delete f; 00097 return 0L; 00098 } 00099 } 00100 00101 TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & mimetype) 00102 { 00103 return device( inDevice, mimetype, true ); 00104 } 00105 00106 TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & 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( TQ_OpenMode 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( (TQ_OpenMode)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 TQIODevice::at(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 #ifdef USE_QT4 00173 qint64 KFilterDev::size() const 00174 #else // USE_QT4 00175 TQIODevice::Offset KFilterDev::size() const 00176 #endif // USE_QT4 00177 { 00178 // Well, hmm, Houston, we have a problem. 00179 // We can't know the size of the uncompressed data 00180 // before uncompressing it....... 00181 00182 // But readAll, which is not virtual, needs the size......... 00183 00184 kdDebug(7005) << "KFilterDev::size - can't be implemented, returning -1" << endl; 00185 //abort(); 00186 return (uint)-1; 00187 } 00188 00189 TQIODevice::Offset KFilterDev::at() const 00190 { 00191 return TQIODevice::at(); 00192 } 00193 00194 bool KFilterDev::at( TQIODevice::Offset pos ) 00195 { 00196 //kdDebug(7005) << "KFilterDev::at " << pos << " currently at " << TQIODevice::at() << endl; 00197 00198 if ( TQIODevice::at() == pos ) 00199 return true; 00200 00201 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00202 00203 if ( pos == 0 ) 00204 { 00205 TQIODevice::at(0); 00206 // We can forget about the cached data 00207 d->ungetchBuffer.resize(0); 00208 d->bNeedHeader = !d->bSkipHeaders; 00209 d->result = KFilterBase::OK; 00210 filter->setInBuffer(0L,0); 00211 filter->reset(); 00212 return filter->device()->reset(); 00213 } 00214 00215 if ( TQIODevice::at() < pos ) // we can start from here 00216 pos = pos - TQIODevice::at(); 00217 else 00218 { 00219 // we have to start from 0 ! Ugly and slow, but better than the previous 00220 // solution (KTarGz was allocating everything into memory) 00221 if (!at(0)) // sets ioIndex to 0 00222 return false; 00223 } 00224 00225 //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl; 00226 TQByteArray dummy( TQMIN( pos, 3*BUFFER_SIZE ) ); 00227 d->bIgnoreData = true; 00228 bool result = ( (TQIODevice::Offset)readBlock( dummy.data(), pos ) == pos ); 00229 d->bIgnoreData = false; 00230 return result; 00231 } 00232 00233 bool KFilterDev::atEnd() const 00234 { 00235 return filter->device()->atEnd() && (d->result == KFilterBase::END) 00236 && d->ungetchBuffer.isEmpty(); 00237 } 00238 00239 TQT_TQIO_LONG KFilterDev::tqreadBlock( char *data, TQT_TQIO_ULONG maxlen ) 00240 { 00241 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00242 //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl; 00243 00244 uint dataReceived = 0; 00245 if ( !d->ungetchBuffer.isEmpty() ) 00246 { 00247 uint len = d->ungetchBuffer.length(); 00248 if ( !d->bIgnoreData ) 00249 { 00250 while ( ( dataReceived < len ) && ( dataReceived < maxlen ) ) 00251 { 00252 *data = d->ungetchBuffer[ len - dataReceived - 1 ]; 00253 data++; 00254 dataReceived++; 00255 } 00256 } 00257 else 00258 { 00259 dataReceived = TQMIN( len, maxlen ); 00260 } 00261 d->ungetchBuffer.truncate( len - dataReceived ); 00262 TQIODevice::at(TQIODevice::at() + dataReceived); 00263 } 00264 00265 // If we came to the end of the stream 00266 // return what we got from the ungetchBuffer. 00267 if ( d->result == KFilterBase::END ) 00268 return dataReceived; 00269 00270 // If we had an error, return -1. 00271 if ( d->result != KFilterBase::OK ) 00272 return -1; 00273 00274 00275 TQ_ULONG outBufferSize; 00276 if ( d->bIgnoreData ) 00277 { 00278 outBufferSize = TQMIN( maxlen, 3*BUFFER_SIZE ); 00279 } 00280 else 00281 { 00282 outBufferSize = maxlen; 00283 } 00284 outBufferSize -= dataReceived; 00285 TQ_ULONG availOut = outBufferSize; 00286 filter->setOutBuffer( data, outBufferSize ); 00287 00288 bool decompressedAll = false; 00289 while ( dataReceived < maxlen ) 00290 { 00291 if (filter->inBufferEmpty()) 00292 { 00293 // Not sure about the best size to set there. 00294 // For sure, it should be bigger than the header size (see comment in readHeader) 00295 d->buffer.resize( BUFFER_SIZE ); 00296 // Request data from underlying device 00297 int size = filter->device()->readBlock( d->buffer.data(), 00298 d->buffer.size() ); 00299 if ( size ) 00300 filter->setInBuffer( d->buffer.data(), size ); 00301 else { 00302 if ( decompressedAll ) 00303 { 00304 // We decoded everything there was to decode. So -> done. 00305 //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl; 00306 d->result = KFilterBase::END; 00307 break; 00308 } 00309 } 00310 //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl; 00311 } 00312 if (d->bNeedHeader) 00313 { 00314 (void) filter->readHeader(); 00315 d->bNeedHeader = false; 00316 } 00317 00318 d->result = filter->uncompress(); 00319 00320 if (d->result == KFilterBase::ERROR) 00321 { 00322 kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl; 00323 break; 00324 } 00325 00326 // We got that much data since the last time we went here 00327 uint outReceived = availOut - filter->outBufferAvailable(); 00328 //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl; 00329 if( availOut < (uint)filter->outBufferAvailable() ) 00330 kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl; 00331 00332 dataReceived += outReceived; 00333 if ( !d->bIgnoreData ) // Move on in the output buffer 00334 { 00335 data += outReceived; 00336 availOut = maxlen - dataReceived; 00337 } 00338 else if ( maxlen - dataReceived < outBufferSize ) 00339 { 00340 availOut = maxlen - dataReceived; 00341 } 00342 TQIODevice::at(TQIODevice::at() + outReceived); 00343 if (d->result == KFilterBase::END) 00344 { 00345 //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl; 00346 break; // Finished. 00347 } 00348 if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 ) 00349 { 00350 decompressedAll = true; 00351 } 00352 filter->setOutBuffer( data, availOut ); 00353 } 00354 00355 return dataReceived; 00356 } 00357 00358 TQT_TQIO_LONG KFilterDev::tqwriteBlock( const char *data /*0 to finish*/, TQT_TQIO_ULONG len ) 00359 { 00360 Q_ASSERT ( filter->mode() == IO_WriteOnly ); 00361 // If we had an error, return 0. 00362 if ( d->result != KFilterBase::OK ) 00363 return 0; 00364 00365 bool finish = (data == 0L); 00366 if (!finish) 00367 { 00368 filter->setInBuffer( data, len ); 00369 if (d->bNeedHeader) 00370 { 00371 (void)filter->writeHeader( d->origFileName ); 00372 d->bNeedHeader = false; 00373 } 00374 } 00375 00376 uint dataWritten = 0; 00377 uint availIn = len; 00378 while ( dataWritten < len || finish ) 00379 { 00380 00381 d->result = filter->compress( finish ); 00382 00383 if (d->result == KFilterBase::ERROR) 00384 { 00385 kdWarning(7005) << "KFilterDev: Error when compressing data" << endl; 00386 // What to do ? 00387 break; 00388 } 00389 00390 // Wrote everything ? 00391 if (filter->inBufferEmpty() || (d->result == KFilterBase::END)) 00392 { 00393 // We got that much data since the last time we went here 00394 uint wrote = availIn - filter->inBufferAvailable(); 00395 00396 //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl; 00397 00398 // Move on in the input buffer 00399 data += wrote; 00400 dataWritten += wrote; 00401 TQIODevice::at(TQIODevice::at() + wrote); 00402 00403 availIn = len - dataWritten; 00404 //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl; 00405 if ( availIn > 0 ) // Not sure this will ever happen 00406 filter->setInBuffer( data, availIn ); 00407 } 00408 00409 if (filter->outBufferFull() || (d->result == KFilterBase::END)) 00410 { 00411 //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl; 00412 int towrite = d->buffer.size() - filter->outBufferAvailable(); 00413 if ( towrite > 0 ) 00414 { 00415 // Write compressed data to underlying device 00416 int size = filter->device()->writeBlock( d->buffer.data(), towrite ); 00417 if ( size != towrite ) { 00418 kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl; 00419 return 0; // indicate an error (happens on disk full) 00420 } 00421 //else 00422 //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl; 00423 } 00424 d->buffer.resize( 8*1024 ); 00425 filter->setOutBuffer( d->buffer.data(), d->buffer.size() ); 00426 if (d->result == KFilterBase::END) 00427 { 00428 //kdDebug(7005) << " KFilterDev::writeBlock END" << endl; 00429 Q_ASSERT(finish); // hopefully we don't get end before finishing 00430 break; 00431 } 00432 } 00433 } 00434 00435 return dataWritten; 00436 } 00437 00438 int KFilterDev::getch() 00439 { 00440 Q_ASSERT ( filter->mode() == IO_ReadOnly ); 00441 //kdDebug(7005) << "KFilterDev::getch" << endl; 00442 if ( !d->ungetchBuffer.isEmpty() ) { 00443 int len = d->ungetchBuffer.length(); 00444 int ch = d->ungetchBuffer[ len-1 ]; 00445 d->ungetchBuffer.truncate( len - 1 ); 00446 TQIODevice::at(TQIODevice::at() + 1); 00447 //kdDebug(7005) << "KFilterDev::getch from ungetch: " << TQString(TQChar(ch)) << endl; 00448 return ch; 00449 } 00450 char buf[1]; 00451 int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF; 00452 //kdDebug(7005) << "KFilterDev::getch ret=" << TQString(TQChar(ret)) << endl; 00453 return ret; 00454 } 00455 00456 int KFilterDev::putch( int c ) 00457 { 00458 //kdDebug(7005) << "KFilterDev::putch" << endl; 00459 char buf[1]; 00460 buf[0] = c; 00461 return writeBlock( buf, 1 ) == 1 ? c : -1; 00462 } 00463 00464 int KFilterDev::ungetch( int ch ) 00465 { 00466 //kdDebug(7005) << "KFilterDev::ungetch " << TQString(TQChar(ch)) << endl; 00467 if ( ch == EOF ) // cannot unget EOF 00468 return ch; 00469 00470 // pipe or similar => we cannot ungetch, so do it manually 00471 d->ungetchBuffer +=ch; 00472 TQIODevice::at(TQIODevice::at() - 1); 00473 return ch; 00474 } 00475 00476 void KFilterDev::setOrigFileName( const TQCString & fileName ) 00477 { 00478 d->origFileName = fileName; 00479 } 00480 00481 void KFilterDev::setSkipHeaders() 00482 { 00483 d->bSkipHeaders = true; 00484 }