~ubuntu-branches/ubuntu/gutsy/kde4libs/gutsy

« back to all changes in this revision

Viewing changes to kdecore/compression/kgzipfilter.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2007-02-21 11:00:12 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20070221110012-6kw8khr9knv6lmg1
Tags: 3.80.3-0ubuntu1
New upstream unstable release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE libraries
 
2
   Copyright (C) 2000-2005 David Faure <faure@kde.org>
 
3
 
 
4
   This library is free software; you can redistribute it and/or
 
5
   modify it under the terms of the GNU Library General Public
 
6
   License version 2 as published by the Free Software Foundation.
 
7
 
 
8
   This library is distributed in the hope that it will be useful,
 
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
   Library General Public License for more details.
 
12
 
 
13
   You should have received a copy of the GNU Library General Public License
 
14
   along with this library; see the file COPYING.LIB.  If not, write to
 
15
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
16
   Boston, MA 02110-1301, USA.
 
17
*/
 
18
 
 
19
#include <time.h>
 
20
#include <zlib.h>
 
21
#include <kdebug.h>
 
22
#include <qiodevice.h>
 
23
 
 
24
#include "kgzipfilter.h"
 
25
 
 
26
/* gzip flag byte */
 
27
#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
 
28
#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
 
29
#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
 
30
#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
 
31
#define COMMENT      0x10 /* bit 4 set: file comment present */
 
32
#define RESERVED     0xE0 /* bits 5..7: reserved */
 
33
 
 
34
// #define DEBUG_GZIP
 
35
 
 
36
class KGzipFilter::KGzipFilterPrivate
 
37
{
 
38
public:
 
39
    z_stream zStream;
 
40
    bool bCompressed;
 
41
};
 
42
 
 
43
KGzipFilter::KGzipFilter()
 
44
        : d(new KGzipFilterPrivate)
 
45
{
 
46
    d->zStream.zalloc = (alloc_func)0;
 
47
    d->zStream.zfree = (free_func)0;
 
48
    d->zStream.opaque = (voidpf)0;
 
49
}
 
50
 
 
51
 
 
52
KGzipFilter::~KGzipFilter()
 
53
{
 
54
    delete d;
 
55
}
 
56
 
 
57
void KGzipFilter::init( int mode )
 
58
{
 
59
    d->zStream.next_in = Z_NULL;
 
60
    d->zStream.avail_in = 0;
 
61
    if ( mode == QIODevice::ReadOnly )
 
62
    {
 
63
        int result = inflateInit2(&d->zStream, -MAX_WBITS); // windowBits is passed < 0 to suppress zlib header
 
64
        if ( result != Z_OK )
 
65
            kDebug(7005) << "inflateInit returned " << result << endl;
 
66
        // No idea what to do with result :)
 
67
    } else if ( mode == QIODevice::WriteOnly )
 
68
    {
 
69
        int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); // same here
 
70
        if ( result != Z_OK )
 
71
            kDebug(7005) << "deflateInit returned " << result << endl;
 
72
    } else {
 
73
        kWarning(7005) << "KGzipFilter: Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported" << endl;
 
74
    }
 
75
    m_mode = mode;
 
76
    d->bCompressed = true;
 
77
    m_headerWritten = false;
 
78
}
 
79
 
 
80
void KGzipFilter::terminate()
 
81
{
 
82
    if ( m_mode == QIODevice::ReadOnly )
 
83
    {
 
84
        int result = inflateEnd(&d->zStream);
 
85
        if ( result != Z_OK )
 
86
            kDebug(7005) << "inflateEnd returned " << result << endl;
 
87
    } else if ( m_mode == QIODevice::WriteOnly )
 
88
    {
 
89
        int result = deflateEnd(&d->zStream);
 
90
        if ( result != Z_OK )
 
91
            kDebug(7005) << "deflateEnd returned " << result << endl;
 
92
    }
 
93
}
 
94
 
 
95
 
 
96
void KGzipFilter::reset()
 
97
{
 
98
    if ( m_mode == QIODevice::ReadOnly )
 
99
    {
 
100
        int result = inflateReset(&d->zStream);
 
101
        if ( result != Z_OK )
 
102
            kDebug(7005) << "inflateReset returned " << result << endl;
 
103
    } else if ( m_mode == QIODevice::WriteOnly ) {
 
104
        int result = deflateReset(&d->zStream);
 
105
        if ( result != Z_OK )
 
106
            kDebug(7005) << "deflateReset returned " << result << endl;
 
107
        m_headerWritten = false;
 
108
    }
 
109
}
 
110
 
 
111
bool KGzipFilter::readHeader()
 
