]> git.saurik.com Git - wxWidgets.git/blame - docs/latex/wx/arc.tex
fix warning about comparing (signed) -1 with (unsigned) in_addr_t under Solaris
[wxWidgets.git] / docs / latex / wx / arc.tex
CommitLineData
00375592
VZ
1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2%% Name: arc.tex
3%% Purpose: Overview of the archive classes
4%% Author: M.J.Wetherell
5%% RCS-ID: $Id$
6%% Copyright: 2004 M.J.Wetherell
8795498c 7%% License: wxWindows license
00375592
VZ
8%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9
10\section{Archive formats such as zip}\label{wxarc}
11
12The archive classes handle archive formats such as zip, tar, rar and cab.
13Currently only the wxZip classes are included.
14
15For each archive type, there are the following classes (using zip here
16as an example):
17
18\begin{twocollist}\twocolwidtha{4cm}
19\twocolitem{\helpref{wxZipInputStream}{wxzipinputstream}}{Input stream}
20\twocolitem{\helpref{wxZipOutputStream}{wxzipoutputstream}}{Output stream}
21\twocolitem{\helpref{wxZipEntry}{wxzipentry}}{Holds the meta-data for an
22entry (e.g. filename, timestamp, etc.)}
23\end{twocollist}
24
25There are also abstract wxArchive classes that can be used to write code
26that can handle any of the archive types,
27see '\helpref{Generic archive programming}{wxarcgeneric}'.
28Also see \helpref{wxFileSystem}{fs} for a higher level interface that
29can handle archive files in a generic way.
30
31The classes are designed to handle archives on both seekable streams such
32as disk files, or non-seekable streams such as pipes and sockets
33(see '\helpref{Archives on non-seekable streams}{wxarcnoseek}').
34
35\wxheading{See also}
36
37\helpref{wxFileSystem}{fs}
38
39
40\subsection{Creating an archive}\label{wxarccreate}
41
42\helpref{Archive formats such as zip}{wxarc}
43
44Call \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} to
45create each new entry in the archive, then write the entry's data.
46Another call to PutNextEntry() closes the current entry and begins the next.
47
48For example:
49
50\begin{verbatim}
51 wxFFileOutputStream out(_T("test.zip"));
52 wxZipOutputStream zip(out);
53 wxTextOutputStream txt(zip);
54
55 zip.PutNextEntry(_T("entry1.txt"));
56 txt << _T("Some text for entry1\n");
57
58 zip.PutNextEntry(_T("entry2.txt"));
59 txt << _T("Some text for entry2\n");
60
61\end{verbatim}
62
63
64\subsection{Extracting an archive}\label{wxarcextract}
65
66\helpref{Archive formats such as zip}{wxarc}
67
68\helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns an
69entry object containing the meta-data for the next entry in the archive
70(and gives away ownership). Reading from the input stream then returns
71the entry's data. Eof() becomes true after an attempt has been made to
72read past the end of the entry's data.
73
74When there are no more entries, GetNextEntry() returns NULL and sets Eof().
75
76\begin{verbatim}
77 wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
78 wxZipEntryPtr entry;
79
80 wxFFileInputStream in(_T("test.zip"));
81 wxZipInputStream zip(in);
82 wxTextInputStream txt(zip);
83 wxString data;
84
85 while (entry.reset(zip.GetNextEntry()), entry.get() != NULL)
86 {
87 wxString name = entry->GetName(); // access meta-data
88 txt >> data; // access data
89 }
90
91\end{verbatim}
92
93
94\subsection{Modifying an archive}\label{wxarcmodify}
95
96\helpref{Archive formats such as zip}{wxarc}
97
98To modify an existing archive, write a new copy of the archive to a new file,
99making any necessary changes along the way and transferring any unchanged
100entries using \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}.
101For archive types which compress entry data, CopyEntry() is likely to be
102much more efficient than transferring the data using Read() and Write()
103since it will copy them without decompressing and recompressing them.
104
105In general modifications are not possible without rewriting the archive,
106though it may be possible in some limited cases. Even then, rewriting
107the archive is usually a better choice since a failure can be handled
108without losing the whole archive.
109
110For example to delete all entries matching the pattern "*.txt":
111
112\begin{verbatim}
113 wxFFileInputStream in(_T("in.zip"));
114 wxFFileOutputStream out(_T("out.zip"));
115
116 wxZipInputStream inzip(in);
117 wxZipOutputStream outzip(out);
118 wxZipEntryPtr entry;
119
120 // transfer any meta-data for the archive as a whole (the zip comment
121 // in the case of zip)
122 outzip.CopyArchiveMetaData(inzip);
123
124 // call CopyEntry for each entry except those matching the pattern
125 while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL)
126 if (!entry->GetName().Matches(_T("*.txt")))
127 if (!outzip.CopyEntry(entry.release(), inzip))
128 break;
129
130 bool success = inzip.Eof() && outzip.Close();
131
132\end{verbatim}
133
134
135\subsection{Looking up an archive entry by name}\label{wxarcbyname}
136
137\helpref{Archive formats such as zip}{wxarc}
138
139Also see \helpref{wxFileSystem}{fs} for a higher level interface that is
140more convenient for accessing archive entries by name.
141
142To open just one entry in an archive, the most efficient way is
143to simply search for it linearly by calling
144 \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} until the
145required entry is found. This works both for archives on seekable and
146non-seekable streams.
147
148The format of filenames in the archive is likely to be different
149from the local filename format. For example zips and tars use
150unix style names, with forward slashes as the path separator,
151and absolute paths are not allowed. So if on Windows the file
152"C:$\backslash$MYDIR$\backslash$MYFILE.TXT" is stored, then when reading
153the entry back \helpref{GetName()}{wxarchiveentryname} will return
154"MYDIR$\backslash$MYFILE.TXT". The conversion into the internal format
155and back has lost some information.
156
157So to avoid ambiguity when searching for an entry matching a local name,
158it is better to convert the local name to the archive's internal format
159and search for that:
160
161\begin{verbatim}
162 wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
163 wxZipEntryPtr entry;
164
165 // convert the local name we are looking for into the internal format
166 wxString name = wxZipEntry::GetInternalName(localname);
167
168 // open the zip
169 wxFFileInputStream in(_T("test.zip"));
170 wxZipInputStream zip(in);
171
172 // call GetNextEntry() until the required internal name is found
173 do {
174 entry.reset(zip.GetNextEntry());
175 }
176 while (entry.get() != NULL && entry->GetInternalName() != name);
177
178 if (entry.get() != NULL) {
179 // read the entry's data...
180 }
181
182\end{verbatim}
183
184To access several entries randomly, it is most efficient to transfer the
185entire catalogue of entries to a container such as a std::map or a
186 \helpref{wxHashMap}{wxhashmap} then entries looked up by name can be
187opened using the \helpref{OpenEntry()}{wxarchiveinputstreamopenentry} method.
188
189\begin{verbatim}
190 WX_DECLARE_STRING_HASH_MAP(wxZipEntry*, ZipCatalog);
191 ZipCatalog::iterator it;
192 wxZipEntry *entry;
193 ZipCatalog cat;
194
195 // open the zip
196 wxFFileInputStream in(_T("test.zip"));
197 wxZipInputStream zip(in);
198
199 // load the zip catalog
200 while ((entry = zip.GetNextEntry()) != NULL) {
201 wxZipEntry*& current = cat[entry->GetInternalName()];
202 // some archive formats can have multiple entries with the same name
203 // (e.g. tar) though it is an error in the case of zip
204 delete current;
205 current = entry;
206 }
207
208 // open an entry by name
209 if ((it = cat.find(wxZipEntry::GetInternalName(localname))) != cat.end()) {
210 zip.OpenEntry(*it->second);
211 // ... now read entry's data
212 }
213
214\end{verbatim}
215
216To open more than one entry simultaneously you need more than one
217underlying stream on the same archive:
218
219\begin{verbatim}
220 // opening another entry without closing the first requires another
221 // input stream for the same file
222 wxFFileInputStream in2(_T("test.zip"));
223 wxZipInputStream zip2(in2);
224 if ((it = cat.find(wxZipEntry::GetInternalName(local2))) != cat.end())
225 zip2.OpenEntry(*it->second);
226
227\end{verbatim}
228
229
230\subsection{Generic archive programming}\label{wxarcgeneric}
231
232\helpref{Archive formats such as zip}{wxarc}
233
234Also see \helpref{wxFileSystem}{fs} for a higher level interface that
235can handle archive files in a generic way.
236
237The specific archive classes, such as the wxZip classes, inherit from
238the following abstract classes which can be used to write code that can
239handle any of the archive types:
240
241\begin{twocollist}\twocolwidtha{5cm}
242\twocolitem{\helpref{wxArchiveInputStream}{wxarchiveinputstream}}{Input stream}
243\twocolitem{\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}}{Output stream}
244\twocolitem{\helpref{wxArchiveEntry}{wxarchiveentry}}{Holds the meta-data for an
245entry (e.g. filename)}
246\end{twocollist}
247
248In order to able to write generic code it's necessary to be able to create
249instances of the classes without knowing which archive type is being used.
250So there is a class factory for each archive type, derived from
251 \helpref{wxArchiveClassFactory}{wxarchiveclassfactory}, which can create
252the other classes.
253
254For example, given {\it wxArchiveClassFactory* factory}:
255
256\begin{verbatim}
257 // create streams without knowing their type
258 wxArchiveInputStreamPtr inarc(factory->NewStream(in));
259 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
260
261 // create an empty entry object
262 wxArchiveEntryPtr entry(factory->NewEntry());
263
264\end{verbatim}
265
266The class factory itself can either be created explicitly:
267
268\begin{verbatim}
269 wxArchiveClassFactory *factory = new wxZipClassFactory;
270
271\end{verbatim}
272
273or using wxWidgets' \helpref{RTTI}{runtimeclassoverview}:
274
275\begin{verbatim}
276wxArchiveClassFactory *MakeFactory(const wxString& type)
277{
278 wxString name = _T("wx") + type.Left(1).Upper() +
279 type.Mid(1).Lower() + _T("ClassFactory");
280
281 wxObject *pObj = wxCreateDynamicObject(name);
282 wxArchiveClassFactory *pcf = wxDynamicCast(pObj, wxArchiveClassFactory);
283
284 if (!pcf) {
285 wxLogError(_T("can't handle '%s' archives"), type.c_str());
286 delete pObj;
287 }
288
289 return pcf;
290}
291
292\end{verbatim}
293
294
295\subsection{Archives on non-seekable streams}\label{wxarcnoseek}
296
297\helpref{Archive formats such as zip}{wxarc}
298
299In general, handling archives on non-seekable streams is done in the same
300way as for seekable streams, with a few caveats.
301
302The main limitation is that accessing entries randomly using
303 \helpref{OpenEntry()}{wxarchiveinputstreamopenentry}
304is not possible, the entries can only be accessed sequentially in the order
305they are stored within the archive.
306
307For each archive type, there will also be other limitations which will
308depend on the order the entries' meta-data is stored within the archive.
309These are not too difficult to deal with, and are outlined below.
310
311\wxheading{PutNextEntry and the entry size}
312
313When writing archives, some archive formats store the entry size before
314the entry's data (tar has this limitation, zip doesn't). In this case
315the entry's size must be passed to
316 \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} or an error
317occurs.
318
319This is only an issue on non-seekable streams, since otherwise the archive
320output stream can seek back and fix up the header once the size of the
321entry is known.
322
323For generic programming, one way to handle this is to supply the size
324whenever it is known, and rely on the error message from the output
325stream when the operation is not supported.
326
327\wxheading{GetNextEntry and the weak reference mechanism}
328
329Some archive formats do not store all an entry's meta-data before the
330entry's data (zip is an example). In this case, when reading from a
331non-seekable stream, \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry}
332can only return a partially populated \helpref{wxArchiveEntry}{wxarchiveentry}
333object - not all the fields are set.
334
335The input stream then keeps a weak reference to the entry object and
336updates it when more meta-data becomes available. A weak reference being
337one that does not prevent you from deleting the wxArchiveEntry object - the
338input stream only attempts to update it if it is still around.
339
340The documentation for each archive entry type gives the details
341of what meta-data becomes available and when. For generic programming,
342when the worst case must be assumed, you can rely on all the fields
343of wxArchiveEntry being fully populated when GetNextEntry() returns,
344with the the following exceptions:
345
346\begin{twocollist}\twocolwidtha{3cm}
347\twocolitem{\helpref{GetSize()}{wxarchiveentrysize}}{Guaranteed to be
348available after the entry has been read to \helpref{Eof()}{wxinputstreameof},
349or \helpref{CloseEntry()}{wxarchiveinputstreamcloseentry} has been called}
350\twocolitem{\helpref{IsReadOnly()}{wxarchiveentryisreadonly}}{Guaranteed to
351be available after the end of the archive has been reached, i.e. after
352GetNextEntry() returns NULL and Eof() is true}
353\end{twocollist}
354
355This mechanism allows \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}
356to always fully preserve entries' meta-data. No matter what order order
357the meta-data occurs within the archive, the input stream will always
358have read it before the output stream must write it.
359
360\wxheading{wxArchiveNotifier}
361
362Notifier objects can be used to get a notification whenever an input
363stream updates a \helpref{wxArchiveEntry}{wxarchiveentry} object's data
364via the weak reference mechanism.
365
366Consider the following code which renames an entry in an archive.
367This is the usual way to modify an entry's meta-data, simply set the
368required field before writing it with
369 \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}:
370
371\begin{verbatim}
372 wxArchiveInputStreamPtr arc(factory->NewStream(in));
373 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
374 wxArchiveEntryPtr entry;
375
376 outarc->CopyArchiveMetaData(*arc);
377
378 while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
379 if (entry->GetName() == from)
380 entry->SetName(to);
381 if (!outarc->CopyEntry(entry.release(), *arc))
382 break;
383 }
384
385 bool success = arc->Eof() && outarc->Close();
386
387\end{verbatim}
388
389However, for non-seekable streams, this technique cannot be used for
390fields such as \helpref{IsReadOnly()}{wxarchiveentryisreadonly},
391which are not necessarily set when
392 \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns. In
393this case a \helpref{wxArchiveNotifier}{wxarchivenotifier} can be used:
394
395\begin{verbatim}
396class MyNotifier : public wxArchiveNotifier
397{
398public:
399 void OnEntryUpdated(wxArchiveEntry& entry) { entry.SetIsReadOnly(false); }
400};
401
402\end{verbatim}
403
404The meta-data changes are done in your notifier's
405 \helpref{OnEntryUpdated()}{wxarchivenotifieronentryupdated} method,
406then \helpref{SetNotifier()}{wxarchiveentrynotifier} is called before
407CopyEntry():
408
409\begin{verbatim}
410 wxArchiveInputStreamPtr arc(factory->NewStream(in));
411 wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
412 wxArchiveEntryPtr entry;
413 MyNotifier notifier;
414
415 outarc->CopyArchiveMetaData(*arc);
416
417 while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
418 entry->SetNotifier(notifier);
419 if (!outarc->CopyEntry(entry.release(), *arc))
420 break;
421 }
422
423 bool success = arc->Eof() && outarc->Close();
424
425\end{verbatim}
426
427SetNotifier() calls OnEntryUpdated() immediately, then the input
428stream calls it again whenever it sets more fields in the entry. Since
429OnEntryUpdated() will be called at least once, this technique always
430works even when it is not strictly necessary to use it. For example,
431changing the entry name can be done this way too and it works on seekable
432streams as well as non-seekable.
433