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