112
{
 
113
#ifdef DEBUG_GZIP
 
114
    kDebug(7005) << "KGzipFilter::readHeader avail=" << d->zStream.avail_in << endl;
 
115
#endif
 
116
    // Assume not compressed until we successfully decode the header
 
117
    d->bCompressed = false;
 
118
    // Assume the first block of data contains the whole header.
 
119
    // The right way is to build this as a big state machine which
 
120
    // is a pain in the ass.
 
121
    // With 8K-blocks, we don't risk much anyway.
 
122
    Bytef *p = d->zStream.next_in;
 
123
    int i = d->zStream.avail_in;
 
124
    if ((i -= 10)  < 0) return false; // Need at least 10 bytes
 
125
#ifdef DEBUG_GZIP
 
126
    kDebug(7005) << "KGzipFilter::readHeader first byte is " << QString::number(*p,16) << endl;
 
127
#endif
 
128
    if (*p++ != 0x1f) return false; // GZip magic
 
129
#ifdef DEBUG_GZIP
 
130
    kDebug(7005) << "KGzipFilter::readHeader second byte is " << QString::number(*p,16) << endl;
 
131
#endif
 
132
    if (*p++ != 0x8b) return false;
 
133
    int method = *p++;
 
134
    int flags = *p++;
 
135
    if ((method != Z_DEFLATED) || (flags & RESERVED) != 0) return false;
 
136
    p += 6;
 
137
    if ((flags & EXTRA_FIELD) != 0) // skip extra field
 
138
    {
 
139
        if ((i -= 2) < 0) return false; // Need at least 2 bytes
 
140
        int len = *p++;
 
141
        len += (*p++) << 8;
 
142
        if ((i -= len) < 0) return false; // Need at least len bytes
 
143
        p += len;
 
144
    }
 
145
    if ((flags & ORIG_NAME) != 0) // skip original file name
 
146
    {
 
147
#ifdef DEBUG_GZIP
 
148
        kDebug(7005) << "ORIG_NAME=" << (char*)p << endl;
 
149
#endif
 
150
        while( (i > 0) && (*p))
 
151
        {
 
152
            i--; p++;
 
153
        }
 
154
        if (--i <= 0) return false;
 
155
        p++;
 
156
    }
 
157
    if ((flags & COMMENT) != 0) // skip comment
 
158
    {
 
159
        while( (i > 0) && (*p))
 
160
        {
 
161
            i--; p++;
 
162
        }
 
163
        if (--i <= 0) return false;
 
164
        p++;
 
165
    }
 
166
    if ((flags & HEAD_CRC) != 0) // skip the header crc
 
167
    {
 
168
        if ((i-=2) < 0) return false;
 
169
        p += 2;
 
170
    }
 
171
 
 
172
    d->zStream.avail_in = i;
 
173
    d->zStream.next_in = p;
 
174
    d->bCompressed = true;
 
175
#ifdef DEBUG_GZIP
 
176
    kDebug(7005) << "header OK" << endl;
 
177
#endif
 
178
    return true;
 
179
}
 
180
 
 
181
/* Output a 16 bit value, lsb first */
 
182
#define put_short(w) \
 
183
    *p++ = (uchar) ((w) & 0xff); \
 
184
    *p++ = (uchar) ((ushort)(w) >> 8);
 
185
 
 
186
/* Output a 32 bit value to the bit stream, lsb first */
 
187
#define put_long(n) \
 
188
    put_short((n) & 0xffff); \
 
189
    put_short(((ulong)(n)) >> 16);
 
190
 
 
191
bool KGzipFilter::writeHeader( const QByteArray & fileName )
 
192
{
 
193
    Bytef *p = d->zStream.next_out;
 
194
    int i = d->zStream.avail_out;
 
195
    *p++ = 0x1f;
 
196
    *p++ = 0x8b;
 
197
    *p++ = Z_DEFLATED;
 
198
    *p++ = ORIG_NAME;
 
199
    put_long( time( 0L ) ); // Modification time (in unix format)
 
200
    *p++ = 0; // Extra flags (2=max compress, 4=fastest compress)
 
201
    *p++ = 3; // Unix
 
202
 
 
203
    uint len = fileName.length();
 
204
    for ( uint j = 0 ; j < len ; ++j )
 
205
        *p++ = fileName[j];
 
206
    *p++ = 0;
 
207
    int headerSize = p - d->zStream.next_out;
 
208
    i -= headerSize;
 
209
    Q_ASSERT(i>0);
 
210
    m_crc = crc32(0L, Z_NULL, 0);
 
211
    d->zStream.next_out = p;
 
212
    d->zStream.avail_out = i;
 
213
    m_headerWritten = true;
 
214
    return true;
 
215
}
 
216
 
 
217
void KGzipFilter::writeFooter()
 
