]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: gzstream.cpp | |
3 | // Purpose: Streams for Gzip files | |
4 | // Author: Mike Wetherell | |
5 | // RCS-ID: $Id$ | |
6 | // Copyright: (c) 2003 Mike Wetherell | |
7 | // Licence: wxWindows licence | |
8 | ///////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) | |
11 | #pragma implementation "gzstream.h" | |
12 | #endif | |
13 | ||
14 | // For compilers that support precompilation, includes "wx.h". | |
15 | #include "wx/wxprec.h" | |
16 | ||
17 | #ifdef __BORLANDC__ | |
18 | #pragma hdrstop | |
19 | #endif | |
20 | ||
21 | #if wxUSE_STREAMS && wxUSE_GZSTREAM && wxUSE_ZLIB | |
22 | ||
23 | #include "wx/log.h" | |
24 | #include "wx/intl.h" | |
25 | #include "wx/datstrm.h" | |
26 | #include "wx/txtstrm.h" | |
27 | #include "wx/filename.h" | |
28 | #include "wx/zstream.h" | |
29 | #include "wx/gzstream.h" | |
30 | ||
31 | #if defined(__WXMSW__) && !defined(__WX_SETUP_H__) && !defined(wxUSE_ZLIB_H_IN_PATH) | |
32 | #include "../zlib/zlib.h" | |
33 | #else | |
34 | #include "zlib.h" | |
35 | #endif | |
36 | ||
37 | ||
38 | ///////////////////////////////////////////////////////////////////////////// | |
39 | // Notes: | |
40 | // | |
41 | // See RFC-1952 and the Gzip/Zlib sources for the details of the Gzip format | |
42 | // | |
43 | // Errors are displayed with wxLogError, but not errors from the compressor | |
44 | // or underlying stream, since they will display their own errors. | |
45 | // | |
46 | // Gzip doesn't use flag 2 to indicate a header crc, so I think it's | |
47 | // probably better not to use it for the moment. | |
48 | // | |
49 | ||
50 | // Flags | |
51 | enum { | |
52 | GZ_ASCII_FLAG = 0x01, | |
53 | GZ_HEAD_CRC = 0x02, | |
54 | GZ_EXTRA_FIELD = 0x04, | |
55 | GZ_ORIG_NAME = 0x08, | |
56 | GZ_COMMENT = 0x10, | |
57 | GZ_RESERVED = 0xE0 | |
58 | }; | |
59 | ||
60 | // Extra flags | |
61 | enum { | |
62 | GZ_SLOWEST = 2, | |
63 | GZ_FASTEST = 4 | |
64 | }; | |
65 | ||
66 | const wxUint16 GZ_MAGIC = 0x8b1f; | |
67 | ||
68 | ||
69 | ///////////////////////////////////////////////////////////////////////////// | |
70 | // Input stream | |
71 | ||
72 | wxGzipInputStream::wxGzipInputStream(wxInputStream& stream, | |
73 | wxMBConv& conv /*=wxConvFile*/) | |
74 | : wxFilterInputStream(stream) | |
75 | { | |
76 | m_decomp = NULL; | |
77 | m_crc = crc32(0, Z_NULL, 0); | |
78 | ||
79 | // Try to read the Gzip magic numbers 0x1f, 0x8b. If not found then the | |
80 | // underlying stream isn't gzipped after all, so unread the bytes taken | |
81 | // so that the underlying stream can be read directly instead. | |
82 | wxUint8 magic[2]; | |
83 | size_t n = m_parent_i_stream->Read(magic, sizeof(magic)).LastRead(); | |
84 | ||
85 | if (n < sizeof(magic) || ((magic[1] << 8) | magic[0]) != GZ_MAGIC) { | |
86 | if (n) | |
87 | m_parent_i_stream->Ungetch(magic, n); | |
88 | // Set EOF rather than error to indicate no gzip data | |
89 | m_lasterror = wxSTREAM_EOF; | |
90 | return; | |
91 | } | |
92 | ||
93 | wxDataInputStream ds(*m_parent_i_stream); | |
94 | ||
95 | // read method, flags, timestamp, extra flags and OS-code | |
96 | int method = ds.Read8(); | |
97 | int flags = ds.Read8(); | |
98 | #if wxUSE_DATETIME | |
99 | wxUint32 datetime = ds.Read32(); | |
100 | if (datetime) // zero means not set (not -1 as usual for time_t) | |
101 | m_datetime = wxLongLong(0, datetime) * 1000L; | |
102 | #else | |
103 | ds.Read32(); | |
104 | #endif | |
105 | ds.Read8(); | |
106 | ds.Read8(); | |
107 | ||
108 | if (flags & GZ_HEAD_CRC) | |
109 | ds.Read16(); | |
110 | ||
111 | if (flags & GZ_EXTRA_FIELD) | |
112 | for (int i = ds.Read16(); i > 0 && m_parent_i_stream->IsOk(); i--) | |
113 | m_parent_i_stream->GetC(); | |
114 | ||
115 | // RFC-1952 specifies ISO-8859-1 for these fields | |
116 | if (flags & GZ_ORIG_NAME) { | |
117 | #if wxUSE_UNICODE | |
118 | wxTextInputStream tis(*m_parent_i_stream, wxT(" \t"), conv); | |
119 | #else | |
120 | wxTextInputStream tis(*m_parent_i_stream); | |
121 | (void)conv; | |
122 | #endif | |
123 | wxChar c; | |
124 | while ((c = tis.GetChar()) != 0 && m_parent_i_stream->IsOk()) | |
125 | m_name += c; | |
126 | } | |
127 | ||
128 | if (flags & GZ_COMMENT) | |
129 | while (m_parent_i_stream->GetC() != 0 && m_parent_i_stream->IsOk()) | |
130 | ; // empty loop | |
131 | ||
132 | m_lasterror = wxSTREAM_READ_ERROR; | |
133 | if (!*m_parent_i_stream) { | |
134 | wxLogDebug(wxT("Error reading Gzip header")); | |
135 | return; | |
136 | } | |
137 | ||
138 | if (flags & GZ_RESERVED) | |
139 | wxLogWarning(_("Unsupported flag in Gzip header")); | |
140 | ||
141 | switch (method) { | |
142 | case Z_DEFLATED: | |
143 | m_decomp = new wxZlibInputStream(*m_parent_i_stream, wxZLIB_NO_HEADER); | |
144 | break; | |
145 | ||
146 | default: | |
147 | wxLogError(_("unsupported compression method in Gzip stream")); | |
148 | return; | |
149 | } | |
150 | ||
151 | if (m_decomp) | |
152 | m_lasterror = m_decomp->GetLastError(); | |
153 | } | |
154 | ||
155 | ||
156 | wxGzipInputStream::~wxGzipInputStream() | |
157 | { | |
158 | delete m_decomp; | |
159 | } | |
160 | ||
161 | ||
162 | size_t wxGzipInputStream::OnSysRead(void *buffer, size_t size) | |
163 | { | |
164 | wxASSERT_MSG(m_decomp, wxT("Gzip not open")); | |
165 | ||
166 | if (!m_decomp) | |
167 | m_lasterror = wxSTREAM_READ_ERROR; | |
168 | if (!IsOk() || !size) | |
169 | return 0; | |
170 | ||
171 | m_decomp->Read(buffer, size); | |
172 | m_crc = crc32(m_crc, (Byte*)buffer, m_decomp->LastRead()); | |
173 | ||
174 | if (m_decomp->Eof()) { | |
175 | wxDataInputStream ds(*m_parent_i_stream); | |
176 | m_lasterror = wxSTREAM_READ_ERROR; | |
177 | ||
178 | if (m_parent_i_stream->IsOk()) { | |
179 | if (ds.Read32() != m_crc) | |
180 | wxLogError(_("reading Gzip stream: bad crc")); | |
181 | else if (ds.Read32() != (wxUint32)TellI()) | |
182 | wxLogError(_("reading Gzip stream: incorrect length")); | |
183 | else | |
184 | m_lasterror = wxSTREAM_EOF; | |
185 | } | |
186 | } | |
187 | else if (!*m_decomp) { | |
188 | m_lasterror = wxSTREAM_READ_ERROR; | |
189 | } | |
190 | ||
191 | return m_decomp->LastRead(); | |
192 | } | |
193 | ||
194 | ||
195 | ///////////////////////////////////////////////////////////////////////////// | |
196 | // Output stream | |
197 | ||
198 | wxGzipOutputStream::wxGzipOutputStream( | |
199 | wxOutputStream& stream, | |
200 | const wxString& originalName /*=wxEmptyString*/, | |
201 | #if wxUSE_DATETIME | |
202 | const wxDateTime& originalTime /*=wxDateTime::Now()*/, | |
203 | #endif | |
204 | int level /*=-1*/, | |
205 | wxMBConv& conv /*=wxConvFile*/) | |
206 | : wxFilterOutputStream(stream) | |
207 | { | |
208 | m_comp = NULL; | |
209 | m_crc = crc32(0, Z_NULL, 0); | |
210 | ||
211 | wxFileName filename(originalName); | |
212 | ||
213 | wxUint32 timestamp = 0; | |
214 | #if wxUSE_DATETIME | |
215 | if (originalTime.IsValid()) | |
216 | timestamp = (originalTime.GetValue() / 1000L).GetLo(); | |
217 | #endif | |
218 | ||
219 | // RFC-1952 specifies ISO-8859-1 for the name. Also it should be just the | |
220 | // name part, no directory, folded to lowercase if case insensitive | |
221 | wxString name = filename.GetFullName(); | |
222 | const wxWX2MBbuf mbName = conv.cWX2MB(name); | |
223 | ||
224 | wxDataOutputStream ds(*m_parent_o_stream); | |
225 | ||
226 | // write signature, method, flags, timestamp, extra flags and OS-code | |
227 | ds.Write16(GZ_MAGIC); | |
228 | ds.Write8(Z_DEFLATED); | |
229 | ds.Write8(mbName && *mbName ? GZ_ORIG_NAME : 0); | |
230 | ds.Write32(timestamp); | |
231 | ds.Write8(level == 1 ? GZ_FASTEST : level == 9 ? GZ_SLOWEST : 0); | |
232 | ds.Write8(255); | |
233 | ||
234 | if (mbName && *mbName) | |
235 | m_parent_o_stream->Write(mbName, strlen(mbName) + 1); | |
236 | ||
237 | m_lasterror = wxSTREAM_WRITE_ERROR; | |
238 | if (!*m_parent_o_stream) { | |
239 | wxLogDebug(wxT("Error writing Gzip header")); | |
240 | return; | |
241 | } | |
242 | ||
243 | m_comp = new wxZlibOutputStream(*m_parent_o_stream, level, wxZLIB_NO_HEADER); | |
244 | ||
245 | if (m_comp) | |
246 | m_lasterror = m_comp->GetLastError(); | |
247 | } | |
248 | ||
249 | ||
250 | wxGzipOutputStream::~wxGzipOutputStream() | |
251 | { | |
252 | if (m_comp && m_comp->IsOk()) { | |
253 | wxUint32 len = (wxUint32)m_comp->TellO(); | |
254 | delete m_comp; | |
255 | if (m_parent_o_stream->IsOk()) { | |
256 | wxDataOutputStream ds(*m_parent_o_stream); | |
257 | ds.Write32(m_crc); | |
258 | ds.Write32(len); // underlying stream will report errors | |
259 | } | |
260 | } else { | |
261 | delete m_comp; | |
262 | } | |
263 | } | |
264 | ||
265 | ||
266 | void wxGzipOutputStream::Sync() | |
267 | { | |
268 | wxASSERT_MSG(m_comp, wxT("Gzip not open")); | |
269 | ||
270 | if (!m_comp) | |
271 | m_lasterror = wxSTREAM_WRITE_ERROR; | |
272 | if (IsOk()) | |
273 | m_comp->Sync(); | |
274 | } | |
275 | ||
276 | ||
277 | size_t wxGzipOutputStream::OnSysWrite(const void *buffer, size_t size) | |
278 | { | |
279 | wxASSERT_MSG(m_comp, wxT("Gzip not open")); | |
280 | ||
281 | if (!m_comp) | |
282 | m_lasterror = wxSTREAM_WRITE_ERROR; | |
283 | if (!IsOk() || !size) | |
284 | return 0; | |
285 | ||
286 | if (m_comp->Write(buffer, size).LastWrite() != size) | |
287 | m_lasterror = wxSTREAM_WRITE_ERROR; | |
288 | m_crc = crc32(m_crc, (Byte*)buffer, size); | |
289 | ||
290 | return m_comp->LastWrite(); | |
291 | } | |
292 | ||
293 | ||
294 | #endif // wxUSE_STREAMS && wxUSE_GZSTREAM && wxUSE_ZLIB |