218
{
 
219
    Q_ASSERT( m_headerWritten );
 
220
    if (!m_headerWritten) kDebug() << kBacktrace();
 
221
    Bytef *p = d->zStream.next_out;
 
222
    int i = d->zStream.avail_out;
 
223
    //kDebug(7005) << "KGzipFilter::writeFooter writing CRC= " << QString::number( m_crc, 16 ) << endl;
 
224
    put_long( m_crc );
 
225
    //kDebug(7005) << "KGzipFilter::writing writing totalin= " << d->zStream.total_in << endl;
 
226
    put_long( d->zStream.total_in );
 
227
    i -= p - d->zStream.next_out;
 
228
    d->zStream.next_out = p;
 
229
    d->zStream.avail_out = i;
 
230
}
 
231
 
 
232
void KGzipFilter::setOutBuffer( char * data, uint maxlen )
 
233
{
 
234
    d->zStream.avail_out = maxlen;
 
235
    d->zStream.next_out = (Bytef *) data;
 
236
}
 
237
void KGzipFilter::setInBuffer( const char * data, uint size )
 
238
{
 
239
#ifdef DEBUG_GZIP
 
240
    kDebug(7005) << "KGzipFilter::setInBuffer avail_in=" << size << endl;
 
241
#endif
 
242
    d->zStream.avail_in = size;
 
243
    d->zStream.next_in = (Bytef*) data;
 
244
}
 
245
int KGzipFilter::inBufferAvailable() const
 
246
{
 
247
    return d->zStream.avail_in;
 
248
}
 
249
int KGzipFilter::outBufferAvailable() const
 
250
{
 
251
    return d->zStream.avail_out;
 
252
}
 
253
 
 
254
KGzipFilter::Result KGzipFilter::uncompress_noop()
 
255
{
 
256
    // I'm not sure we really need support for that (uncompressed streams),
 
257
    // but why not, it can't hurt to have it. One case I can think of is someone
 
258
    // naming a tar file "blah.tar.gz" :-)
 
259
    if ( d->zStream.avail_in > 0 )
 
260
    {
 
261
        int n = (d->zStream.avail_in < d->zStream.avail_out) ? d->zStream.avail_in : d->zStream.avail_out;
 
262
        memcpy( d->zStream.next_out, d->zStream.next_in, n );
 
263
        d->zStream.avail_out -= n;
 
264
        d->zStream.next_in += n;
 
265
        d->zStream.avail_in -= n;
 
266
        return OK;
 
267
    } else
 
268
        return END;
 
269
}
 
270
 
 
271
KGzipFilter::Result KGzipFilter::uncompress()
 
272
{
 
273
    Q_ASSERT ( m_mode == QIODevice::ReadOnly );
 
274
    if ( d->bCompressed )
 
275
    {
 
276
#ifdef DEBUG_GZIP
 
277
        kDebug(7005) << "Calling inflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable() << endl;
 
278
        kDebug(7005) << "    next_in=" << d->zStream.next_in << endl;
 
279
#endif
 
280
        int result = inflate(&d->zStream, Z_SYNC_FLUSH);
 
281
#ifdef DEBUG_GZIP
 
282
        kDebug(7005) << " -> inflate returned " << result << endl;
 
283
        kDebug(7005) << "Now avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable() << endl;
 
284
        kDebug(7005) << "    next_in=" << d->zStream.next_in << endl;
 
285
#else
 
286
        if ( result != Z_OK && result != Z_STREAM_END )
 
287
            kDebug(7005) << "Warning: inflate() returned " << result << endl;
 
288
#endif
 
289
        return ( result == Z_OK ? OK : ( result == Z_STREAM_END ? END : ERROR ) );
 
290
    } else
 
291
        return uncompress_noop();
 
292
}
 
293
 
 
294
KGzipFilter::Result KGzipFilter::compress( bool finish )
 
295
{
 
296
    Q_ASSERT ( d->bCompressed );
 
297
    Q_ASSERT ( m_mode == QIODevice::WriteOnly );
 
298
 
 
299
    Bytef* p = d->zStream.next_in;
 
300
    ulong len = d->zStream.avail_in;
 
301
#ifdef DEBUG_GZIP
 
302
    kDebug(7005) << "  calling deflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable() << endl;
 
303
#endif
 
304
    int result = deflate(&d->zStream, finish ? Z_FINISH : Z_NO_FLUSH);
 
305
    if ( result != Z_OK && result != Z_STREAM_END )
 
306
        kDebug(7005) << "  deflate returned " << result << endl;
 
307
    if ( m_headerWritten )
 
308
    {
 
309
        //kDebug(7005) << "Computing CRC for the next " << len - d->zStream.avail_in << " bytes" << endl;
 
310
        m_crc = crc32(m_crc, p, len - d->zStream.avail_in);
 
311
    }
 
312
    if ( result == Z_STREAM_END && m_headerWritten )
 
313
    {
 
314
        //kDebug(7005) << "KGzipFilter::compress finished, write footer" << endl;
 
315
        writeFooter();
 
316
    }
 
317
    return ( result == Z_OK ? OK : ( result == Z_STREAM_END ? END : ERROR ) );
 
318
}