<precomp-headers>on</precomp-headers>
<precomp-headers-file>wxprec_$(id)</precomp-headers-file>
<precomp-headers-exclude>
- src/common/unzip.c
src/common/extended.c
src/msw/gsocket.cpp
src/msw/gsockmsw.cpp
src/common/textfile.cpp
src/common/tokenzr.cpp
src/common/txtstrm.cpp
- src/common/unzip.c
+ src/common/archive.cpp
src/common/uri.cpp
src/common/variant.cpp
src/common/wfstream.cpp
All:
+- new classes for reading and writing ZIP files (M.J.Wetherell)
- Norwegian (Bokmål) translation added (Hans F. Nordhaug)
- wxDynamicLibrary::HasSymbol() added
- added wxTextInputStream::operator>>(wchar_t) for compilers which support this
--- /dev/null
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Name: arc.tex
+%% Purpose: Overview of the archive classes
+%% Author: M.J.Wetherell
+%% RCS-ID: $Id$
+%% Copyright: 2004 M.J.Wetherell
+%% License: wxWidgets license
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{Archive formats such as zip}\label{wxarc}
+
+The archive classes handle archive formats such as zip, tar, rar and cab.
+Currently only the wxZip classes are included.
+
+For each archive type, there are the following classes (using zip here
+as an example):
+
+\begin{twocollist}\twocolwidtha{4cm}
+\twocolitem{\helpref{wxZipInputStream}{wxzipinputstream}}{Input stream}
+\twocolitem{\helpref{wxZipOutputStream}{wxzipoutputstream}}{Output stream}
+\twocolitem{\helpref{wxZipEntry}{wxzipentry}}{Holds the meta-data for an
+entry (e.g. filename, timestamp, etc.)}
+\end{twocollist}
+
+There are also abstract wxArchive classes that can be used to write code
+that can handle any of the archive types,
+see '\helpref{Generic archive programming}{wxarcgeneric}'.
+Also see \helpref{wxFileSystem}{fs} for a higher level interface that
+can handle archive files in a generic way.
+
+The classes are designed to handle archives on both seekable streams such
+as disk files, or non-seekable streams such as pipes and sockets
+(see '\helpref{Archives on non-seekable streams}{wxarcnoseek}').
+
+\wxheading{See also}
+
+\helpref{wxFileSystem}{fs}
+
+
+\subsection{Creating an archive}\label{wxarccreate}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+Call \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} to
+create each new entry in the archive, then write the entry's data.
+Another call to PutNextEntry() closes the current entry and begins the next.
+
+For example:
+
+\begin{verbatim}
+ wxFFileOutputStream out(_T("test.zip"));
+ wxZipOutputStream zip(out);
+ wxTextOutputStream txt(zip);
+
+ zip.PutNextEntry(_T("entry1.txt"));
+ txt << _T("Some text for entry1\n");
+
+ zip.PutNextEntry(_T("entry2.txt"));
+ txt << _T("Some text for entry2\n");
+
+\end{verbatim}
+
+
+\subsection{Extracting an archive}\label{wxarcextract}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+\helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns an
+entry object containing the meta-data for the next entry in the archive
+(and gives away ownership). Reading from the input stream then returns
+the entry's data. Eof() becomes true after an attempt has been made to
+read past the end of the entry's data.
+
+When there are no more entries, GetNextEntry() returns NULL and sets Eof().
+
+\begin{verbatim}
+ wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
+ wxZipEntryPtr entry;
+
+ wxFFileInputStream in(_T("test.zip"));
+ wxZipInputStream zip(in);
+ wxTextInputStream txt(zip);
+ wxString data;
+
+ while (entry.reset(zip.GetNextEntry()), entry.get() != NULL)
+ {
+ wxString name = entry->GetName(); // access meta-data
+ txt >> data; // access data
+ }
+
+\end{verbatim}
+
+
+\subsection{Modifying an archive}\label{wxarcmodify}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+To modify an existing archive, write a new copy of the archive to a new file,
+making any necessary changes along the way and transferring any unchanged
+entries using \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}.
+For archive types which compress entry data, CopyEntry() is likely to be
+much more efficient than transferring the data using Read() and Write()
+since it will copy them without decompressing and recompressing them.
+
+In general modifications are not possible without rewriting the archive,
+though it may be possible in some limited cases. Even then, rewriting
+the archive is usually a better choice since a failure can be handled
+without losing the whole archive.
+
+For example to delete all entries matching the pattern "*.txt":
+
+\begin{verbatim}
+ wxFFileInputStream in(_T("in.zip"));
+ wxFFileOutputStream out(_T("out.zip"));
+
+ wxZipInputStream inzip(in);
+ wxZipOutputStream outzip(out);
+ wxZipEntryPtr entry;
+
+ // transfer any meta-data for the archive as a whole (the zip comment
+ // in the case of zip)
+ outzip.CopyArchiveMetaData(inzip);
+
+ // call CopyEntry for each entry except those matching the pattern
+ while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL)
+ if (!entry->GetName().Matches(_T("*.txt")))
+ if (!outzip.CopyEntry(entry.release(), inzip))
+ break;
+
+ bool success = inzip.Eof() && outzip.Close();
+
+\end{verbatim}
+
+
+\subsection{Looking up an archive entry by name}\label{wxarcbyname}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+Also see \helpref{wxFileSystem}{fs} for a higher level interface that is
+more convenient for accessing archive entries by name.
+
+To open just one entry in an archive, the most efficient way is
+to simply search for it linearly by calling
+ \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} until the
+required entry is found. This works both for archives on seekable and
+non-seekable streams.
+
+The format of filenames in the archive is likely to be different
+from the local filename format. For example zips and tars use
+unix style names, with forward slashes as the path separator,
+and absolute paths are not allowed. So if on Windows the file
+"C:$\backslash$MYDIR$\backslash$MYFILE.TXT" is stored, then when reading
+the entry back \helpref{GetName()}{wxarchiveentryname} will return
+"MYDIR$\backslash$MYFILE.TXT". The conversion into the internal format
+and back has lost some information.
+
+So to avoid ambiguity when searching for an entry matching a local name,
+it is better to convert the local name to the archive's internal format
+and search for that:
+
+\begin{verbatim}
+ wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
+ wxZipEntryPtr entry;
+
+ // convert the local name we are looking for into the internal format
+ wxString name = wxZipEntry::GetInternalName(localname);
+
+ // open the zip
+ wxFFileInputStream in(_T("test.zip"));
+ wxZipInputStream zip(in);
+
+ // call GetNextEntry() until the required internal name is found
+ do {
+ entry.reset(zip.GetNextEntry());
+ }
+ while (entry.get() != NULL && entry->GetInternalName() != name);
+
+ if (entry.get() != NULL) {
+ // read the entry's data...
+ }
+
+\end{verbatim}
+
+To access several entries randomly, it is most efficient to transfer the
+entire catalogue of entries to a container such as a std::map or a
+ \helpref{wxHashMap}{wxhashmap} then entries looked up by name can be
+opened using the \helpref{OpenEntry()}{wxarchiveinputstreamopenentry} method.
+
+\begin{verbatim}
+ WX_DECLARE_STRING_HASH_MAP(wxZipEntry*, ZipCatalog);
+ ZipCatalog::iterator it;
+ wxZipEntry *entry;
+ ZipCatalog cat;
+
+ // open the zip
+ wxFFileInputStream in(_T("test.zip"));
+ wxZipInputStream zip(in);
+
+ // load the zip catalog
+ while ((entry = zip.GetNextEntry()) != NULL) {
+ wxZipEntry*& current = cat[entry->GetInternalName()];
+ // some archive formats can have multiple entries with the same name
+ // (e.g. tar) though it is an error in the case of zip
+ delete current;
+ current = entry;
+ }
+
+ // open an entry by name
+ if ((it = cat.find(wxZipEntry::GetInternalName(localname))) != cat.end()) {
+ zip.OpenEntry(*it->second);
+ // ... now read entry's data
+ }
+
+\end{verbatim}
+
+To open more than one entry simultaneously you need more than one
+underlying stream on the same archive:
+
+\begin{verbatim}
+ // opening another entry without closing the first requires another
+ // input stream for the same file
+ wxFFileInputStream in2(_T("test.zip"));
+ wxZipInputStream zip2(in2);
+ if ((it = cat.find(wxZipEntry::GetInternalName(local2))) != cat.end())
+ zip2.OpenEntry(*it->second);
+
+\end{verbatim}
+
+
+\subsection{Generic archive programming}\label{wxarcgeneric}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+Also see \helpref{wxFileSystem}{fs} for a higher level interface that
+can handle archive files in a generic way.
+
+The specific archive classes, such as the wxZip classes, inherit from
+the following abstract classes which can be used to write code that can
+handle any of the archive types:
+
+\begin{twocollist}\twocolwidtha{5cm}
+\twocolitem{\helpref{wxArchiveInputStream}{wxarchiveinputstream}}{Input stream}
+\twocolitem{\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}}{Output stream}
+\twocolitem{\helpref{wxArchiveEntry}{wxarchiveentry}}{Holds the meta-data for an
+entry (e.g. filename)}
+\end{twocollist}
+
+In order to able to write generic code it's necessary to be able to create
+instances of the classes without knowing which archive type is being used.
+So there is a class factory for each archive type, derived from
+ \helpref{wxArchiveClassFactory}{wxarchiveclassfactory}, which can create
+the other classes.
+
+For example, given {\it wxArchiveClassFactory* factory}:
+
+\begin{verbatim}
+ // create streams without knowing their type
+ wxArchiveInputStreamPtr inarc(factory->NewStream(in));
+ wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
+
+ // create an empty entry object
+ wxArchiveEntryPtr entry(factory->NewEntry());
+
+\end{verbatim}
+
+The class factory itself can either be created explicitly:
+
+\begin{verbatim}
+ wxArchiveClassFactory *factory = new wxZipClassFactory;
+
+\end{verbatim}
+
+or using wxWidgets' \helpref{RTTI}{runtimeclassoverview}:
+
+\begin{verbatim}
+wxArchiveClassFactory *MakeFactory(const wxString& type)
+{
+ wxString name = _T("wx") + type.Left(1).Upper() +
+ type.Mid(1).Lower() + _T("ClassFactory");
+
+ wxObject *pObj = wxCreateDynamicObject(name);
+ wxArchiveClassFactory *pcf = wxDynamicCast(pObj, wxArchiveClassFactory);
+
+ if (!pcf) {
+ wxLogError(_T("can't handle '%s' archives"), type.c_str());
+ delete pObj;
+ }
+
+ return pcf;
+}
+
+\end{verbatim}
+
+
+\subsection{Archives on non-seekable streams}\label{wxarcnoseek}
+
+\helpref{Archive formats such as zip}{wxarc}
+
+In general, handling archives on non-seekable streams is done in the same
+way as for seekable streams, with a few caveats.
+
+The main limitation is that accessing entries randomly using
+ \helpref{OpenEntry()}{wxarchiveinputstreamopenentry}
+is not possible, the entries can only be accessed sequentially in the order
+they are stored within the archive.
+
+For each archive type, there will also be other limitations which will
+depend on the order the entries' meta-data is stored within the archive.
+These are not too difficult to deal with, and are outlined below.
+
+\wxheading{PutNextEntry and the entry size}
+
+When writing archives, some archive formats store the entry size before
+the entry's data (tar has this limitation, zip doesn't). In this case
+the entry's size must be passed to
+ \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} or an error
+occurs.
+
+This is only an issue on non-seekable streams, since otherwise the archive
+output stream can seek back and fix up the header once the size of the
+entry is known.
+
+For generic programming, one way to handle this is to supply the size
+whenever it is known, and rely on the error message from the output
+stream when the operation is not supported.
+
+\wxheading{GetNextEntry and the weak reference mechanism}
+
+Some archive formats do not store all an entry's meta-data before the
+entry's data (zip is an example). In this case, when reading from a
+non-seekable stream, \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry}
+can only return a partially populated \helpref{wxArchiveEntry}{wxarchiveentry}
+object - not all the fields are set.
+
+The input stream then keeps a weak reference to the entry object and
+updates it when more meta-data becomes available. A weak reference being
+one that does not prevent you from deleting the wxArchiveEntry object - the
+input stream only attempts to update it if it is still around.
+
+The documentation for each archive entry type gives the details
+of what meta-data becomes available and when. For generic programming,
+when the worst case must be assumed, you can rely on all the fields
+of wxArchiveEntry being fully populated when GetNextEntry() returns,
+with the the following exceptions:
+
+\begin{twocollist}\twocolwidtha{3cm}
+\twocolitem{\helpref{GetSize()}{wxarchiveentrysize}}{Guaranteed to be
+available after the entry has been read to \helpref{Eof()}{wxinputstreameof},
+or \helpref{CloseEntry()}{wxarchiveinputstreamcloseentry} has been called}
+\twocolitem{\helpref{IsReadOnly()}{wxarchiveentryisreadonly}}{Guaranteed to
+be available after the end of the archive has been reached, i.e. after
+GetNextEntry() returns NULL and Eof() is true}
+\end{twocollist}
+
+This mechanism allows \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}
+to always fully preserve entries' meta-data. No matter what order order
+the meta-data occurs within the archive, the input stream will always
+have read it before the output stream must write it.
+
+\wxheading{wxArchiveNotifier}
+
+Notifier objects can be used to get a notification whenever an input
+stream updates a \helpref{wxArchiveEntry}{wxarchiveentry} object's data
+via the weak reference mechanism.
+
+Consider the following code which renames an entry in an archive.
+This is the usual way to modify an entry's meta-data, simply set the
+required field before writing it with
+ \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}:
+
+\begin{verbatim}
+ wxArchiveInputStreamPtr arc(factory->NewStream(in));
+ wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
+ wxArchiveEntryPtr entry;
+
+ outarc->CopyArchiveMetaData(*arc);
+
+ while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
+ if (entry->GetName() == from)
+ entry->SetName(to);
+ if (!outarc->CopyEntry(entry.release(), *arc))
+ break;
+ }
+
+ bool success = arc->Eof() && outarc->Close();
+
+\end{verbatim}
+
+However, for non-seekable streams, this technique cannot be used for
+fields such as \helpref{IsReadOnly()}{wxarchiveentryisreadonly},
+which are not necessarily set when
+ \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns. In
+this case a \helpref{wxArchiveNotifier}{wxarchivenotifier} can be used:
+
+\begin{verbatim}
+class MyNotifier : public wxArchiveNotifier
+{
+public:
+ void OnEntryUpdated(wxArchiveEntry& entry) { entry.SetIsReadOnly(false); }
+};
+
+\end{verbatim}
+
+The meta-data changes are done in your notifier's
+ \helpref{OnEntryUpdated()}{wxarchivenotifieronentryupdated} method,
+then \helpref{SetNotifier()}{wxarchiveentrynotifier} is called before
+CopyEntry():
+
+\begin{verbatim}
+ wxArchiveInputStreamPtr arc(factory->NewStream(in));
+ wxArchiveOutputStreamPtr outarc(factory->NewStream(out));
+ wxArchiveEntryPtr entry;
+ MyNotifier notifier;
+
+ outarc->CopyArchiveMetaData(*arc);
+
+ while (entry.reset(arc->GetNextEntry()), entry.get() != NULL) {
+ entry->SetNotifier(notifier);
+ if (!outarc->CopyEntry(entry.release(), *arc))
+ break;
+ }
+
+ bool success = arc->Eof() && outarc->Close();
+
+\end{verbatim}
+
+SetNotifier() calls OnEntryUpdated() immediately, then the input
+stream calls it again whenever it sets more fields in the entry. Since
+OnEntryUpdated() will be called at least once, this technique always
+works even when it is not strictly necessary to use it. For example,
+changing the entry name can be done this way too and it works on seekable
+streams as well as non-seekable.
+
--- /dev/null
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveClassFactory}}\label{wxarchiveclassfactory}
+
+An abstract base class which serves as a common interface to
+archive class factories such as \helpref{wxZipClassFactory}{wxzipclassfactory}.
+
+For each supported archive type (such as zip) there is a class factory
+derived from wxArchiveClassFactory, which allows archive objects to be
+created in a generic way, without knowing the particular type of archive
+being used.
+
+\wxheading{Derived from}
+
+\helpref{wxObject}{wxobject}
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{Generic archive programming}{wxarcgeneric}\\
+\helpref{wxArchiveEntry}{wxarchiveentry}\\
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}\\
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveClassFactory::Get/SetConv}\label{wxarchiveclassfactoryconv}
+
+\constfunc{wxMBConv\&}{GetConv}{\void}
+
+\func{void}{SetConv}{\param{wxMBConv\& }{conv}}
+
+The \helpref{wxMBConv}{wxmbconv} object that the created streams
+will use when translating meta-data. The initial default, set by the
+constructor, is wxConvLocal.
+
+
+\membersection{wxArchiveClassFactory::GetInternalName}\label{wxarchiveclassfactorygetinternalname}
+
+\constfunc{wxString}{GetInternalName}{\param{const wxString\& }{name}, \param{wxPathFormat }{format = wxPATH\_NATIVE}}
+
+Calls the static GetInternalName() function for the archive entry type,
+for example
+ \helpref{wxZipEntry::GetInternalName()}{wxzipentrygetinternalname}.
+
+
+\membersection{wxArchiveClassFactory::NewEntry}\label{wxarchiveclassfactorynewentry}
+
+\constfunc{wxArchiveEntry*}{NewEntry}{\void}
+
+Create a new \helpref{wxArchiveEntry}{wxarchiveentry} object of the
+appropriate type.
+
+
+\membersection{wxArchiveClassFactory::NewStream}\label{wxarchiveclassfactorynewstream}
+
+\constfunc{wxArchiveInputStream*}{NewStream}{\param{wxInputStream\& }{stream}}
+
+\constfunc{wxArchiveOutputStream*}{NewStream}{\param{wxOutputStream\& }{stream}}
+
+Create a new \helpref{wxArchiveInputStream}{wxarchiveinputstream}
+or \helpref{wxArchiveOutputStream}{wxarchiveoutputstream} of the
+appropriate type.
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveEntry}}\label{wxarchiveentry}
+
+An abstract base class which serves as a common interface to
+archive entry classes such as \helpref{wxZipEntry}{wxzipentry}.
+These hold the meta-data (filename, timestamp, etc.), for entries
+in archive files such as zips and tars.
+
+\wxheading{Derived from}
+
+\helpref{wxObject}{wxobject}
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{Generic archive programming}{wxarcgeneric}\\
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}\\
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}\\
+\helpref{wxArchiveNotifier}{wxarchivenotifier}
+
+\wxheading{Non-seekable streams}
+
+This information applies only when reading archives from non-seekable
+streams. When the stream is
+seekable \helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry}
+returns a fully populated \helpref{wxArchiveEntry}{wxarchiveentry}.
+See '\helpref{Archives on non-seekable streams}{wxarcnoseek}' for
+more information.
+
+For generic programming, when the worst case must be assumed, you can
+rely on all the fields of wxArchiveEntry being fully populated when
+GetNextEntry() returns, with the the following exceptions:
+
+\begin{twocollist}\twocolwidtha{3cm}
+\twocolitem{\helpref{GetSize()}{wxarchiveentrysize}}{Guaranteed to be
+available after the entry has been read to \helpref{Eof()}{wxinputstreameof},
+or \helpref{CloseEntry()}{wxarchiveinputstreamcloseentry} has been called}
+\twocolitem{\helpref{IsReadOnly()}{wxarchiveentryisreadonly}}{Guaranteed to
+be available after the end of the archive has been reached, i.e. after
+GetNextEntry() returns NULL and Eof() is true}
+\end{twocollist}
+
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveEntry::Clone}\label{wxarchiveentryclone}
+
+\constfunc{wxArchiveEntry*}{Clone}{\void}
+
+Returns a copy of this entry object.
+
+
+\membersection{wxArchiveEntry::Get/SetDateTime}\label{wxarchiveentrydatetime}
+
+\constfunc{wxDateTime}{GetDateTime}{\void}
+
+\func{void}{SetDateTime}{\param{const wxDateTime\& }{dt}}
+
+The entry's timestamp.
+
+
+\membersection{wxArchiveEntry::GetInternalFormat}\label{wxarchiveentrygetinternalformat}
+
+\constfunc{wxPathFormat}{GetInternalFormat}{\void}
+
+Returns the path format used internally within the archive to store
+filenames.
+
+
+\membersection{wxArchiveEntry::GetInternalName}\label{wxarchiveentrygetinternalname}
+
+\constfunc{wxString}{GetInternalName}{\void}
+
+Returns the entry's filename in the internal format used within the
+archive. The name can include directory components, i.e. it can be a
+full path.
+
+The names of directory entries are returned without any trailing path
+separator. This gives a canonical name that can be used in comparisons.
+
+\wxheading{See also}
+
+\helpref{Looking up an archive entry by name}{wxarcbyname}
+
+
+\membersection{wxArchiveEntry::Get/SetName}\label{wxarchiveentryname}
+
+\constfunc{wxString}{GetName}{\param{wxPathFormat }{format = wxPATH\_NATIVE}}
+
+\func{void}{SetName}{\param{const wxString\& }{name}, \param{wxPathFormat }{format = wxPATH\_NATIVE}}
+
+The entry's name, by default in the native format. The name can include
+directory components, i.e. it can be a full path.
+
+If this is a directory entry, (i.e. if \helpref{IsDir()}{wxarchiveentryisdir}
+is true) then GetName() returns the name with a trailing path separator.
+
+Similarly, setting a name with a trailing path separator sets IsDir().
+
+
+\membersection{wxArchiveEntry::GetOffset}\label{wxarchiveentrygetoffset}
+
+\constfunc{off\_t}{GetOffset}{\void}
+
+Returns a numeric value unique to the entry within the archive.
+
+
+\membersection{wxArchiveEntry::Get/SetSize}\label{wxarchiveentrysize}
+
+\constfunc{off\_t}{GetSize}{\void}
+
+\func{void}{SetSize}{\param{off\_t }{size}}
+
+The size of the entry's data in bytes.
+
+
+\membersection{wxArchiveEntry::IsDir/SetIsDir}\label{wxarchiveentryisdir}
+
+\constfunc{bool}{IsDir}{\void}
+
+\func{void}{SetIsDir}{\param{bool }{isDir = true}}
+
+True if this is a directory entry.
+
+Directory entries are entries with no data, which are used to store
+the meta-data of directories. They also make it possible for completely
+empty directories to be stored.
+
+The names of entries within an archive can be complete paths, and
+unarchivers typically create whatever directories are necessary as they
+restore files, even if the archive contains no explicit directory entries.
+
+
+\membersection{wxArchiveEntry::IsReadOnly/SetIsReadOnly}\label{wxarchiveentryisreadonly}
+
+\constfunc{bool}{IsReadOnly}{\void}
+
+\func{void}{SetIsReadOnly}{\param{bool }{isReadOnly = true}}
+
+True if the entry is a read-only file.
+
+
+\membersection{wxArchiveEntry::Set/UnsetNotifier}\label{wxarchiveentrynotifier}
+
+\func{void}{SetNotifier}{\param{wxArchiveNotifier\& }{notifier}}
+
+\func{void}{UnsetNotifier}{\void}
+
+Sets the \helpref{notifier}{wxarchivenotifier} for this entry.
+Whenever the \helpref{wxArchiveInputStream}{wxarchiveinputstream} updates
+this entry, it will then invoke the associated
+notifier's \helpref{OnEntryUpdated}{wxarchivenotifieronentryupdated}
+method.
+
+Setting a notifier is not usually necessary. It is used to handle
+certain cases when modifying an archive in a pipeline (i.e. between
+non-seekable streams).
+
+\wxheading{See also}
+
+\helpref{Archives on non-seekable streams}{wxarcnoseek}\\
+\helpref{wxArchiveNotifier}{wxarchivenotifier}
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveInputStream}}\label{wxarchiveinputstream}
+
+An abstract base class which serves as a common interface to
+archive input streams such as \helpref{wxZipInputStream}{wxzipinputstream}.
+
+\helpref{GetNextEntry()}{wxarchiveinputstreamgetnextentry} returns an
+ \helpref{wxArchiveEntry}{wxarchiveentry} object containing the meta-data
+for the next entry in the archive (and gives away ownership). Reading from
+the wxArchiveInputStream then returns the entry's data. Eof() becomes true
+after an attempt has been made to read past the end of the entry's data.
+When there are no more entries, GetNextEntry() returns NULL and sets Eof().
+
+\wxheading{Derived from}
+
+\helpref{wxFilterInputStream}{wxfilterinputstream}
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{Data structures}
+{\small \begin{verbatim}
+typedef wxArchiveEntry entry\_type
+\end{verbatim}}
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{wxArchiveEntry}{wxarchiveentry}\\
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveInputStream::CloseEntry}\label{wxarchiveinputstreamcloseentry}
+
+\func{bool}{CloseEntry}{\void}
+
+Closes the current entry. On a non-seekable stream reads to the end of
+the current entry first.
+
+
+\membersection{wxArchiveInputStream::GetNextEntry}\label{wxarchiveinputstreamgetnextentry}
+
+\func{wxArchiveEntry*}{GetNextEntry}{\void}
+
+Closes the current entry if one is open, then reads the meta-data for
+the next entry and returns it in a \helpref{wxArchiveEntry}{wxarchiveentry}
+object, giving away ownership. Reading this wxArchiveInputStream then
+returns the entry's data.
+
+
+\membersection{wxArchiveInputStream::OpenEntry}\label{wxarchiveinputstreamopenentry}
+
+\func{bool}{OpenEntry}{\param{wxArchiveEntry\& }{entry}}
+
+Closes the current entry if one is open, then opens the entry specified
+by the \helpref{wxArchiveEntry}{wxarchiveentry} object.
+
+{\it entry} must be from the same archive file that this
+wxArchiveInputStream is reading, and it must be reading it from a
+seekable stream.
+
+\wxheading{See also}
+
+\helpref{Looking up an archive entry by name}{wxarcbyname}
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveIterator}}\label{wxarchiveiterator}
+
+An input iterator template class that can be used to transfer an archive's
+catalogue to a container. It is only available if wxUSE\_STL is set to 1
+in setup.h, and the uses for it outlined below require a compiler which
+supports member templates.
+
+\begin{verbatim}
+template <class Arc, class T = typename Arc::entry_type*>
+class wxArchiveIterator
+{
+ // this constructor creates an 'end of sequence' object
+ wxArchiveIterator();
+
+ // template parameter 'Arc' should be the type of an archive input stream
+ wxArchiveIterator(Arc& arc) {
+
+ /* ... */
+};
+
+\end{verbatim}
+
+The first template parameter should be the type of archive input stream
+(e.g. \helpref{wxArchiveInputStream}{wxarchiveinputstream}) and the
+second can either be a pointer to an entry
+(e.g. \helpref{wxArchiveEntry}{wxarchiveentry}*), or a string/pointer pair
+(e.g. std::pair<wxString, wxArchiveEntry*>).
+
+The {\tt <wx/archive.h>} header defines the following typedefs:
+
+\begin{verbatim}
+ typedef wxArchiveIterator<wxArchiveInputStream> wxArchiveIter;
+
+ typedef wxArchiveIterator<wxArchiveInputStream,
+ std::pair<wxString, wxArchiveEntry*> > wxArchivePairIter;
+
+\end{verbatim}
+
+The header for any implementation of this interface should define similar
+typedefs for its types, for example in {\tt <wx/zipstrm.h>} there is:
+
+\begin{verbatim}
+ typedef wxArchiveIterator<wxZipInputStream> wxZipIter;
+
+ typedef wxArchiveIterator<wxZipInputStream,
+ std::pair<wxString, wxZipEntry*> > wxZipPairIter;
+
+\end{verbatim}
+
+Transferring the catalogue of an archive {\it arc} to a vector {\it cat},
+can then be done something like this:
+
+\begin{verbatim}
+ std::vector<wxArchiveEntry*> cat((wxArchiveIter)arc, wxArchiveIter());
+
+\end{verbatim}
+
+When the iterator is dereferenced, it gives away ownership of an entry
+object. So in the above example, when you have finished with {\it cat}
+you must delete the pointers it contains.
+
+If you have smart pointers with normal copy semantics (i.e. not auto\_ptr
+or \helpref{wxScopedPtr}{wxscopedptr}), then you can create an iterator
+which uses them instead. For example, with a smart pointer class for
+zip entries {\it ZipEntryPtr}:
+
+\begin{verbatim}
+ typedef std::vector<ZipEntryPtr> ZipCatalog;
+ typedef wxArchiveIterator<wxZipInputStream, ZipEntryPtr> ZipIter;
+ ZipCatalog cat((ZipIter)zip, ZipIter());
+
+\end{verbatim}
+
+Iterators that return std::pair objects can be used to
+populate a std::multimap, to allow entries to be looked
+up by name. The string is initialised using the wxArchiveEntry object's
+ \helpref{GetInternalName()}{wxarchiveentrygetinternalname} function.
+
+\begin{verbatim}
+ typedef std::multimap<wxString, wxZipEntry*> ZipCatalog;
+ ZipCatalog cat((wxZipPairIter)zip, wxZipPairIter());
+
+\end{verbatim}
+
+Note that this iterator also gives away ownership of an entry
+object each time it is dereferenced. So in the above example, when
+you have finished with {\it cat} you must delete the pointers it contains.
+
+Or if you have them, a pair containing a smart pointer can be used
+(again {\it ZipEntryPtr}), no worries about ownership:
+
+\begin{verbatim}
+ typedef std::multimap<wxString, ZipEntryPtr> ZipCatalog;
+ typedef wxArchiveIterator<wxZipInputStream,
+ std::pair<wxString, ZipEntryPtr> > ZipPairIter;
+ ZipCatalog cat((ZipPairIter)zip, ZipPairIter());
+
+\end{verbatim}
+
+\wxheading{Derived from}
+
+No base class
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{See also}
+
+\helpref{wxArchiveEntry}{wxarchiveentry}\\
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}\\
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+
+\wxheading{Data structures}
+{\small \begin{verbatim}
+typedef std::input\_iterator\_tag iterator\_category
+typedef T value\_type
+typedef ptrdiff\_t difference\_type
+typedef T* pointer
+typedef T\& reference
+\end{verbatim}}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveIterator::wxArchiveIterator}\label{wxarchiveiteratorwxarchiveiterator}
+
+\func{}{wxArchiveIterator}{\void}
+
+Construct an 'end of sequence' instance.
+
+\func{}{wxArchiveIterator}{\param{Arc\& }{arc}}
+
+Construct iterator that returns all the entries in the archive input
+stream {\it arc}.
+
+
+\membersection{wxArchiveIterator::operator*}\label{wxarchiveiteratoroperatorunknown}
+
+\constfunc{const T\&}{operator*}{\void}
+
+Returns an entry object from the archive input stream, giving away
+ownership.
+
+
+\membersection{wxArchiveIterator::operator++}\label{wxarchiveiteratoroperatorunknown}
+
+\func{wxArchiveIterator\&}{operator++}{\void}
+
+\func{wxArchiveIterator\&}{operator++}{\param{int}{}}
+
+Position the input iterator at the next entry in the archive input stream.
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveNotifier}}\label{wxarchivenotifier}
+
+If you need to know when a
+ \helpref{wxArchiveInputStream}{wxarchiveinputstream} updates a
+ \helpref{wxArchiveEntry}{wxarchiveentry} object, you can create
+a notifier by deriving from this abstract base class, overriding
+ \helpref{OnEntryUpdated()}{wxarchivenotifieronentryupdated}. An instance
+of your notifier class can then be assigned to the wxArchiveEntry object
+using \helpref{wxArchiveEntry::SetNotifier()}{wxarchiveentrynotifier}.
+Your OnEntryUpdated() method will then be invoked whenever the input
+stream updates the entry.
+
+Setting a notifier is not usually necessary. It is used to handle
+certain cases when modifying an archive in a pipeline (i.e. between
+non-seekable streams).
+See \helpref{Archives on non-seekable streams}{wxarcnoseek}.
+
+\wxheading{Derived from}
+
+No base class
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{See also}
+
+\helpref{Archives on non-seekable streams}{wxarcnoseek}\\
+\helpref{wxArchiveEntry}{wxarchiveentry}\\
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}\\
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveNotifier::OnEntryUpdated}\label{wxarchivenotifieronentryupdated}
+
+\func{void}{OnEntryUpdated}{\param{class wxArchiveEntry\& }{entry}}
+
+This method must be overridden in your derived class.
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/archive.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxArchiveOutputStream}}\label{wxarchiveoutputstream}
+
+An abstract base class which serves as a common interface to
+archive output streams such as \helpref{wxZipOutputStream}{wxzipoutputstream}.
+
+\helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} is used
+to create a new entry in the output archive, then the entry's data is
+written to the wxArchiveOutputStream. Another call to PutNextEntry()
+closes the current entry and begins the next.
+
+\wxheading{Derived from}
+
+\helpref{wxFilterOutputStream}{wxfilteroutputstream}
+
+\wxheading{Include files}
+
+<wx/archive.h>
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{wxArchiveEntry}{wxarchiveentry}\\
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxArchiveOutputStream::\destruct{wxArchiveOutputStream}}\label{wxarchiveoutputstreamdtor}
+
+\func{}{\destruct{wxArchiveOutputStream}}{\void}
+
+Calls \helpref{Close()}{wxarchiveoutputstreamclose} if it has not already
+been called.
+
+
+\membersection{wxArchiveOutputStream::Close}\label{wxarchiveoutputstreamclose}
+
+\func{bool}{Close}{\void}
+
+Closes the archive, returning true if it was successfully written.
+Called by the destructor if not called explicitly.
+
+
+\membersection{wxArchiveOutputStream::CloseEntry}\label{wxarchiveoutputstreamcloseentry}
+
+\func{bool}{CloseEntry}{\void}
+
+Close the current entry. It is called implicitly whenever another new
+entry is created with \helpref{CopyEntry()}{wxarchiveoutputstreamcopyentry}
+or \helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry}, or
+when the archive is closed.
+
+
+\membersection{wxArchiveOutputStream::CopyArchiveMetaData}\label{wxarchiveoutputstreamcopyarchivemetadata}
+
+\func{bool}{CopyArchiveMetaData}{\param{wxArchiveInputStream\& }{stream}}
+
+Some archive formats have additional meta-data that applies to the archive
+as a whole. For example in the case of zip there is a comment, which
+is stored at the end of the zip file. CopyArchiveMetaData() can be used
+to transfer such information when writing a modified copy of an archive.
+
+Since the position of the meta-data can vary between the various archive
+formats, it is best to call CopyArchiveMetaData() before transferring
+the entries. The \helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+will then hold on to the meta-data and write it at the correct point in
+the output file.
+
+When the input archive is being read from a non-seekable stream, the
+meta-data may not be available when CopyArchiveMetaData() is called,
+in which case the two streams set up a link and transfer the data
+when it becomes available.
+
+
+\membersection{wxArchiveOutputStream::CopyEntry}\label{wxarchiveoutputstreamcopyentry}
+
+\func{bool}{CopyEntry}{\param{wxArchiveEntry* }{entry}, \param{wxArchiveInputStream\& }{stream}}
+
+Takes ownership of {\it entry} and uses it to create a new entry in the
+archive. {\it entry} is then opened in the input stream {\it stream}
+and its contents copied to this stream.
+
+For archive types which compress entry data, CopyEntry() is likely to be
+much more efficient than transferring the data using Read() and Write()
+since it will copy them without decompressing and recompressing them.
+
+{\it entry} must be from the same archive file that {\it stream} is
+accessing. For non-seekable streams, {\it entry} must also be the last
+thing read from {\it stream}.
+
+
+\membersection{wxArchiveOutputStream::PutNextDirEntry}\label{wxarchiveoutputstreamputnextdirentry}
+
+\func{bool}{PutNextDirEntry}{\param{const wxString\& }{name}, \param{const wxDateTime\& }{dt = wxDateTime::Now()}}
+
+Create a new directory entry
+(see \helpref{wxArchiveEntry::IsDir()}{wxarchiveentryisdir})
+with the given name and timestamp.
+
+\helpref{PutNextEntry()}{wxarchiveoutputstreamputnextentry} can
+also be used to create directory entries, by supplying a name with
+a trailing path separator.
+
+
+\membersection{wxArchiveOutputStream::PutNextEntry}\label{wxarchiveoutputstreamputnextentry}
+
+\func{bool}{PutNextEntry}{\param{wxArchiveEntry* }{entry}}
+
+Takes ownership of {\it entry} and uses it to create a new entry in
+the archive. The entry's data can then be written by writing to this
+wxArchiveOutputStream.
+
+\func{bool}{PutNextEntry}{\param{const wxString\& }{name}, \param{const wxDateTime\& }{dt = wxDateTime::Now()}, \param{off\_t }{size = wxInvalidOffset}}
+
+Create a new entry with the given name, timestamp and size. The entry's
+data can then be written by writing to this wxArchiveOutputStream.
+
+
\input accessible.tex
\input activevt.tex
\input app.tex
+\input archive.tex
\input array.tex
\input arrstrng.tex
\input artprov.tex
\input tenvvars.tex
\input wxPython.tex
\input re_syntax.tex
+\input arc.tex
%
-% automatically generated by HelpGen from
-% zipstream.h at 02/May/99 19:54:25
+% automatically generated by HelpGen $Revision$ from
+% wx/zipstrm.h at 16/Sep/04 12:19:29
%
-\section{\class{wxZipInputStream}}\label{wxzipinputstream}
+\section{\class{wxZipClassFactory}}\label{wxzipclassfactory}
-This class is input stream from ZIP archive. The archive
-must be local file (accessible via FILE*).
-It has all features including GetSize and seeking.
+Class factory for the zip archive format. See the base class
+for details.
-\wxheading{Note}
+\wxheading{Derived from}
-If you need to enumerate files in ZIP archive, you can use
-\helpref{wxFileSystem}{wxfilesystem} together with wxZipFSHandler (see
-\helpref{the overview}{fs}).
+\helpref{wxArchiveClassFactory}{wxarchiveclassfactory}
+\wxheading{Include files}
+
+<wx/zipstrm.h>
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{Generic archive programming}{wxarcgeneric}
+\helpref{wxZipEntry}{wxzipentry}\\
+\helpref{wxZipInputStream}{wxzipinputstream}\\
+\helpref{wxZipOutputStream}{wxzipoutputstream}
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/zipstrm.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxZipEntry}}\label{wxzipentry}
+
+Holds the meta-data for an entry in a zip.
\wxheading{Derived from}
-\helpref{wxInputStream}{wxinputstream}
+\helpref{wxArchiveEntry}{wxarchiveentry}
\wxheading{Include files}
<wx/zipstrm.h>
+\wxheading{Data structures}
+
+Constants for \helpref{Get/SetMethod}{wxzipentrymethod}:
+
+\begin{verbatim}
+// Compression Method, only 0 (store) and 8 (deflate) are supported here
+//
+enum wxZipMethod
+{
+ wxZIP_METHOD_STORE,
+ wxZIP_METHOD_SHRINK,
+ wxZIP_METHOD_REDUCE1,
+ wxZIP_METHOD_REDUCE2,
+ wxZIP_METHOD_REDUCE3,
+ wxZIP_METHOD_REDUCE4,
+ wxZIP_METHOD_IMPLODE,
+ wxZIP_METHOD_TOKENIZE,
+ wxZIP_METHOD_DEFLATE,
+ wxZIP_METHOD_DEFLATE64,
+ wxZIP_METHOD_BZIP2 = 12,
+ wxZIP_METHOD_DEFAULT = 0xffff
+};
+
+\end{verbatim}
+
+Constants for \helpref{Get/SetSystemMadeBy}{wxzipentrysystemmadeby}:
+
+\begin{verbatim}
+// Originating File-System.
+//
+// These are Pkware's values. Note that Info-zip disagree on some of them,
+// most notably NTFS.
+//
+enum wxZipSystem
+{
+ wxZIP_SYSTEM_MSDOS,
+ wxZIP_SYSTEM_AMIGA,
+ wxZIP_SYSTEM_OPENVMS,
+ wxZIP_SYSTEM_UNIX,
+ wxZIP_SYSTEM_VM_CMS,
+ wxZIP_SYSTEM_ATARI_ST,
+ wxZIP_SYSTEM_OS2_HPFS,
+ wxZIP_SYSTEM_MACINTOSH,
+ wxZIP_SYSTEM_Z_SYSTEM,
+ wxZIP_SYSTEM_CPM,
+ wxZIP_SYSTEM_WINDOWS_NTFS,
+ wxZIP_SYSTEM_MVS,
+ wxZIP_SYSTEM_VSE,
+ wxZIP_SYSTEM_ACORN_RISC,
+ wxZIP_SYSTEM_VFAT,
+ wxZIP_SYSTEM_ALTERNATE_MVS,
+ wxZIP_SYSTEM_BEOS,
+ wxZIP_SYSTEM_TANDEM,
+ wxZIP_SYSTEM_OS_400
+};
+
+\end{verbatim}
+
+Constants for \helpref{Get/SetExternalAttributes}{wxzipentryexternalattributes}:
+
+\begin{verbatim}
+// Dos/Win file attributes
+//
+enum wxZipAttributes
+{
+ wxZIP_A_RDONLY = 0x01,
+ wxZIP_A_HIDDEN = 0x02,
+ wxZIP_A_SYSTEM = 0x04,
+ wxZIP_A_SUBDIR = 0x10,
+ wxZIP_A_ARCH = 0x20,
+
+ wxZIP_A_MASK = 0x37
+};
+
+\end{verbatim}
+
+Constants for \helpref{Get/SetFlags}{wxzipentrygetflags}:
+
+\begin{verbatim}
+// Values for the flags field in the zip headers
+//
+enum wxZipFlags
+{
+ wxZIP_ENCRYPTED = 0x0001,
+ wxZIP_DEFLATE_NORMAL = 0x0000, // normal compression
+ wxZIP_DEFLATE_EXTRA = 0x0002, // extra compression
+ wxZIP_DEFLATE_FAST = 0x0004, // fast compression
+ wxZIP_DEFLATE_SUPERFAST = 0x0006, // superfast compression
+ wxZIP_DEFLATE_MASK = 0x0006,
+ wxZIP_SUMS_FOLLOW = 0x0008, // crc and sizes come after the data
+ wxZIP_ENHANCED = 0x0010,
+ wxZIP_PATCH = 0x0020,
+ wxZIP_STRONG_ENC = 0x0040,
+ wxZIP_UNUSED = 0x0F80,
+ wxZIP_RESERVED = 0xF000
+};
+
+\end{verbatim}
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{wxZipInputStream}{wxzipinputstream}\\
+\helpref{wxZipOutputStream}{wxzipoutputstream}\\
+\helpref{wxZipNotifier}{wxzipnotifier}
+
+\wxheading{Field availability}
+
+When reading a zip from a stream that is seekable,
+ \helpref{GetNextEntry()}{wxzipinputstreamgetnextentry} returns
+a fully populated wxZipEntry object except for
+ \helpref{wxZipEntry::GetLocalExtra()}{wxzipentrylocalextra}. GetLocalExtra()
+becomes available when the entry is opened, either by calling
+ \helpref{wxZipInputStream::OpenEntry}{wxzipinputstreamopenentry} or by
+making an attempt to read the entry's data.
+
+For zips on \helpref{non-seekable}{wxarcnoseek} streams, the following
+fields are always available when GetNextEntry() returns:
+
+\helpref{GetDateTime}{wxarchiveentrydatetime}\\
+\helpref{GetInternalFormat}{wxarchiveentrygetinternalformat}\\
+\helpref{GetInternalName}{wxzipentrygetinternalname}\\
+\helpref{GetFlags}{wxzipentrygetflags}\\
+\helpref{GetLocalExtra}{wxzipentrylocalextra}\\
+\helpref{GetMethod}{wxzipentrymethod}\\
+\helpref{GetName}{wxarchiveentryname}\\
+\helpref{GetOffset}{wxarchiveentrygetoffset}\\
+\helpref{IsDir}{wxarchiveentryisdir}
+
+The following fields are also usually available when GetNextEntry()
+returns, however, if the zip was also written to a non-seekable stream
+the zipper is permitted to store them after the entry's data. In that
+case they become available when the entry's data has been read to Eof(),
+or \helpref{CloseEntry()}{wxarchiveinputstreamcloseentry} has been called.
+{\tt (GetFlags() \& wxZIP\_SUMS\_FOLLOW) != 0} indicates that one or
+more of these come after the data:
+
+\helpref{GetCompressedSize}{wxzipentrygetcompressedsize}\\
+\helpref{GetCrc}{wxzipentrygetcrc}\\
+\helpref{GetSize}{wxarchiveentrysize}
+
+The following are stored at the end of the zip, and become available
+when the end of the zip has been reached, i.e. after GetNextEntry()
+returns NULL and Eof() is true:
+
+\helpref{GetComment}{wxzipentrycomment}\\
+\helpref{GetExternalAttributes}{wxzipentryexternalattributes}\\
+\helpref{GetExtra}{wxzipentryextra}\\
+\helpref{GetMode}{wxzipentrymode}\\
+\helpref{GetSystemMadeBy}{wxzipentrysystemmadeby}\\
+\helpref{IsReadOnly}{wxarchiveentryisreadonly}\\
+\helpref{IsMadeByUnix}{wxzipentryismadebyunix}\\
+\helpref{IsText}{wxzipentryistext}
+
+
\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxZipEntry::wxZipEntry}\label{wxzipentrywxzipentry}
+
+\func{}{wxZipEntry}{\param{const wxString\& }{name = wxEmptyString}, \param{const wxDateTime\& }{dt = wxDateTime::Now()}, \param{off\_t }{size = wxInvalidOffset}}
+
+Constructor.
+
+\func{}{wxZipEntry}{\param{const wxZipEntry\& }{entry}}
+
+Copy constructor.
+
+
+\membersection{wxZipEntry::Clone}\label{wxzipentryclone}
+
+\constfunc{wxZipEntry*}{Clone}{\void}
+
+Make a copy of this entry.
+
+
+\membersection{wxZipEntry::Get/SetComment}\label{wxzipentrycomment}
+
+\constfunc{wxString}{GetComment}{\void}
+
+\func{void}{SetComment}{\param{const wxString\& }{comment}}
+
+A short comment for this entry.
+
+
+\membersection{wxZipEntry::GetCompressedSize}\label{wxzipentrygetcompressedsize}
+
+\constfunc{off\_t}{GetCompressedSize}{\void}
+
+The compressed size of this entry in bytes.
+
+
+\membersection{wxZipEntry::GetCrc}\label{wxzipentrygetcrc}
+
+\constfunc{wxUint32}{GetCrc}{\void}
+
+CRC32 for this entry's data.
+
+
+\membersection{wxZipEntry::Get/SetExternalAttributes}\label{wxzipentryexternalattributes}
+
+\constfunc{wxUint32}{GetExternalAttributes}{\void}
+
+\func{void}{SetExternalAttributes}{\param{wxUint32 }{attr}}
+
+The low 8 bits are always the DOS/Windows file attributes for this entry.
+The values of these attributes are given in the
+enumeration {\tt wxZipAttributes}.
+
+The remaining bits can store platform specific permission bits or
+attributes, and their meaning depends on the value
+of \helpref{SetSystemMadeBy()}{wxzipentrysystemmadeby}.
+If \helpref{IsMadeByUnix()}{wxzipentryismadebyunix} is true then the
+high 16 bits are unix mode bits.
+
+The following other accessors access these bits:
+
+\helpref{IsReadOnly/SetIsReadOnly}{wxarchiveentryisreadonly}\\
+\helpref{IsDir/SetIsDir}{wxarchiveentryisdir}\\
+\helpref{Get/SetMode}{wxzipentrymode}
+
+
+\membersection{wxZipEntry::Get/SetExtra}\label{wxzipentryextra}
+
+\constfunc{const char*}{GetExtra}{\void}
+
+\constfunc{size\_t}{GetExtraLen}{\void}
+
+\func{void}{SetExtra}{\param{const char* }{extra}, \param{size\_t }{len}}
+
+The extra field from the entry's central directory record.
+
+The extra field is used to store platform or application specific
+data. See Pkware's document 'appnote.txt' for information on its format.
+
+
+\membersection{wxZipEntry::GetFlags}\label{wxzipentrygetflags}
+
+\constfunc{int}{GetFlags}{\void}
+
+Returns a combination of the bits flags in the enumeration {\tt wxZipFlags}.
+
+
+\membersection{wxZipEntry::GetInternalName}\label{wxzipentrygetinternalname}
+
+\constfunc{wxString}{GetInternalName}{\void}
+
+Returns the entry's filename in the internal format used within the
+archive. The name can include directory components, i.e. it can be a
+full path.
+
+The names of directory entries are returned without any trailing path
+separator. This gives a canonical name that can be used in comparisons.
+
+\func{wxString}{GetInternalName}{\param{const wxString\& }{name}, \param{wxPathFormat }{format = wxPATH\_NATIVE}, \param{bool* }{pIsDir = NULL}}
+
+A static member that translates a filename into the internal format used
+within the archive. If the third parameter is provided, the bool pointed
+to is set to indicate whether the name looks like a directory name
+(i.e. has a trailing path separator).
+
+\wxheading{See also}
+
+\helpref{Looking up an archive entry by name}{wxarcbyname}
+
+
+\membersection{wxZipEntry::Get/SetLocalExtra}\label{wxzipentrylocalextra}
+
+\constfunc{const char*}{GetLocalExtra}{\void}
+
+\constfunc{size\_t}{GetLocalExtraLen}{\void}
+
+\func{void}{SetLocalExtra}{\param{const char* }{extra}, \param{size\_t }{len}}
+
+The extra field from the entry's local record.
+
+The extra field is used to store platform or application specific
+data. See Pkware's document 'appnote.txt' for information on its format.
+
+
+\membersection{wxZipEntry::Get/SetMethod}\label{wxzipentrymethod}
+
+\constfunc{int}{GetMethod}{\void}
+
+\func{void}{SetMethod}{\param{int }{method}}
+
+The compression method. The enumeration {\tt wxZipMethod} lists the
+possible values.
+
+The default constructor sets this to wxZIP\_METHOD\_DEFAULT,
+which allows \helpref{wxZipOutputStream}{wxzipoutputstream} to
+choose the method when writing the entry.
+
+
+\membersection{wxZipEntry::Get/SetMode}\label{wxzipentrymode}
+
+\constfunc{int}{GetMode}{\void}
+
+If \helpref{IsMadeByUnix()}{wxzipentryismadebyunix} is true then
+returns the unix permission bits stored in
+ \helpref{GetExternalAttributes()}{wxzipentryexternalattributes}.
+Otherwise synthesises them from the DOS attributes.
+
+\func{void}{SetMode}{\param{int }{mode}}
+
+Sets the DOS attributes
+in \helpref{GetExternalAttributes()}{wxzipentryexternalattributes}
+to be consistent with the {\tt mode} given.
+
+If \helpref{IsMadeByUnix()}{wxzipentryismadebyunix} is true then also
+stores {\tt mode} in GetExternalAttributes().
+
+Note that the default constructor
+sets \helpref{GetSystemMadeBy()}{wxzipentrysystemmadeby} to
+wxZIP\_SYSTEM\_MSDOS by default. So to be able to store unix
+permissions when creating zips, call SetSystemMadeBy(wxZIP\_SYSTEM\_UNIX).
+
+
+\membersection{wxZipEntry::SetNotifier}\label{wxzipentrynotifier}
+
+\func{void}{SetNotifier}{\param{wxZipNotifier\& }{notifier}}
+
+\func{void}{UnsetNotifier}{\void}
+
+Sets the \helpref{notifier}{wxzipnotifier} for this entry.
+Whenever the \helpref{wxZipInputStream}{wxzipinputstream} updates
+this entry, it will then invoke the associated
+notifier's \helpref{OnEntryUpdated}{wxzipnotifieronentryupdated}
+method.
+
+Setting a notifier is not usually necessary. It is used to handle
+certain cases when modifying an zip in a pipeline (i.e. between
+non-seekable streams).
+
+\wxheading{See also}
+
+\helpref{Archives on non-seekable streams}{wxarcnoseek}\\
+\helpref{wxZipNotifier}{wxzipnotifier}
+
+
+\membersection{wxZipEntry::Get/SetSystemMadeBy}\label{wxzipentrysystemmadeby}
+
+\constfunc{int}{GetSystemMadeBy}{\void}
+
+\func{void}{SetSystemMadeBy}{\param{int }{system}}
+
+The originating file-system. The default constructor sets this to
+wxZIP\_SYSTEM\_MSDOS. Set it to wxZIP\_SYSTEM\_UNIX in order to be
+able to store unix permissions using \helpref{SetMode()}{wxzipentrymode}.
+
+
+\membersection{wxZipEntry::IsMadeByUnix}\label{wxzipentryismadebyunix}
+
+\constfunc{bool}{IsMadeByUnix}{\void}
+
+Returns true if \helpref{GetSystemMadeBy()}{wxzipentrysystemmadeby}
+is a flavour of unix.
+
+
+\membersection{wxZipEntry::IsText/SetIsText}\label{wxzipentryistext}
+
+\constfunc{bool}{IsText}{\void}
+
+\func{void}{SetIsText}{\param{bool }{isText = true}}
+
+Indicates that this entry's data is text in an 8-bit encoding.
+
+
+\membersection{wxZipEntry::operator=}\label{wxzipentryoperatorassign}
+
+\func{wxZipEntry\& operator}{operator=}{\param{const wxZipEntry\& }{entry}}
+
+Assignment operator.
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/zipstrm.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxZipInputStream}}\label{wxzipinputstream}
+
+Input stream for reading zip files.
+
+\helpref{GetNextEntry()}{wxzipinputstreamgetnextentry} returns an
+ \helpref{wxZipEntry}{wxzipentry} object containing the meta-data
+for the next entry in the zip (and gives away ownership). Reading from
+the wxZipInputStream then returns the entry's data. Eof() becomes true
+after an attempt has been made to read past the end of the entry's data.
+When there are no more entries, GetNextEntry() returns NULL and sets Eof().
+
+\wxheading{Derived from}
+
+\helpref{wxArchiveInputStream}{wxarchiveinputstream}
+
+\wxheading{Include files}
+
+<wx/zipstrm.h>
+
+\wxheading{Data structures}
+{\small \begin{verbatim}
+typedef wxZipEntry entry\_type
+\end{verbatim}}
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{wxZipEntry}{wxzipentry}\\
+\helpref{wxZipOutputStream}{wxzipoutputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
\membersection{wxZipInputStream::wxZipInputStream}\label{wxzipinputstreamwxzipinputstream}
+\func{}{wxZipInputStream}{\param{wxInputStream\& }{stream}, \param{wxMBConv\& }{conv = wxConvLocal}}
+
+Constructor. In a Unicode build the second parameter {\tt conv} is
+used to translate the filename and comment fields into Unicode. It has
+no effect on the stream's data.
+
\func{}{wxZipInputStream}{\param{const wxString\& }{archive}, \param{const wxString\& }{file}}
-Constructor.
+Compatibility constructor.
+
+
+\membersection{wxZipInputStream::CloseEntry}\label{wxzipinputstreamcloseentry}
+
+\func{bool}{CloseEntry}{\void}
+
+Closes the current entry. On a non-seekable stream reads to the end of
+the current entry first.
+
+
+\membersection{wxZipInputStream::GetComment}\label{wxzipinputstreamgetcomment}
+
+\func{wxString}{GetComment}{\void}
+
+Returns the zip comment.
+
+This is stored a the end of the zip, therefore when reading a zip
+from a non-seekable stream, it returns the empty string until the
+end of the zip has been reached, i.e. when GetNextEntry() returns
+NULL.
+
+
+\membersection{wxZipInputStream::GetNextEntry}\label{wxzipinputstreamgetnextentry}
+
+\func{wxZipEntry*}{GetNextEntry}{\void}
+
+Closes the current entry if one is open, then reads the meta-data for
+the next entry and returns it in a \helpref{wxZipEntry}{wxzipentry}
+object, giving away ownership. The stream is then open and can be read.
+
+
+\membersection{wxZipInputStream::GetTotalEntries}\label{wxzipinputstreamgettotalentries}
+
+\func{int}{GetTotalEntries}{\void}
+
+For a zip on a seekable stream returns the total number of entries in
+the zip. For zips on non-seekable streams returns the number of entries
+returned so far by \helpref{GetNextEntry()}{wxzipinputstreamgetnextentry}.
+
+
+\membersection{wxZipInputStream::OpenEntry}\label{wxzipinputstreamopenentry}
+
+\func{bool}{OpenEntry}{\param{wxZipEntry\& }{entry}}
+
+Closes the current entry if one is open, then opens the entry specified
+by the {\it entry} object.
+
+{\it entry} should be from the same zip file, and the zip should
+be on a seekable stream.
+
+\wxheading{See also}
+
+\helpref{Looking up an archive entry by name}{wxarcbyname}
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/zipstrm.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxZipNotifier}}\label{wxzipnotifier}
+
+If you need to know when a \helpref{wxZipInputStream}{wxzipinputstream}
+updates a \helpref{wxZipEntry}{wxzipentry},
+you can create a notifier by deriving from this abstract base class,
+overriding \helpref{OnEntryUpdated()}{wxzipnotifieronentryupdated}.
+An instance of your notifier class can then be assigned to wxZipEntry
+objects, using \helpref{wxZipEntry::SetNotifier()}{wxzipentrynotifier}.
+
+Setting a notifier is not usually necessary. It is used to handle
+certain cases when modifying an zip in a pipeline (i.e. between
+non-seekable streams).
+See '\helpref{Archives on non-seekable streams}{wxarcnoseek}'.
+
+\wxheading{Derived from}
+
+No base class
+
+\wxheading{Include files}
+
+<wx/zipstrm.h>
+
+\wxheading{See also}
+
+\helpref{Archives on non-seekable streams}{wxarcnoseek}\\
+\helpref{wxZipEntry}{wxzipentry}\\
+\helpref{wxZipInputStream}{wxzipinputstream}\\
+\helpref{wxZipOutputStream}{wxzipoutputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxZipNotifier::OnEntryUpdated}\label{wxzipnotifieronentryupdated}
+
+\func{void}{OnEntryUpdated}{\param{wxZipEntry\& }{entry}}
+
+Override this to receive notifications when
+an \helpref{wxZipEntry}{wxzipentry} object changes.
+
+
+%
+% automatically generated by HelpGen $Revision$ from
+% wx/zipstrm.h at 16/Sep/04 12:19:29
+%
+
+\section{\class{wxZipOutputStream}}\label{wxzipoutputstream}
+
+Output stream for writing zip files.
+
+\helpref{PutNextEntry()}{wxzipoutputstreamputnextentry} is used to create
+a new entry in the output zip, then the entry's data is written to the
+wxZipOutputStream. Another call to PutNextEntry() closes the current
+entry and begins the next.
+
+\wxheading{Derived from}
+
+\helpref{wxArchiveOutputStream}{wxarchiveoutputstream}
+
+\wxheading{Include files}
+
+<wx/zipstrm.h>
+
+\wxheading{See also}
+
+\helpref{Archive formats such as zip}{wxarc}\\
+\helpref{wxZipEntry}{wxzipentry}\\
+\helpref{wxZipInputStream}{wxzipinputstream}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxZipOutputStream::wxZipOutputStream}\label{wxzipoutputstreamwxzipoutputstream}
+
+\func{}{wxZipOutputStream}{\param{wxOutputStream\& }{stream}, \param{int }{level = -1}, \param{wxMBConv\& }{conv = wxConvLocal}}
+
+Constructor. {\tt level} is the compression level to use.
+It can be a value between 0 and 9 or -1 to use the default value
+which currently is equivalent to 6.
+
+In a Unicode build the third parameter {\tt conv} is used to translate
+the filename and comment fields to Unicode. It has no effect on the
+stream's data.
+
+
+\membersection{wxZipOutputStream::\destruct{wxZipOutputStream}}\label{wxzipoutputstreamdtor}
+
+\func{}{\destruct{wxZipOutputStream}}{\void}
+
+The destructor calls \helpref{Close()}{wxzipoutputstreamclose} to finish
+writing the zip if it has not been called already.
+
+
+\membersection{wxZipOutputStream::Close}\label{wxzipoutputstreamclose}
+
+\func{bool}{Close}{\void}
+
+Finishes writing the zip, returning true if successfully.
+Called by the destructor if not called explicitly.
+
+
+\membersection{wxZipOutputStream::CloseEntry}\label{wxzipoutputstreamcloseentry}
+
+\func{bool}{CloseEntry}{\void}
+
+Close the current entry. It is called implicitly whenever another new
+entry is created with \helpref{CopyEntry()}{wxzipoutputstreamcopyentry}
+or \helpref{PutNextEntry()}{wxzipoutputstreamputnextentry}, or
+when the zip is closed.
+
+
+\membersection{wxZipOutputStream::CopyArchiveMetaData}\label{wxzipoutputstreamcopyarchivemetadata}
+
+\func{bool}{CopyArchiveMetaData}{\param{wxZipInputStream\& }{inputStream}}
+
+Transfers the zip comment from the \helpref{wxZipInputStream}{wxzipinputstream}
+to this output stream.
+
+
+\membersection{wxZipOutputStream::CopyEntry}\label{wxzipoutputstreamcopyentry}
+
+\func{bool}{CopyEntry}{\param{wxZipEntry* }{entry}, \param{wxZipInputStream\& }{inputStream}}
+
+Takes ownership of {\tt entry} and uses it to create a new entry
+in the zip. {\tt entry} is then opened in {\tt inputStream} and its contents
+copied to this stream.
+
+CopyEntry() is much more efficient than transferring the data using
+Read() and Write() since it will copy them without decompressing and
+recompressing them.
+
+For zips on seekable streams, {\tt entry} must be from the same zip file
+as {\tt stream}. For non-seekable streams, {\tt entry} must also be the
+last thing read from {\tt inputStream}.
+
+
+\membersection{wxZipOutputStream::Get/SetLevel}\label{wxzipoutputstreamlevel}
+
+\constfunc{int}{GetLevel}{\void}
+
+\func{void}{SetLevel}{\param{int }{level}}
+
+Set the compression level that will be used the next time an entry is
+created. It can be a value between 0 and 9 or -1 to use the default value
+which currently is equivalent to 6.
+
+
+\membersection{wxZipOutputStream::PutNextDirEntry}\label{wxzipoutputstreamputnextdirentry}
+
+\func{bool}{PutNextDirEntry}{\param{const wxString\& }{name}, \param{const wxDateTime\& }{dt = wxDateTime::Now()}}
+
+Create a new directory entry
+(see \helpref{wxArchiveEntry::IsDir()}{wxarchiveentryisdir})
+with the given name and timestamp.
+
+\helpref{PutNextEntry()}{wxzipoutputstreamputnextentry} can
+also be used to create directory entries, by supplying a name with
+a trailing path separator.
+
+
+\membersection{wxZipOutputStream::PutNextEntry}\label{wxzipoutputstreamputnextentry}
+
+\func{bool}{PutNextEntry}{\param{wxZipEntry* }{entry}}
+
+Takes ownership of {\tt entry} and uses it to create a new entry
+in the zip.
+
+\func{bool}{PutNextEntry}{\param{const wxString\& }{name}, \param{const wxDateTime\& }{dt = wxDateTime::Now()}, \param{off\_t }{size = wxInvalidOffset}}
+
+Create a new entry with the given name, timestamp and size.
+
-\wxheading{Parameters}
+\membersection{wxZipOutputStream::SetComment}\label{wxzipoutputstreamsetcomment}
-\docparam{archive}{name of ZIP file}
+\func{void}{SetComment}{\param{const wxString\& }{comment}}
-\docparam{file}{name of file stored in the archive}
+Sets a comment for the zip as a whole. It is written at the end of the
+zip.
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: archive.h
+// Purpose: Streams for archive formats
+// Author: Mike Wetherell
+// RCS-ID: $Id$
+// Copyright: (c) 2004 Mike Wetherell
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_ARCHIVE_H__
+#define _WX_ARCHIVE_H__
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+#pragma interface "archive.h"
+#endif
+
+#include "wx/defs.h"
+
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
+#include "wx/stream.h"
+#include "wx/filename.h"
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveNotifier
+
+class WXDLLIMPEXP_BASE wxArchiveNotifier
+{
+public:
+ virtual ~wxArchiveNotifier() { }
+
+ virtual void OnEntryUpdated(class wxArchiveEntry& entry) = 0;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveEntry
+//
+// Holds an entry's meta data, such as filename and timestamp.
+
+class WXDLLIMPEXP_BASE wxArchiveEntry : public wxObject
+{
+public:
+ virtual ~wxArchiveEntry() { }
+
+ virtual wxDateTime GetDateTime() const = 0;
+ virtual wxFileOffset GetSize() const = 0;
+ virtual wxFileOffset GetOffset() const = 0;
+ virtual bool IsDir() const = 0;
+ virtual bool IsReadOnly() const = 0;
+ virtual wxString GetInternalName() const = 0;
+ virtual wxPathFormat GetInternalFormat() const = 0;
+ virtual wxString GetName(wxPathFormat format = wxPATH_NATIVE) const = 0;
+
+ virtual void SetDateTime(const wxDateTime& dt) = 0;
+ virtual void SetSize(wxFileOffset size) = 0;
+ virtual void SetIsDir(bool isDir = true) = 0;
+ virtual void SetIsReadOnly(bool isReadOnly = true) = 0;
+ virtual void SetName(const wxString& name,
+ wxPathFormat format = wxPATH_NATIVE) = 0;
+
+ wxArchiveEntry *Clone() const { return DoClone(); }
+
+ void SetNotifier(wxArchiveNotifier& notifier);
+ virtual void UnsetNotifier() { m_notifier = NULL; }
+
+protected:
+ wxArchiveEntry() : m_notifier(NULL) { }
+
+ virtual void SetOffset(wxFileOffset offset) = 0;
+ virtual wxArchiveEntry* DoClone() const = 0;
+
+ wxArchiveNotifier *GetNotifier() const { return m_notifier; }
+ wxArchiveEntry& operator=(const wxArchiveEntry& entry);
+
+private:
+ wxArchiveNotifier *m_notifier;
+
+ DECLARE_ABSTRACT_CLASS(wxArchiveEntry)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveInputStream
+//
+// GetNextEntry() returns an wxArchiveEntry object containing the meta-data
+// for the next entry in the archive (and gives away ownership). Reading from
+// the wxArchiveInputStream then returns the entry's data. Eof() becomes true
+// after an attempt has been made to read past the end of the entry's data.
+//
+// When there are no more entries, GetNextEntry() returns NULL and sets Eof().
+
+class WXDLLIMPEXP_BASE wxArchiveInputStream : public wxFilterInputStream
+{
+public:
+ typedef wxArchiveEntry entry_type;
+
+ virtual ~wxArchiveInputStream() { }
+
+ virtual bool OpenEntry(wxArchiveEntry& entry) = 0;
+ virtual bool CloseEntry() = 0;
+
+ wxArchiveEntry *GetNextEntry() { return DoGetNextEntry(); }
+
+ virtual char Peek() { return wxInputStream::Peek(); }
+
+protected:
+ wxArchiveInputStream(wxInputStream& stream, wxMBConv& conv);
+
+ virtual wxArchiveEntry *DoGetNextEntry() = 0;
+
+ wxMBConv& GetConv() const { return m_conv; }
+
+private:
+ wxMBConv& m_conv;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveOutputStream
+//
+// PutNextEntry is used to create a new entry in the output archive, then
+// the entry's data is written to the wxArchiveOutputStream.
+//
+// Only one entry can be open for output at a time; another call to
+// PutNextEntry closes the current entry and begins the next.
+//
+// The overload 'bool PutNextEntry(wxArchiveEntry *entry)' takes ownership
+// of the entry object.
+
+class WXDLLIMPEXP_BASE wxArchiveOutputStream : public wxFilterOutputStream
+{
+public:
+ virtual ~wxArchiveOutputStream() { }
+
+ virtual bool PutNextEntry(wxArchiveEntry *entry) = 0;
+
+ virtual bool PutNextEntry(const wxString& name,
+ const wxDateTime& dt = wxDateTime::Now(),
+ wxFileOffset size = wxInvalidOffset) = 0;
+
+ virtual bool PutNextDirEntry(const wxString& name,
+ const wxDateTime& dt = wxDateTime::Now()) = 0;
+
+ virtual bool CopyEntry(wxArchiveEntry *entry,
+ wxArchiveInputStream& stream) = 0;
+
+ virtual bool CopyArchiveMetaData(wxArchiveInputStream& stream) = 0;
+
+ virtual bool CloseEntry() = 0;
+ virtual bool Close() = 0;
+
+protected:
+ wxArchiveOutputStream(wxOutputStream& stream, wxMBConv& conv);
+
+ wxMBConv& GetConv() const { return m_conv; }
+
+private:
+ wxMBConv& m_conv;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveClassFactory
+//
+// A wxArchiveClassFactory instance for a particular archive type allows
+// the creation of the other classes that may be needed.
+
+class WXDLLIMPEXP_BASE wxArchiveClassFactory : public wxObject
+{
+public:
+ virtual ~wxArchiveClassFactory() { }
+
+ wxArchiveEntry *NewEntry() const
+ { return DoNewEntry(); }
+ wxArchiveInputStream *NewStream(wxInputStream& stream) const
+ { return DoNewStream(stream); }
+ wxArchiveOutputStream *NewStream(wxOutputStream& stream) const
+ { return DoNewStream(stream); }
+
+ virtual wxString GetInternalName(
+ const wxString& name,
+ wxPathFormat format = wxPATH_NATIVE) const = 0;
+
+ void SetConv(wxMBConv& conv) { m_pConv = &conv; }
+ wxMBConv& GetConv() const { return *m_pConv; }
+
+protected:
+ virtual wxArchiveEntry *DoNewEntry() const = 0;
+ virtual wxArchiveInputStream *DoNewStream(wxInputStream& stream) const = 0;
+ virtual wxArchiveOutputStream *DoNewStream(wxOutputStream& stream) const = 0;
+
+ wxArchiveClassFactory() : m_pConv(&wxConvLocal) { }
+ wxArchiveClassFactory& operator=(const wxArchiveClassFactory& WXUNUSED(f))
+ { return *this; }
+
+private:
+ wxMBConv *m_pConv;
+
+ DECLARE_ABSTRACT_CLASS(wxArchiveClassFactory)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveIterator
+//
+// An input iterator that can be used to transfer an archive's catalog to
+// a container.
+
+#if wxUSE_STL || defined WX_TEST_ARCHIVE_ITERATOR
+#include <iterator>
+#include <utility>
+
+template <class X, class Y>
+void WXDLLIMPEXP_BASE _wxSetArchiveIteratorValue(
+ X& val, Y entry, void *WXUNUSED(d))
+{
+ val = X(entry);
+}
+template <class X, class Y, class Z>
+void WXDLLIMPEXP_BASE _wxSetArchiveIteratorValue(
+ std::pair<X, Y>& val, Z entry, Z WXUNUSED(d))
+{
+ val = std::make_pair(X(entry->GetInternalName()), Y(entry));
+}
+
+#if defined _MSC_VER && _MSC_VER < 1300
+template <class Arc, class T = Arc::entry_type*>
+#else
+template <class Arc, class T = typename Arc::entry_type*>
+#endif
+class WXDLLIMPEXP_BASE wxArchiveIterator
+{
+public:
+ typedef std::input_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ wxArchiveIterator() : m_rep(NULL) { }
+
+ wxArchiveIterator(Arc& arc) {
+ typename Arc::entry_type* entry = arc.GetNextEntry();
+ m_rep = entry ? new Rep(arc, entry) : NULL;
+ }
+
+ wxArchiveIterator(const wxArchiveIterator& it) : m_rep(it.m_rep) {
+ if (m_rep)
+ m_rep->AddRef();
+ }
+
+ ~wxArchiveIterator() {
+ if (m_rep)
+ m_rep->UnRef();
+ }
+
+ const T& operator *() const {
+ return m_rep->GetValue();
+ }
+
+ const T* operator ->() const {
+ return &**this;
+ }
+
+ wxArchiveIterator& operator =(const wxArchiveIterator& it) {
+ if (it.m_rep)
+ it.m_rep.AddRef();
+ if (m_rep)
+ m_rep.UnRef();
+ m_rep = it.m_rep;
+ return *this;
+ }
+
+ wxArchiveIterator& operator ++() {
+ m_rep = m_rep->Next();
+ return *this;
+ }
+
+ wxArchiveIterator operator ++(int) {
+ wxArchiveIterator it(*this);
+ ++(*this);
+ return it;
+ }
+
+ friend bool operator ==(const wxArchiveIterator& i,
+ const wxArchiveIterator& j) {
+ return i.m_rep == j.m_rep;
+ }
+
+ friend bool operator !=(const wxArchiveIterator& i,
+ const wxArchiveIterator& j) {
+ return !(i == j);
+ }
+
+private:
+ class Rep {
+ Arc& m_arc;
+ typename Arc::entry_type* m_entry;
+ T m_value;
+ int m_ref;
+
+ public:
+ Rep(Arc& arc, typename Arc::entry_type* entry)
+ : m_arc(arc), m_entry(entry), m_value(), m_ref(1) { }
+ ~Rep()
+ { delete m_entry; }
+
+ void AddRef() {
+ m_ref++;
+ }
+
+ void UnRef() {
+ if (--m_ref == 0)
+ delete this;
+ }
+
+ Rep *Next() {
+ typename Arc::entry_type* entry = m_arc.GetNextEntry();
+ if (!entry) {
+ UnRef();
+ return NULL;
+ }
+ if (m_ref > 1) {
+ m_ref--;
+ return new Rep(m_arc, entry);
+ }
+ delete m_entry;
+ m_entry = entry;
+ m_value = T();
+ return this;
+ }
+
+ const T& GetValue() {
+ if (m_entry) {
+ _wxSetArchiveIteratorValue(m_value, m_entry, m_entry);
+ m_entry = NULL;
+ }
+ return m_value;
+ }
+ } *m_rep;
+};
+
+typedef wxArchiveIterator<wxArchiveInputStream> wxArchiveIter;
+typedef wxArchiveIterator<wxArchiveInputStream,
+ std::pair<wxString, wxArchiveEntry*> > wxArchivePairIter;
+
+#endif // wxUSE_STL || defined WX_TEST_ARCHIVE_ITERATOR
+
+#endif // wxUSE_STREAMS
+
+#endif // _WX_ARCHIVE_H__
private:
// these vars are used by FindFirst/Next:
- void *m_Archive;
+ class wxZipInputStream *m_Archive;
wxString m_Pattern, m_BaseDir, m_ZipFile;
bool m_AllowDirs, m_AllowFiles;
wxLongToLongHashMap *m_DirsFound;
wxString DoFind();
+ void CloseArchive(class wxZipInputStream *archive);
DECLARE_NO_COPY_CLASS(wxZipFSHandler)
};
/////////////////////////////////////////////////////////////////////////////
-// Name: zipstream.h
-// Purpose: wxZipInputStream for reading files from ZIP archive
-// Author: Vaclav Slavik
-// Copyright: (c) 1999 Vaclav Slavik
+// Name: zipstrm.h
+// Purpose: Streams for Zip files
+// Author: Mike Wetherell
+// RCS-ID: $Id$
+// Copyright: (c) Mike Wetherell
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#ifndef __ZIPSTREAM_H__
-#define __ZIPSTREAM_H__
+#ifndef _WX_WXZIPSTREAM_H__
+#define _WX_WXZIPSTREAM_H__
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma interface "zipstrm.h"
#include "wx/defs.h"
-#if wxUSE_STREAMS && wxUSE_ZIPSTREAM && wxUSE_ZLIB
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
-#include "wx/stream.h"
+#include "wx/archive.h"
+#include "wx/hashmap.h"
+#include "wx/filename.h"
-//--------------------------------------------------------------------------------
-// wxZipInputStream
-// This class is input stream from ZIP archive. The archive
-// must be local file (accessible via FILE*)
-//--------------------------------------------------------------------------------
+/////////////////////////////////////////////////////////////////////////////
+// constants
+
+// Compression Method, only 0 (store) and 8 (deflate) are supported here
+//
+enum wxZipMethod
+{
+ wxZIP_METHOD_STORE,
+ wxZIP_METHOD_SHRINK,
+ wxZIP_METHOD_REDUCE1,
+ wxZIP_METHOD_REDUCE2,
+ wxZIP_METHOD_REDUCE3,
+ wxZIP_METHOD_REDUCE4,
+ wxZIP_METHOD_IMPLODE,
+ wxZIP_METHOD_TOKENIZE,
+ wxZIP_METHOD_DEFLATE,
+ wxZIP_METHOD_DEFLATE64,
+ wxZIP_METHOD_BZIP2 = 12,
+ wxZIP_METHOD_DEFAULT = 0xffff
+};
+
+// Originating File-System.
+//
+// These are Pkware's values. Note that Info-zip disagree on some of them,
+// most notably NTFS.
+//
+enum wxZipSystem
+{
+ wxZIP_SYSTEM_MSDOS,
+ wxZIP_SYSTEM_AMIGA,
+ wxZIP_SYSTEM_OPENVMS,
+ wxZIP_SYSTEM_UNIX,
+ wxZIP_SYSTEM_VM_CMS,
+ wxZIP_SYSTEM_ATARI_ST,
+ wxZIP_SYSTEM_OS2_HPFS,
+ wxZIP_SYSTEM_MACINTOSH,
+ wxZIP_SYSTEM_Z_SYSTEM,
+ wxZIP_SYSTEM_CPM,
+ wxZIP_SYSTEM_WINDOWS_NTFS,
+ wxZIP_SYSTEM_MVS,
+ wxZIP_SYSTEM_VSE,
+ wxZIP_SYSTEM_ACORN_RISC,
+ wxZIP_SYSTEM_VFAT,
+ wxZIP_SYSTEM_ALTERNATE_MVS,
+ wxZIP_SYSTEM_BEOS,
+ wxZIP_SYSTEM_TANDEM,
+ wxZIP_SYSTEM_OS_400
+};
+
+// Dos/Win file attributes
+//
+enum wxZipAttributes
+{
+ wxZIP_A_RDONLY = 0x01,
+ wxZIP_A_HIDDEN = 0x02,
+ wxZIP_A_SYSTEM = 0x04,
+ wxZIP_A_SUBDIR = 0x10,
+ wxZIP_A_ARCH = 0x20,
+
+ wxZIP_A_MASK = 0x37
+};
+
+// Values for the flags field in the zip headers
+//
+enum wxZipFlags
+{
+ wxZIP_ENCRYPTED = 0x0001,
+ wxZIP_DEFLATE_NORMAL = 0x0000, // normal compression
+ wxZIP_DEFLATE_EXTRA = 0x0002, // extra compression
+ wxZIP_DEFLATE_FAST = 0x0004, // fast compression
+ wxZIP_DEFLATE_SUPERFAST = 0x0006, // superfast compression
+ wxZIP_DEFLATE_MASK = 0x0006,
+ wxZIP_SUMS_FOLLOW = 0x0008, // crc and sizes come after the data
+ wxZIP_ENHANCED = 0x0010,
+ wxZIP_PATCH = 0x0020,
+ wxZIP_STRONG_ENC = 0x0040,
+ wxZIP_UNUSED = 0x0F80,
+ wxZIP_RESERVED = 0xF000
+};
+
+// Forward decls
+//
+class WXDLLIMPEXP_BASE wxZipEntry;
+class WXDLLIMPEXP_BASE wxZipInputStream;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxZipNotifier
-class WXDLLIMPEXP_BASE wxZipInputStream : public wxInputStream
+class WXDLLIMPEXP_BASE wxZipNotifier
{
public:
- wxZipInputStream(const wxString& archive, const wxString& file);
- // archive is name of .zip archive, file is name of file to be extracted.
- // Remember that archive must be local file accesible via fopen, fread functions!
- ~wxZipInputStream();
+ virtual ~wxZipNotifier() { }
+
+ virtual void OnEntryUpdated(wxZipEntry& entry) = 0;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Zip Entry - holds the meta data for a file in the zip
+
+class WXDLLIMPEXP_BASE wxZipEntry : public wxArchiveEntry
+{
+public:
+ wxZipEntry(const wxString& name = wxEmptyString,
+ const wxDateTime& dt = wxDateTime::Now(),
+ wxFileOffset size = wxInvalidOffset);
+ virtual ~wxZipEntry();
+
+ wxZipEntry(const wxZipEntry& entry);
+ wxZipEntry& operator=(const wxZipEntry& entry);
+
+ // Get accessors
+ wxDateTime GetDateTime() const { return m_DateTime; }
+ wxFileOffset GetSize() const { return m_Size; }
+ wxFileOffset GetOffset() const { return m_Offset; }
+ wxString GetInternalName() const { return m_Name; }
+ int GetMethod() const { return m_Method; }
+ int GetFlags() const { return m_Flags; }
+ wxUint32 GetCrc() const { return m_Crc; }
+ wxFileOffset GetCompressedSize() const { return m_CompressedSize; }
+ int GetSystemMadeBy() const { return m_SystemMadeBy; }
+ wxString GetComment() const { return m_Comment; }
+ wxUint32 GetExternalAttributes() const { return m_ExternalAttributes; }
+ wxPathFormat GetInternalFormat() const { return wxPATH_UNIX; }
+ int GetMode() const;
+ const char *GetLocalExtra() const;
+ size_t GetLocalExtraLen() const;
+ const char *GetExtra() const;
+ size_t GetExtraLen() const;
+ wxString GetName(wxPathFormat format = wxPATH_NATIVE) const;
+
+ // is accessors
+ inline bool IsDir() const;
+ inline bool IsText() const;
+ inline bool IsReadOnly() const;
+ inline bool IsMadeByUnix() const;
+
+ // set accessors
+ void SetDateTime(const wxDateTime& dt) { m_DateTime = dt; }
+ void SetSize(wxFileOffset size) { m_Size = size; }
+ void SetMethod(int method) { m_Method = method; }
+ void SetComment(const wxString& comment) { m_Comment = comment; }
+ void SetExternalAttributes(wxUint32 attr ) { m_ExternalAttributes = attr; }
+ void SetSystemMadeBy(int system);
+ void SetMode(int mode);
+ void SetExtra(const char *extra, size_t len);
+ void SetLocalExtra(const char *extra, size_t len);
+
+ inline void SetName(const wxString& name,
+ wxPathFormat format = wxPATH_NATIVE);
+
+ static wxString GetInternalName(const wxString& name,
+ wxPathFormat format = wxPATH_NATIVE,
+ bool *pIsDir = NULL);
+
+ // set is accessors
+ void SetIsDir(bool isDir = true);
+ inline void SetIsReadOnly(bool isReadOnly = true);
+ inline void SetIsText(bool isText = true);
- virtual wxFileOffset GetLength() const {return m_Size;}
- virtual bool Eof() const;
+ wxZipEntry *Clone() const { return ZipClone(); }
+
+ void SetNotifier(wxZipNotifier& notifier);
+ void UnsetNotifier();
protected:
- virtual size_t OnSysRead(void *buffer, size_t bufsize);
- virtual wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode);
- virtual wxFileOffset OnSysTell() const {return m_Pos;}
+ // Internal attributes
+ enum { TEXT_ATTR = 1 };
+
+ // protected Get accessors
+ int GetVersionNeeded() const { return m_VersionNeeded; }
+ wxFileOffset GetKey() const { return m_Key; }
+ int GetVersionMadeBy() const { return m_VersionMadeBy; }
+ int GetDiskStart() const { return m_DiskStart; }
+ int GetInternalAttributes() const { return m_InternalAttributes; }
+
+ void SetVersionNeeded(int version) { m_VersionNeeded = version; }
+ void SetOffset(wxFileOffset offset) { m_Offset = offset; }
+ void SetFlags(int flags) { m_Flags = flags; }
+ void SetVersionMadeBy(int version) { m_VersionMadeBy = version; }
+ void SetCrc(wxUint32 crc) { m_Crc = crc; }
+ void SetCompressedSize(wxFileOffset size) { m_CompressedSize = size; }
+ void SetKey(wxFileOffset offset) { m_Key = offset; }
+ void SetDiskStart(int start) { m_DiskStart = start; }
+ void SetInternalAttributes(int attr) { m_InternalAttributes = attr; }
+
+ virtual wxZipEntry *ZipClone() const { return new wxZipEntry(*this); }
+
+ void Notify();
private:
+ wxArchiveEntry* DoClone() const { return ZipClone(); }
+
+ size_t ReadLocal(wxInputStream& stream, wxMBConv& conv);
+ size_t WriteLocal(wxOutputStream& stream, wxMBConv& conv) const;
+
+ size_t ReadCentral(wxInputStream& stream, wxMBConv& conv);
+ size_t WriteCentral(wxOutputStream& stream, wxMBConv& conv) const;
+
+ size_t ReadDescriptor(wxInputStream& stream);
+ size_t WriteDescriptor(wxOutputStream& stream, wxUint32 crc,
+ wxFileOffset compressedSize, wxFileOffset size);
+
+ wxUint8 m_SystemMadeBy; // one of enum wxZipSystem
+ wxUint8 m_VersionMadeBy; // major * 10 + minor
+
+ wxUint16 m_VersionNeeded; // ver needed to extract (20 i.e. v2.0)
+ wxUint16 m_Flags;
+ wxUint16 m_Method; // compression method (one of wxZipMethod)
+ wxDateTime m_DateTime;
+ wxUint32 m_Crc;
+ wxFileOffset m_CompressedSize;
wxFileOffset m_Size;
- wxFileOffset m_Pos;
+ wxString m_Name; // in internal format
+ wxFileOffset m_Key; // the original offset for copied entries
+ wxFileOffset m_Offset; // file offset of the entry
+
+ wxString m_Comment;
+ wxUint16 m_DiskStart; // for multidisk archives, not unsupported
+ wxUint16 m_InternalAttributes; // bit 0 set for text files
+ wxUint32 m_ExternalAttributes; // system specific depends on SystemMadeBy
+
+ class wxZipMemory *m_Extra;
+ class wxZipMemory *m_LocalExtra;
+
+ wxZipNotifier *m_zipnotifier;
+ class wxZipWeakLinks *m_backlink;
- // this void* is handle of archive . I'm sorry it is void and not proper
- // type but I don't want to make unzip.h header public.
- void *m_Archive;
+ friend class wxZipInputStream;
+ friend class wxZipOutputStream;
+
+ DECLARE_DYNAMIC_CLASS(wxZipEntry)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxZipOutputStream
+
+WX_DECLARE_LIST_WITH_DECL(wxZipEntry, _wxZipEntryList, class WXDLLIMPEXP_BASE);
+
+class WXDLLIMPEXP_BASE wxZipOutputStream : public wxArchiveOutputStream
+{
+public:
+ wxZipOutputStream(wxOutputStream& stream,
+ int level = -1,
+ wxMBConv& conv = wxConvLocal);
+ virtual ~wxZipOutputStream();
+
+ bool PutNextEntry(wxZipEntry *entry) { return DoCreate(entry); }
+
+ bool PutNextEntry(const wxString& name,
+ const wxDateTime& dt = wxDateTime::Now(),
+ wxFileOffset size = wxInvalidOffset);
+
+ bool PutNextDirEntry(const wxString& name,
+ const wxDateTime& dt = wxDateTime::Now());
+
+ bool CopyEntry(wxZipEntry *entry, wxZipInputStream& inputStream);
+ bool CopyArchiveMetaData(wxZipInputStream& inputStream);
+
+ void Sync();
+ bool CloseEntry();
+ bool Close();
+
+ void SetComment(const wxString& comment) { m_Comment = comment; }
+
+ int GetLevel() const { return m_level; }
+ void SetLevel(int level);
+
+protected:
+ virtual size_t OnSysWrite(const void *buffer, size_t size);
+ virtual wxFileOffset OnSysTell() const { return m_entrySize; }
+
+ struct Buffer { const char *m_data; size_t m_size; };
+ virtual wxOutputStream *OpenCompressor(wxOutputStream& stream,
+ wxZipEntry& entry,
+ const Buffer bufs[]);
+ virtual bool CloseCompressor(wxOutputStream *comp);
+
+ bool IsParentSeekable() const { return m_offsetAdjustment
+ != wxInvalidOffset; }
+
+private:
+ bool PutNextEntry(wxArchiveEntry *entry);
+ bool CopyEntry(wxArchiveEntry *entry, wxArchiveInputStream& stream);
+ bool CopyArchiveMetaData(wxArchiveInputStream& stream);
+
+ bool IsOpened() const { return m_comp || m_pending; }
+
+ bool DoCreate(wxZipEntry *entry, bool raw = false);
+ void CreatePendingEntry(const void *buffer, size_t size);
+ void CreatePendingEntry();
+
+ class wxStoredOutputStream *m_store;
+ class wxZlibOutputStream2 *m_deflate;
+ class wxZipStreamLink *m_backlink;
+ _wxZipEntryList m_entries;
+ char *m_initialData;
+ size_t m_initialSize;
+ wxZipEntry *m_pending;
+ bool m_raw;
+ wxFileOffset m_headerOffset;
+ size_t m_headerSize;
+ wxFileOffset m_entrySize;
+ wxUint32 m_crcAccumulator;
+ wxOutputStream *m_comp;
+ int m_level;
+ wxFileOffset m_offsetAdjustment;
+ wxString m_Comment;
+
+ DECLARE_NO_COPY_CLASS(wxZipOutputStream)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxZipInputStream
+
+class WXDLLIMPEXP_BASE wxZipInputStream : public wxArchiveInputStream
+{
+public:
+ typedef wxZipEntry entry_type;
+
+ wxZipInputStream(wxInputStream& stream, wxMBConv& conv = wxConvLocal);
+ wxZipInputStream(const wxString& archive, const wxString& file);
+ virtual ~wxZipInputStream();
+
+ bool OpenEntry(wxZipEntry& entry) { return DoOpen(&entry); }
+ bool CloseEntry();
+
+ wxZipEntry *GetNextEntry();
+
+ wxString GetComment();
+ int GetTotalEntries();
+
+ virtual wxFileOffset GetLength() const { return m_entry.GetSize(); }
+
+protected:
+ size_t OnSysRead(void *buffer, size_t size);
+ wxFileOffset OnSysTell() const { return m_decomp ? m_decomp->TellI() : 0; }
+ wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode);
+
+ virtual wxInputStream *OpenDecompressor(wxInputStream& stream);
+ virtual bool CloseDecompressor(wxInputStream *decomp);
+
+private:
+ void Init();
+ wxInputStream& OpenFile(const wxString& archive);
+
+ wxArchiveEntry *DoGetNextEntry() { return GetNextEntry(); }
+
+ bool OpenEntry(wxArchiveEntry& entry);
+
+ wxStreamError ReadLocal(bool readEndRec = false);
+ wxStreamError ReadCentral();
+
+ wxUint32 ReadSignature();
+ bool FindEndRecord();
+ bool LoadEndRecord();
+
+ bool AtHeader() const { return m_headerSize == 0; }
+ bool AfterHeader() const { return m_headerSize > 0 && !m_decomp; }
+ bool IsOpened() const { return m_decomp != NULL; }
+
+ wxZipStreamLink *MakeLink(wxZipOutputStream *out);
+
+ bool DoOpen(wxZipEntry *entry = NULL, bool raw = false);
+ bool OpenDecompressor(bool raw = false);
+
+ class wxStoredInputStream *m_store;
+ class wxZlibInputStream2 *m_inflate;
+ class wxRawInputStream *m_rawin;
+ class wxFFileInputStream *m_ffile;
+ wxZipEntry m_entry;
+ bool m_raw;
+ size_t m_headerSize;
+ wxUint32 m_crcAccumulator;
+ wxInputStream *m_decomp;
+ bool m_parentSeekable;
+ class wxZipWeakLinks *m_weaklinks;
+ class wxZipStreamLink *m_streamlink;
+ wxFileOffset m_offsetAdjustment;
+ wxFileOffset m_position;
+ wxUint32 m_signature;
+ size_t m_TotalEntries;
+ wxString m_Comment;
+
+ friend bool wxZipOutputStream::CopyEntry(
+ wxZipEntry *entry, wxZipInputStream& inputStream);
+ friend bool wxZipOutputStream::CopyArchiveMetaData(
+ wxZipInputStream& inputStream);
DECLARE_NO_COPY_CLASS(wxZipInputStream)
};
-#endif
- // wxUSE_STREAMS && wxUSE_ZIPSTREAM && wxUSE_ZLIB
+/////////////////////////////////////////////////////////////////////////////
+// wxZipClassFactory
+class WXDLLIMPEXP_BASE wxZipClassFactory : public wxArchiveClassFactory
+{
+public:
+ wxZipEntry *NewEntry() const
+ { return new wxZipEntry; }
+ wxZipInputStream *NewStream(wxInputStream& stream) const
+ { return new wxZipInputStream(stream, GetConv()); }
+ wxZipOutputStream *NewStream(wxOutputStream& stream) const
+ { return new wxZipOutputStream(stream, -1, GetConv()); }
+
+ wxString GetInternalName(const wxString& name,
+ wxPathFormat format = wxPATH_NATIVE) const
+ { return wxZipEntry::GetInternalName(name, format); }
+
+protected:
+ wxArchiveEntry *DoNewEntry() const
+ { return NewEntry(); }
+ wxArchiveInputStream *DoNewStream(wxInputStream& stream) const
+ { return NewStream(stream); }
+ wxArchiveOutputStream *DoNewStream(wxOutputStream& stream) const
+ { return NewStream(stream); }
+
+private:
+ DECLARE_DYNAMIC_CLASS(wxZipClassFactory)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Iterators
+
+#if wxUSE_STL || defined WX_TEST_ARCHIVE_ITERATOR
+typedef wxArchiveIterator<wxZipInputStream> wxZipIter;
+typedef wxArchiveIterator<wxZipInputStream,
+ std::pair<wxString, wxZipEntry*> > wxZipPairIter;
#endif
- // __ZIPSTREAM_H__
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxZipEntry inlines
+
+bool wxZipEntry::IsText() const
+{
+ return (m_InternalAttributes & TEXT_ATTR) != 0;
+}
+
+bool wxZipEntry::IsDir() const
+{
+ return (m_ExternalAttributes & wxZIP_A_SUBDIR) != 0;
+}
+
+bool wxZipEntry::IsReadOnly() const
+{
+ return (m_ExternalAttributes & wxZIP_A_RDONLY) != 0;
+}
+
+bool wxZipEntry::IsMadeByUnix() const
+{
+ const int pattern =
+ (1 << wxZIP_SYSTEM_OPENVMS) |
+ (1 << wxZIP_SYSTEM_UNIX) |
+ (1 << wxZIP_SYSTEM_ATARI_ST) |
+ (1 << wxZIP_SYSTEM_ACORN_RISC) |
+ (1 << wxZIP_SYSTEM_BEOS) | (1 << wxZIP_SYSTEM_TANDEM);
+
+ // note: some unix zippers put madeby = dos
+ return (m_SystemMadeBy == wxZIP_SYSTEM_MSDOS
+ && (m_ExternalAttributes & ~0xFFFF))
+ || ((pattern >> m_SystemMadeBy) & 1);
+}
+
+void wxZipEntry::SetIsText(bool isText)
+{
+ if (isText)
+ m_InternalAttributes |= TEXT_ATTR;
+ else
+ m_InternalAttributes &= ~TEXT_ATTR;
+}
+
+void wxZipEntry::SetIsReadOnly(bool isReadOnly)
+{
+ if (isReadOnly)
+ SetMode(GetMode() & ~0222);
+ else
+ SetMode(GetMode() | 0200);
+}
+
+void wxZipEntry::SetName(const wxString& name,
+ wxPathFormat format /*=wxPATH_NATIVE*/)
+{
+ bool isDir;
+ m_Name = GetInternalName(name, format, &isDir);
+ SetIsDir(isDir);
+}
+
+
+#endif // wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
+#endif // _WX_WXZIPSTREAM_H__
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: archive.cpp
+// Purpose: Streams for archive formats
+// Author: Mike Wetherell
+// RCS-ID: $Id$
+// Copyright: (c) Mike Wetherell
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+ #pragma implementation "archive.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/defs.h"
+#endif
+
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
+#include "wx/archive.h"
+#include "wx/html/forcelnk.h"
+
+IMPLEMENT_ABSTRACT_CLASS(wxArchiveEntry, wxObject)
+IMPLEMENT_ABSTRACT_CLASS(wxArchiveClassFactory, wxObject)
+
+FORCE_LINK(zipstrm)
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveInputStream
+
+wxArchiveInputStream::wxArchiveInputStream(wxInputStream& stream,
+ wxMBConv& conv)
+ : wxFilterInputStream(stream),
+ m_conv(conv)
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveOutputStream
+
+wxArchiveOutputStream::wxArchiveOutputStream(wxOutputStream& stream,
+ wxMBConv& conv)
+ : wxFilterOutputStream(stream),
+ m_conv(conv)
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxArchiveEntry
+
+void wxArchiveEntry::SetNotifier(wxArchiveNotifier& notifier)
+{
+ UnsetNotifier();
+ m_notifier = ¬ifier;
+ m_notifier->OnEntryUpdated(*this);
+}
+
+wxArchiveEntry& wxArchiveEntry::operator=(const wxArchiveEntry& entry)
+{
+ m_notifier = entry.m_notifier;
+ return *this;
+}
+
+#endif
#endif
#include "wx/filesys.h"
+#include "wx/wfstream.h"
#include "wx/zipstrm.h"
#include "wx/fs_zip.h"
-/* Not the right solution (paths in makefiles) but... */
-#ifdef __BORLANDC__
-#include "../common/unzip.h"
-#else
-#include "unzip.h"
-#endif
//----------------------------------------------------------------------------
// wxZipFSHandler
wxZipFSHandler::~wxZipFSHandler()
{
if (m_Archive)
- unzClose((unzFile)m_Archive);
+ CloseArchive(m_Archive);
if (m_DirsFound)
delete m_DirsFound;
}
+void wxZipFSHandler::CloseArchive(wxZipInputStream *archive)
+{
+ wxInputStream *stream = archive->GetFilterInputStream();
+ delete archive;
+ delete stream;
+}
+
+
+
bool wxZipFSHandler::CanOpen(const wxString& location)
{
wxString p = GetProtocol(location);
if (m_Archive)
{
- unzClose((unzFile)m_Archive);
+ CloseArchive(m_Archive);
m_Archive = NULL;
}
m_ZipFile = left;
wxString nativename = wxFileSystem::URLToFileName(m_ZipFile).GetFullPath();
- m_Archive = (void*) unzOpen(nativename.mb_str(wxConvFile));
+ m_Archive = new wxZipInputStream(*new wxFFileInputStream(nativename));
m_Pattern = right.AfterLast(wxT('/'));
m_BaseDir = right.BeforeLast(wxT('/'));
if (m_Archive)
{
- if (unzGoToFirstFile((unzFile)m_Archive) != UNZ_OK)
- {
- unzClose((unzFile)m_Archive);
- m_Archive = NULL;
- }
- else
+ if (m_AllowDirs)
{
- if (m_AllowDirs)
- {
- delete m_DirsFound;
- m_DirsFound = new wxLongToLongHashMap();
- }
- return DoFind();
+ delete m_DirsFound;
+ m_DirsFound = new wxLongToLongHashMap();
}
+ return DoFind();
}
return wxEmptyString;
}
wxString wxZipFSHandler::DoFind()
{
- static char namebuf[1024]; // char, not wxChar!
- char *c;
wxString namestr, dir, filename;
wxString match = wxEmptyString;
while (match == wxEmptyString)
{
- unzGetCurrentFileInfo((unzFile)m_Archive, NULL, namebuf, 1024, NULL, 0, NULL, 0);
- for (c = namebuf; *c; c++) if (*c == '\\') *c = '/';
- namestr = wxString::FromAscii(namebuf); // TODO what encoding does ZIP use?
+ wxZipEntry *entry = m_Archive->GetNextEntry();
+ if (!entry)
+ {
+ CloseArchive(m_Archive);
+ m_Archive = NULL;
+ break;
+ }
+ namestr = entry->GetName(wxPATH_UNIX);
+ delete entry;
if (m_AllowDirs)
{
if (m_AllowFiles && !filename.IsEmpty() && m_BaseDir == dir &&
wxMatchWild(m_Pattern, filename, false))
match = m_ZipFile + wxT("#zip:") + namestr;
-
- if (unzGoToNextFile((unzFile)m_Archive) != UNZ_OK)
- {
- unzClose((unzFile)m_Archive);
- m_Archive = NULL;
- break;
- }
}
return match;
/////////////////////////////////////////////////////////////////////////////
-// Name: zipstream.cpp
-// Purpose: input stream for ZIP archive access
-// Author: Vaclav Slavik
-// Copyright: (c) 1999 Vaclav Slavik
+// Name: zipstrm.cpp
+// Purpose: Streams for Zip files
+// Author: Mike Wetherell
+// RCS-ID: $Id$
+// Copyright: (c) Mike Wetherell
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "zipstrm.h"
+ #pragma implementation "zipstrm.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#pragma hdrstop
#endif
-#if wxUSE_STREAMS && wxUSE_ZIPSTREAM && wxUSE_ZLIB
+#ifndef WX_PRECOMP
+ #include "wx/defs.h"
+#endif
+
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
+#include "wx/zipstrm.h"
+#include "wx/log.h"
+#include "wx/intl.h"
+#include "wx/datstrm.h"
+#include "wx/zstream.h"
+#include "wx/mstream.h"
+#include "wx/utils.h"
+#include "wx/buffer.h"
+#include "wx/ptr_scpd.h"
+#include "wx/wfstream.h"
+#include "wx/html/forcelnk.h"
+#include "zlib.h"
+
+// value for the 'version needed to extract' field (20 means 2.0)
+enum {
+ VERSION_NEEDED_TO_EXTRACT = 20
+};
+
+// signatures for the various records (PKxx)
+enum {
+ CENTRAL_MAGIC = 0x02014b50, // central directory record
+ LOCAL_MAGIC = 0x04034b50, // local header
+ END_MAGIC = 0x06054b50, // end of central directory record
+ SUMS_MAGIC = 0x08074b50 // data descriptor (info-zip)
+};
+
+// unix file attributes. zip stores them in the high 16 bits of the
+// 'external attributes' field, hence the extra zeros.
+enum {
+ wxZIP_S_IFMT = 0xF0000000,
+ wxZIP_S_IFDIR = 0x40000000,
+ wxZIP_S_IFREG = 0x80000000
+};
+
+// minimum sizes for the various records
+enum {
+ CENTRAL_SIZE = 46,
+ LOCAL_SIZE = 30,
+ END_SIZE = 22,
+ SUMS_SIZE = 12
+};
+
+// The number of bytes that must be written to an wxZipOutputStream before
+// a zip entry is created. The purpose of this latency is so that
+// OpenCompressor() can see a little data before deciding which compressor
+// it should use.
+enum {
+ OUTPUT_LATENCY = 4096
+};
+
+// Some offsets into the local header
+enum {
+ SUMS_OFFSET = 14
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxZipEntry, wxArchiveEntry)
+IMPLEMENT_DYNAMIC_CLASS(wxZipClassFactory, wxArchiveClassFactory)
+
+FORCE_LINK_ME(zipstrm)
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Helpers
+
+// read a string of a given length
+//
+static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv)
+{
+#if wxUSE_UNICODE
+ wxCharBuffer buf(len);
+ stream.Read(buf.data(), len);
+ wxString str(buf, conv);
+#else
+ wxString str;
+ (void)conv;
+ {
+ wxStringBuffer buf(str, len);
+ stream.Read(buf, len);
+ }
+#endif
+
+ return str;
+}
+
+// Decode a little endian wxUint32 number from a character array
+//
+static inline wxUint32 CrackUint32(const char *m)
+{
+ const unsigned char *n = (const unsigned char*)m;
+ return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0];
+}
+
+// Temporarily lower the logging level in debug mode to avoid a warning
+// from SeekI about seeking on a stream with data written back to it.
+//
+static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
+{
+#ifdef __WXDEBUG__
+ wxLogLevel level = wxLog::GetLogLevel();
+ wxLog::SetLogLevel(wxLOG_Debug - 1);
+ wxFileOffset result = stream.SeekI(pos);
+ wxLog::SetLogLevel(level);
+ return result;
+#else
+ return stream.SeekI(pos);
+#endif
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Stored input stream
+// Trival decompressor for files which are 'stored' in the zip file.
+
+class wxStoredInputStream : public wxFilterInputStream
+{
+public:
+ wxStoredInputStream(wxInputStream& stream);
+
+ void Open(wxFileOffset len) { Close(); m_len = len; }
+ void Close() { m_pos = 0; m_lasterror = wxSTREAM_NO_ERROR; }
+
+ virtual char Peek() { return wxInputStream::Peek(); }
+ virtual size_t GetSize() const { return m_len; }
+
+protected:
+ virtual size_t OnSysRead(void *buffer, size_t size);
+ virtual wxFileOffset OnSysTell() const { return m_pos; }
+
+private:
+ wxFileOffset m_pos;
+ wxFileOffset m_len;
+
+ DECLARE_NO_COPY_CLASS(wxStoredInputStream)
+};
+
+wxStoredInputStream::wxStoredInputStream(wxInputStream& stream)
+ : wxFilterInputStream(stream),
+ m_pos(0),
+ m_len(0)
+{
+}
+
+size_t wxStoredInputStream::OnSysRead(void *buffer, size_t size)
+{
+ size_t count = wxMin(size, m_len - m_pos + (size_t)0);
+ count = m_parent_i_stream->Read(buffer, count).LastRead();
+ m_pos += count;
+
+ if (m_pos == m_len)
+ m_lasterror = wxSTREAM_EOF;
+ else if (!*m_parent_i_stream)
+ m_lasterror = wxSTREAM_READ_ERROR;
+
+ return count;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Stored output stream
+// Trival compressor for files which are 'stored' in the zip file.
+
+class wxStoredOutputStream : public wxFilterOutputStream
+{
+public:
+ wxStoredOutputStream(wxOutputStream& stream) :
+ wxFilterOutputStream(stream), m_pos(0) { }
+
+ bool Close() {
+ m_pos = 0;
+ m_lasterror = wxSTREAM_NO_ERROR;
+ return true;
+ }
+
+protected:
+ virtual size_t OnSysWrite(const void *buffer, size_t size);
+ virtual wxFileOffset OnSysTell() const { return m_pos; }
+
+private:
+ wxFileOffset m_pos;
+ DECLARE_NO_COPY_CLASS(wxStoredOutputStream)
+};
+
+size_t wxStoredOutputStream::OnSysWrite(const void *buffer, size_t size)
+{
+ if (!IsOk() || !size)
+ return 0;
+ size_t count = m_parent_o_stream->Write(buffer, size).LastWrite();
+ if (count != size)
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ m_pos += count;
+ return count;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxRawInputStream
+//
+// Used to handle the unusal case of raw copying an entry of unknown
+// length. This can only happen when the zip being copied from is being
+// read from a non-seekable stream, and also was original written to a
+// non-seekable stream.
+//
+// In this case there's no option but to decompress the stream to find
+// it's length, but we can still write the raw compressed data to avoid the
+// compression overhead (which is the greater one).
+//
+// Usage is like this:
+// m_rawin = new wxRawInputStream(*m_parent_i_stream);
+// m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
+//
+// The wxRawInputStream owns a wxTeeInputStream object, the role of which
+// is something like the unix 'tee' command; it is a transparent filter, but
+// allows the data read to be read a second time via an extra method 'GetData'.
+//
+// The wxRawInputStream then draws data through the tee using a decompressor
+// then instead of returning the decompressed data, retuns the raw data
+// from wxTeeInputStream::GetData().
+
+class wxTeeInputStream : public wxFilterInputStream
+{
+public:
+ wxTeeInputStream(wxInputStream& stream);
+
+ size_t GetCount() const { return m_end - m_start; }
+ size_t GetData(char *buffer, size_t size);
+
+ void Open();
+ bool Final();
+
+ wxInputStream& Read(void *buffer, size_t size);
+
+protected:
+ virtual size_t OnSysRead(void *buffer, size_t size);
+ virtual wxFileOffset OnSysTell() const { return m_pos; }
+
+private:
+ wxFileOffset m_pos;
+ wxMemoryBuffer m_buf;
+ size_t m_start;
+ size_t m_end;
+
+ DECLARE_NO_COPY_CLASS(wxTeeInputStream)
+};
+
+wxTeeInputStream::wxTeeInputStream(wxInputStream& stream)
+ : wxFilterInputStream(stream),
+ m_pos(0), m_buf(8192), m_start(0), m_end(0)
+{
+}
+
+void wxTeeInputStream::Open()
+{
+ m_pos = m_start = m_end = 0;
+ m_lasterror = wxSTREAM_NO_ERROR;
+}
+
+bool wxTeeInputStream::Final()
+{
+ bool final = m_end == m_buf.GetDataLen();
+ m_end = m_buf.GetDataLen();
+ return final;
+}
+
+wxInputStream& wxTeeInputStream::Read(void *buffer, size_t size)
+{
+ size_t count = wxInputStream::Read(buffer, size).LastRead();
+ m_end = m_buf.GetDataLen();
+ m_buf.AppendData(buffer, count);
+ return *this;
+}
+
+size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size)
+{
+ size_t count = m_parent_i_stream->Read(buffer, size).LastRead();
+ m_lasterror = m_parent_i_stream->GetLastError();
+ return count;
+}
+
+size_t wxTeeInputStream::GetData(char *buffer, size_t size)
+{
+ if (m_wbacksize) {
+ size_t len = m_buf.GetDataLen();
+ len = len > m_wbacksize ? len - m_wbacksize : 0;
+ m_buf.SetDataLen(len);
+ if (m_end > len) {
+ wxFAIL; // we've already returned data that's now being ungot
+ m_end = len;
+ }
+ m_parent_i_stream->Ungetch(m_wback, m_wbacksize);
+ free(m_wback);
+ m_wback = NULL;
+ m_wbacksize = 0;
+ m_wbackcur = 0;
+ }
+
+ if (size > GetCount())
+ size = GetCount();
+ if (size) {
+ memcpy(buffer, m_buf + m_start, size);
+ m_start += size;
+ wxASSERT(m_start <= m_end);
+ }
+
+ if (m_start == m_end && m_start > 0 && m_buf.GetDataLen() > 0) {
+ size_t len = m_buf.GetDataLen();
+ char *buf = (char*)m_buf.GetWriteBuf(len);
+ len -= m_end;
+ memmove(buf, buf + m_end, len);
+ m_buf.UngetWriteBuf(len);
+ m_start = m_end = 0;
+ }
+
+ return size;
+}
+
+class wxRawInputStream : public wxFilterInputStream
+{
+public:
+ wxRawInputStream(wxInputStream& stream);
+ virtual ~wxRawInputStream() { delete m_tee; }
+
+ wxInputStream* Open(wxInputStream *decomp);
+ wxInputStream& GetTee() const { return *m_tee; }
+
+protected:
+ virtual size_t OnSysRead(void *buffer, size_t size);
+ virtual wxFileOffset OnSysTell() const { return m_pos; }
+
+private:
+ wxFileOffset m_pos;
+ wxTeeInputStream *m_tee;
+
+ enum { BUFSIZE = 8192 };
+ wxCharBuffer m_dummy;
+
+ DECLARE_NO_COPY_CLASS(wxRawInputStream)
+};
+
+wxRawInputStream::wxRawInputStream(wxInputStream& stream)
+ : wxFilterInputStream(stream),
+ m_pos(0),
+ m_tee(new wxTeeInputStream(stream)),
+ m_dummy(BUFSIZE)
+{
+}
+
+wxInputStream *wxRawInputStream::Open(wxInputStream *decomp)
+{
+ if (decomp) {
+ m_parent_i_stream = decomp;
+ m_pos = 0;
+ m_lasterror = wxSTREAM_NO_ERROR;
+ m_tee->Open();
+ return this;
+ } else {
+ return NULL;
+ }
+}
+
+size_t wxRawInputStream::OnSysRead(void *buffer, size_t size)
+{
+ char *buf = (char*)buffer;
+ size_t count = 0;
+
+ while (count < size && IsOk())
+ {
+ while (m_parent_i_stream->IsOk() && m_tee->GetCount() == 0)
+ m_parent_i_stream->Read(m_dummy.data(), BUFSIZE);
+
+ size_t n = m_tee->GetData(buf + count, size - count);
+ count += n;
+
+ if (n == 0 && m_tee->Final())
+ m_lasterror = m_parent_i_stream->GetLastError();
+ }
+
+ m_pos += count;
+ return count;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Zlib streams than can be reused without recreating.
+
+class wxZlibOutputStream2 : public wxZlibOutputStream
+{
+public:
+ wxZlibOutputStream2(wxOutputStream& stream, int level) :
+ wxZlibOutputStream(stream, level, wxZLIB_NO_HEADER) { }
+
+ bool Open(wxOutputStream& stream);
+ bool Close() { DoFlush(true); m_pos = wxInvalidOffset; return IsOk(); }
+};
+
+bool wxZlibOutputStream2::Open(wxOutputStream& stream)
+{
+ wxCHECK(m_pos == wxInvalidOffset, false);
+
+ m_deflate->next_out = m_z_buffer;
+ m_deflate->avail_out = m_z_size;
+ m_pos = 0;
+ m_lasterror = wxSTREAM_NO_ERROR;
+ m_parent_o_stream = &stream;
+
+ if (deflateReset(m_deflate) != Z_OK) {
+ wxLogError(_("can't re-initialize zlib deflate stream"));
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ return false;
+ }
+
+ return true;
+}
+
+class wxZlibInputStream2 : public wxZlibInputStream
+{
+public:
+ wxZlibInputStream2(wxInputStream& stream) :
+ wxZlibInputStream(stream, wxZLIB_NO_HEADER) { }
+
+ bool Open(wxInputStream& stream);
+};
+
+bool wxZlibInputStream2::Open(wxInputStream& stream)
+{
+ m_inflate->avail_in = 0;
+ m_pos = 0;
+ m_lasterror = wxSTREAM_NO_ERROR;
+ m_parent_i_stream = &stream;
+
+ if (inflateReset(m_inflate) != Z_OK) {
+ wxLogError(_("can't re-initialize zlib inflate stream"));
+ m_lasterror = wxSTREAM_READ_ERROR;
+ return false;
+ }
+
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Class to hold wxZipEntry's Extra and LocalExtra fields
+
+class wxZipMemory
+{
+public:
+ wxZipMemory() : m_data(NULL), m_size(0), m_capacity(0), m_ref(1) { }
+
+ wxZipMemory *AddRef() { m_ref++; return this; }
+ void Release() { if (--m_ref == 0) delete this; }
+
+ char *GetData() const { return m_data; }
+ size_t GetSize() const { return m_size; }
+ size_t GetCapacity() const { return m_capacity; }
+
+ wxZipMemory *Unique(size_t size);
+
+private:
+ ~wxZipMemory() { delete m_data; }
+
+ char *m_data;
+ size_t m_size;
+ size_t m_capacity;
+ int m_ref;
+};
+
+wxZipMemory *wxZipMemory::Unique(size_t size)
+{
+ wxZipMemory *zm;
+
+ if (m_ref > 1) {
+ --m_ref;
+ zm = new wxZipMemory;
+ } else {
+ zm = this;
+ }
+
+ if (zm->m_capacity < size) {
+ delete zm->m_data;
+ zm->m_data = new char[size];
+ zm->m_capacity = size;
+ }
+
+ zm->m_size = size;
+ return zm;
+}
+
+static inline wxZipMemory *AddRef(wxZipMemory *zm)
+{
+ if (zm)
+ zm->AddRef();
+ return zm;
+}
+
+static inline void Release(wxZipMemory *zm)
+{
+ if (zm)
+ zm->Release();
+}
+
+static void Copy(wxZipMemory*& dest, wxZipMemory *src)
+{
+ Release(dest);
+ dest = AddRef(src);
+}
+
+static void Unique(wxZipMemory*& zm, size_t size)
+{
+ if (!zm && size)
+ zm = new wxZipMemory;
+ if (zm)
+ zm = zm->Unique(size);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Collection of weak references to entries
+
+WX_DECLARE_HASH_MAP(long, wxZipEntry*, wxIntegerHash,
+ wxIntegerEqual, _wxOffsetZipEntryMap);
+
+class wxZipWeakLinks
+{
+public:
+ wxZipWeakLinks() : m_ref(1) { }
+
+ void Release(const wxZipInputStream* WXUNUSED(x))
+ { if (--m_ref == 0) delete this; }
+ void Release(wxFileOffset key)
+ { RemoveEntry(key); if (--m_ref == 0) delete this; }
+
+ wxZipWeakLinks *AddEntry(wxZipEntry *entry, wxFileOffset key);
+ void RemoveEntry(wxFileOffset key) { m_entries.erase(key); }
+ wxZipEntry *GetEntry(wxFileOffset key) const;
+ bool IsEmpty() const { return m_entries.empty(); }
+
+private:
+ ~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
+
+ int m_ref;
+ _wxOffsetZipEntryMap m_entries;
+};
+
+wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
+{
+ m_entries[key] = entry;
+ m_ref++;
+ return this;
+}
+
+wxZipEntry *wxZipWeakLinks::GetEntry(wxFileOffset key) const
+{
+ _wxOffsetZipEntryMap::const_iterator it = m_entries.find(key);
+ return it != m_entries.end() ? it->second : NULL;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ZipEntry
+
+wxZipEntry::wxZipEntry(
+ const wxString& name /*=wxEmptyString*/,
+ const wxDateTime& dt /*=wxDateTime::Now()*/,
+ wxFileOffset size /*=wxInvalidOffset*/)
+ :
+ m_SystemMadeBy(wxZIP_SYSTEM_MSDOS),
+ m_VersionMadeBy(wxMAJOR_VERSION * 10 + wxMINOR_VERSION),
+ m_VersionNeeded(VERSION_NEEDED_TO_EXTRACT),
+ m_Flags(0),
+ m_Method(wxZIP_METHOD_DEFAULT),
+ m_DateTime(dt),
+ m_Crc(0),
+ m_CompressedSize(wxInvalidOffset),
+ m_Size(size),
+ m_Key(wxInvalidOffset),
+ m_Offset(wxInvalidOffset),
+ m_DiskStart(0),
+ m_InternalAttributes(0),
+ m_ExternalAttributes(0),
+ m_Extra(NULL),
+ m_LocalExtra(NULL),
+ m_zipnotifier(NULL),
+ m_backlink(NULL)
+{
+ if (!name.empty())
+ SetName(name);
+}
+
+wxZipEntry::~wxZipEntry()
+{
+ if (m_backlink)
+ m_backlink->Release(m_Key);
+ Release(m_Extra);
+ Release(m_LocalExtra);
+}
+
+wxZipEntry::wxZipEntry(const wxZipEntry& e)
+ : m_SystemMadeBy(e.m_SystemMadeBy),
+ m_VersionMadeBy(e.m_VersionMadeBy),
+ m_VersionNeeded(e.m_VersionNeeded),
+ m_Flags(e.m_Flags),
+ m_Method(e.m_Method),
+ m_DateTime(e.m_DateTime),
+ m_Crc(e.m_Crc),
+ m_CompressedSize(e.m_CompressedSize),
+ m_Size(e.m_Size),
+ m_Name(e.m_Name),
+ m_Key(e.m_Key),
+ m_Offset(e.m_Offset),
+ m_Comment(e.m_Comment),
+ m_DiskStart(e.m_DiskStart),
+ m_InternalAttributes(e.m_InternalAttributes),
+ m_ExternalAttributes(e.m_ExternalAttributes),
+ m_Extra(AddRef(e.m_Extra)),
+ m_LocalExtra(AddRef(e.m_LocalExtra)),
+ m_zipnotifier(e.m_zipnotifier),
+ m_backlink(NULL)
+{
+}
+
+wxZipEntry& wxZipEntry::operator=(const wxZipEntry& e)
+{
+ if (&e != this) {
+ m_SystemMadeBy = e.m_SystemMadeBy;
+ m_VersionMadeBy = e.m_VersionMadeBy;
+ m_VersionNeeded = e.m_VersionNeeded;
+ m_Flags = e.m_Flags;
+ m_Method = e.m_Method;
+ m_DateTime = e.m_DateTime;
+ m_Crc = e.m_Crc;
+ m_CompressedSize = e.m_CompressedSize;
+ m_Size = e.m_Size;
+ m_Name = e.m_Name;
+ m_Key = e.m_Key;
+ m_Offset = e.m_Offset;
+ m_Comment = e.m_Comment;
+ m_DiskStart = e.m_DiskStart;
+ m_InternalAttributes = e.m_InternalAttributes;
+ m_ExternalAttributes = e.m_ExternalAttributes;
+ Copy(m_Extra, e.m_Extra);
+ Copy(m_LocalExtra, e.m_LocalExtra);
+ m_zipnotifier = e.m_zipnotifier;
+ if (m_backlink) {
+ m_backlink->Release(m_Key);
+ m_backlink = NULL;
+ }
+ }
+ return *this;
+}
+
+wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
+{
+ bool isDir = IsDir() && !m_Name.empty();
+
+ switch (wxFileName::GetFormat(format)) {
+ case wxPATH_DOS:
+ {
+ wxString name(isDir ? m_Name + _T("\\") : m_Name);
+ for (size_t i = name.length() - 1; i > 0; --i)
+ if (name[i] == _T('/'))
+ name[i] = _T('\\');
+ return name;
+ }
+
+ case wxPATH_UNIX:
+ return isDir ? m_Name + _T("/") : m_Name;
+
+ default:
+ ;
+ }
+
+ wxFileName fn;
+
+ if (isDir)
+ fn.AssignDir(m_Name, wxPATH_UNIX);
+ else
+ fn.Assign(m_Name, wxPATH_UNIX);
+
+ return fn.GetFullPath(format);
+}
+
+// Static - Internally tars and zips use forward slashes for the path
+// separator, absolute paths aren't allowed, and directory names have a
+// trailing slash. This function converts a path into this internal format,
+// but without a trailing slash for a directory.
+//
+wxString wxZipEntry::GetInternalName(const wxString& name,
+ wxPathFormat format /*=wxPATH_NATIVE*/,
+ bool *pIsDir /*=NULL*/)
+{
+ wxString internal;
+
+ if (wxFileName::GetFormat(format) != wxPATH_UNIX)
+ internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX);
+ else
+ internal = name;
+
+ bool isDir = !internal.empty() && internal.Last() == '/';
+ if (pIsDir)
+ *pIsDir = isDir;
+ if (isDir)
+ internal.erase(internal.length() - 1);
+
+ while (!internal.empty() && *internal.begin() == '/')
+ internal.erase(0, 1);
+ while (!internal.empty() && internal.compare(0, 2, _T("./")) == 0)
+ internal.erase(0, 2);
+ if (internal == _T(".") || internal == _T(".."))
+ internal = wxEmptyString;
+
+ return internal;
+}
+
+void wxZipEntry::SetSystemMadeBy(int system)
+{
+ int mode = GetMode();
+ bool wasUnix = IsMadeByUnix();
+
+ m_SystemMadeBy = system;
+
+ if (!wasUnix && IsMadeByUnix()) {
+ SetIsDir(IsDir());
+ SetMode(mode);
+ } else if (wasUnix && !IsMadeByUnix()) {
+ m_ExternalAttributes &= 0xffff;
+ }
+}
+
+void wxZipEntry::SetIsDir(bool isDir /*=true*/)
+{
+ if (isDir)
+ m_ExternalAttributes |= wxZIP_A_SUBDIR;
+ else
+ m_ExternalAttributes &= ~wxZIP_A_SUBDIR;
+
+ if (IsMadeByUnix()) {
+ m_ExternalAttributes &= ~wxZIP_S_IFMT;
+ if (isDir)
+ m_ExternalAttributes |= wxZIP_S_IFDIR;
+ else
+ m_ExternalAttributes |= wxZIP_S_IFREG;
+ }
+}
+
+// Return unix style permission bits
+//
+int wxZipEntry::GetMode() const
+{
+ // return unix permissions if present
+ if (IsMadeByUnix())
+ return (m_ExternalAttributes >> 16) & 0777;
+
+ // otherwise synthesize from the dos attribs
+ int mode = 0644;
+ if (m_ExternalAttributes & wxZIP_A_RDONLY)
+ mode &= ~0200;
+ if (m_ExternalAttributes & wxZIP_A_SUBDIR)
+ mode |= 0111;
+
+ return mode;
+}
+
+// Set unix permissions
+//
+void wxZipEntry::SetMode(int mode)
+{
+ // Set dos attrib bits to be compatible
+ if (mode & 0222)
+ m_ExternalAttributes &= ~wxZIP_A_RDONLY;
+ else
+ m_ExternalAttributes |= wxZIP_A_RDONLY;
+
+ // set the actual unix permission bits if the system type allows
+ if (IsMadeByUnix()) {
+ m_ExternalAttributes &= ~(0777L << 16);
+ m_ExternalAttributes |= (mode & 0777L) << 16;
+ }
+}
+
+const char *wxZipEntry::GetExtra() const
+{
+ return m_Extra ? m_Extra->GetData() : NULL;
+}
+
+size_t wxZipEntry::GetExtraLen() const
+{
+ return m_Extra ? m_Extra->GetSize() : 0;
+}
+
+void wxZipEntry::SetExtra(const char *extra, size_t len)
+{
+ Unique(m_Extra, len);
+ if (len)
+ memcpy(m_Extra->GetData(), extra, len);
+}
+
+const char *wxZipEntry::GetLocalExtra() const
+{
+ return m_LocalExtra ? m_LocalExtra->GetData() : NULL;
+}
+
+size_t wxZipEntry::GetLocalExtraLen() const
+{
+ return m_LocalExtra ? m_LocalExtra->GetSize() : 0;
+}
+
+void wxZipEntry::SetLocalExtra(const char *extra, size_t len)
+{
+ Unique(m_LocalExtra, len);
+ if (len)
+ memcpy(m_LocalExtra->GetData(), extra, len);
+}
+
+void wxZipEntry::SetNotifier(wxZipNotifier& notifier)
+{
+ wxArchiveEntry::UnsetNotifier();
+ m_zipnotifier = ¬ifier;
+ m_zipnotifier->OnEntryUpdated(*this);
+}
+
+void wxZipEntry::Notify()
+{
+ if (m_zipnotifier)
+ m_zipnotifier->OnEntryUpdated(*this);
+ else if (GetNotifier())
+ GetNotifier()->OnEntryUpdated(*this);
+}
+
+void wxZipEntry::UnsetNotifier()
+{
+ wxArchiveEntry::UnsetNotifier();
+ m_zipnotifier = NULL;
+}
+
+size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
+{
+ wxUint16 nameLen, extraLen;
+ wxUint32 compressedSize, size, crc;
+
+ wxDataInputStream ds(stream);
+
+ ds >> m_VersionNeeded >> m_Flags >> m_Method;
+ SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
+ ds >> crc >> compressedSize >> size >> nameLen >> extraLen;
+
+ bool sumsValid = (m_Flags & wxZIP_SUMS_FOLLOW) == 0;
+
+ if (sumsValid || crc)
+ m_Crc = crc;
+ if ((sumsValid || compressedSize) || m_Method == wxZIP_METHOD_STORE)
+ m_CompressedSize = compressedSize;
+ if ((sumsValid || size) || m_Method == wxZIP_METHOD_STORE)
+ m_Size = size;
+
+ SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+
+ if (extraLen || GetLocalExtraLen()) {
+ Unique(m_LocalExtra, extraLen);
+ if (extraLen)
+ stream.Read(m_LocalExtra->GetData(), extraLen);
+ }
+
+ return LOCAL_SIZE + nameLen + extraLen;
+}
+
+size_t wxZipEntry::WriteLocal(wxOutputStream& stream, wxMBConv& conv) const
+{
+ wxString unixName = GetName(wxPATH_UNIX);
+ const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
+ const char *name = name_buf;
+ if (!name) name = "";
+ wxUint16 nameLen = strlen(name);
+
+ wxDataOutputStream ds(stream);
+
+ ds << m_VersionNeeded << m_Flags << m_Method;
+ ds.Write32(GetDateTime().GetAsDOS());
+
+ ds.Write32(m_Crc);
+ ds.Write32(m_CompressedSize != wxInvalidOffset ? m_CompressedSize : 0);
+ ds.Write32(m_Size != wxInvalidOffset ? m_Size : 0);
+
+ ds << nameLen;
+ wxUint16 extraLen = GetLocalExtraLen();
+ ds.Write16(extraLen);
+
+ stream.Write(name, nameLen);
+ if (extraLen)
+ stream.Write(m_LocalExtra->GetData(), extraLen);
+
+ return LOCAL_SIZE + nameLen + extraLen;
+}
+
+size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
+{
+ wxUint16 nameLen, extraLen, commentLen;
+
+ wxDataInputStream ds(stream);
+
+ ds >> m_VersionMadeBy >> m_SystemMadeBy;
+
+ SetVersionNeeded(ds.Read16());
+ SetFlags(ds.Read16());
+ SetMethod(ds.Read16());
+ SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
+ SetCrc(ds.Read32());
+ SetCompressedSize(ds.Read32());
+ SetSize(ds.Read32());
+
+ ds >> nameLen >> extraLen >> commentLen
+ >> m_DiskStart >> m_InternalAttributes >> m_ExternalAttributes;
+ SetOffset(ds.Read32());
+
+ SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+
+ if (extraLen || GetExtraLen()) {
+ Unique(m_Extra, extraLen);
+ if (extraLen)
+ stream.Read(m_Extra->GetData(), extraLen);
+ }
+
+ if (commentLen)
+ m_Comment = ReadString(stream, commentLen, conv);
+ else
+ m_Comment.clear();
+
+ return CENTRAL_SIZE + nameLen + extraLen + commentLen;
+}
+
+size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const
+{
+ wxString unixName = GetName(wxPATH_UNIX);
+ const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
+ const char *name = name_buf;
+ if (!name) name = "";
+ wxUint16 nameLen = strlen(name);
+
+ const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
+ const char *comment = comment_buf;
+ if (!comment) comment = "";
+ wxUint16 commentLen = strlen(comment);
+
+ wxUint16 extraLen = GetExtraLen();
+
+ wxDataOutputStream ds(stream);
+
+ ds << CENTRAL_MAGIC << m_VersionMadeBy << m_SystemMadeBy;
+
+ ds.Write16(GetVersionNeeded());
+ ds.Write16(GetFlags());
+ ds.Write16(GetMethod());
+ ds.Write32(GetDateTime().GetAsDOS());
+ ds.Write32(GetCrc());
+ ds.Write32(GetCompressedSize());
+ ds.Write32(GetSize());
+ ds.Write16(nameLen);
+ ds.Write16(extraLen);
+
+ ds << commentLen << m_DiskStart << m_InternalAttributes
+ << m_ExternalAttributes << (wxUint32)GetOffset();
+
+ stream.Write(name, nameLen);
+ if (extraLen)
+ stream.Write(GetExtra(), extraLen);
+ stream.Write(comment, commentLen);
+
+ return CENTRAL_SIZE + nameLen + extraLen + commentLen;
+}
+
+// Info-zip prefixes this record with a signature, but pkzip doesn't. So if
+// the 1st value is the signature then it is probably an info-zip record,
+// though there is a small chance that it is in fact a pkzip record which
+// happens to have the signature as it's CRC.
+//
+size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
+{
+ wxDataInputStream ds(stream);
+
+ m_Crc = ds.Read32();
+ m_CompressedSize = ds.Read32();
+ m_Size = ds.Read32();
+
+ // if 1st value is the signature then this is probably an info-zip record
+ if (m_Crc == SUMS_MAGIC)
+ {
+ char buf[8];
+ stream.Read(buf, sizeof(buf));
+ wxUint32 u1 = CrackUint32(buf);
+ wxUint32 u2 = CrackUint32(buf + 4);
+
+ // look for the signature of the following record to decide which
+ if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) &&
+ (u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC))
+ {
+ // it's a pkzip style record after all!
+ stream.Ungetch(buf, sizeof(buf));
+ }
+ else
+ {
+ // it's an info-zip record as expected
+ stream.Ungetch(buf + 4, sizeof(buf) - 4);
+ m_Crc = m_CompressedSize;
+ m_CompressedSize = m_Size;
+ m_Size = u1;
+ return SUMS_SIZE + 4;
+ }
+ }
+
+ return SUMS_SIZE;
+}
+
+size_t wxZipEntry::WriteDescriptor(wxOutputStream& stream, wxUint32 crc,
+ wxFileOffset compressedSize, wxFileOffset size)
+{
+ m_Crc = crc;
+ m_CompressedSize = compressedSize;
+ m_Size = size;
+
+ wxDataOutputStream ds(stream);
+
+ ds.Write32(crc);
+ ds.Write32(compressedSize);
+ ds.Write32(size);
+
+ return SUMS_SIZE;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxZipEndRec - holds the end of central directory record
+
+class wxZipEndRec
+{
+public:
+ wxZipEndRec();
+
+ int GetDiskNumber() const { return m_DiskNumber; }
+ int GetStartDisk() const { return m_StartDisk; }
+ int GetEntriesHere() const { return m_EntriesHere; }
+ int GetTotalEntries() const { return m_TotalEntries; }
+ wxFileOffset GetSize() const { return m_Size; }
+ wxFileOffset GetOffset() const { return m_Offset; }
+ wxString GetComment() const { return m_Comment; }
+
+ void SetDiskNumber(int num) { m_DiskNumber = num; }
+ void SetStartDisk(int num) { m_StartDisk = num; }
+ void SetEntriesHere(int num) { m_EntriesHere = num; }
+ void SetTotalEntries(int num) { m_TotalEntries = num; }
+ void SetSize(wxFileOffset size) { m_Size = (wxUint32)size; }
+ void SetOffset(wxFileOffset offset) { m_Offset = (wxUint32)offset; }
+ void SetComment(const wxString& comment) { m_Comment = comment; }
+
+ bool Read(wxInputStream& stream, wxMBConv& conv);
+ bool Write(wxOutputStream& stream, wxMBConv& conv) const;
+
+private:
+ wxUint16 m_DiskNumber;
+ wxUint16 m_StartDisk;
+ wxUint16 m_EntriesHere;
+ wxUint16 m_TotalEntries;
+ wxUint32 m_Size;
+ wxUint32 m_Offset;
+ wxString m_Comment;
+};
+
+wxZipEndRec::wxZipEndRec()
+ : m_DiskNumber(0),
+ m_StartDisk(0),
+ m_EntriesHere(0),
+ m_TotalEntries(0),
+ m_Size(0),
+ m_Offset(0)
+{
+}
+
+bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const
+{
+ const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
+ const char *comment = comment_buf;
+ if (!comment) comment = "";
+ wxUint16 commentLen = strlen(comment);
+
+ wxDataOutputStream ds(stream);
+
+ ds << END_MAGIC << m_DiskNumber << m_StartDisk << m_EntriesHere
+ << m_TotalEntries << m_Size << m_Offset << commentLen;
+
+ stream.Write(comment, commentLen);
+
+ return stream.IsOk();
+}
+
+bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
+{
+ wxDataInputStream ds(stream);
+ wxUint16 commentLen;
+
+ ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere
+ >> m_TotalEntries >> m_Size >> m_Offset >> commentLen;
+
+ if (commentLen)
+ m_Comment = ReadString(stream, commentLen, conv);
+
+ if (stream.IsOk())
+ if (m_DiskNumber == 0 && m_StartDisk == 0 &&
+ m_EntriesHere == m_TotalEntries)
+ return true;
+ else
+ wxLogError(_("unsupported zip archive"));
+
+ return false;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// A weak link from an input stream to an output stream
+
+class wxZipStreamLink
+{
+public:
+ wxZipStreamLink(wxZipOutputStream *stream) : m_ref(1), m_stream(stream) { }
+
+ wxZipStreamLink *AddRef() { m_ref++; return this; }
+ wxZipOutputStream *GetOutputStream() const { return m_stream; }
+
+ void Release(class wxZipInputStream *WXUNUSED(s))
+ { if (--m_ref == 0) delete this; }
+ void Release(class wxZipOutputStream *WXUNUSED(s))
+ { m_stream = NULL; if (--m_ref == 0) delete this; }
+
+private:
+ ~wxZipStreamLink() { }
+
+ int m_ref;
+ wxZipOutputStream *m_stream;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Input stream
+
+wxDECLARE_SCOPED_PTR(wxZipEntry, _wxZipEntryPtr)
+wxDEFINE_SCOPED_PTR (wxZipEntry, _wxZipEntryPtr)
+
+// constructor
+//
+wxZipInputStream::wxZipInputStream(wxInputStream& stream,
+ wxMBConv& conv /*=wxConvLocal*/)
+ : wxArchiveInputStream(stream, conv)
+{
+ m_ffile = NULL;
+ Init();
+}
+
+// Compatibility constructor
+//
+wxZipInputStream::wxZipInputStream(const wxString& archive,
+ const wxString& file)
+ : wxArchiveInputStream(OpenFile(archive), wxConvLocal)
+{
+ // no error messages
+ wxLogNull nolog;
+ Init();
+ _wxZipEntryPtr entry;
+
+ if (m_ffile->Ok()) {
+ do {
+ entry.reset(GetNextEntry());
+ }
+ while (entry.get() != NULL && entry->GetInternalName() != file);
+ }
+
+ if (entry.get() == NULL)
+ m_lasterror = wxSTREAM_READ_ERROR;
+}
+
+wxInputStream& wxZipInputStream::OpenFile(const wxString& archive)
+{
+ wxLogNull nolog;
+ m_ffile = new wxFFileInputStream(archive);
+ return *m_ffile;
+}
+
+void wxZipInputStream::Init()
+{
+ m_store = new wxStoredInputStream(*m_parent_i_stream);
+ m_inflate = NULL;
+ m_rawin = NULL;
+ m_raw = false;
+ m_headerSize = 0;
+ m_decomp = NULL;
+ m_parentSeekable = false;
+ m_weaklinks = new wxZipWeakLinks;
+ m_streamlink = NULL;
+ m_offsetAdjustment = 0;
+ m_position = wxInvalidOffset;
+ m_signature = 0;
+ m_TotalEntries = 0;
+ m_lasterror = m_parent_i_stream->GetLastError();
+}
+
+wxZipInputStream::~wxZipInputStream()
+{
+ CloseDecompressor(m_decomp);
+
+ delete m_store;
+ delete m_inflate;
+ delete m_rawin;
+ delete m_ffile;
+
+ m_weaklinks->Release(this);
+
+ if (m_streamlink)
+ m_streamlink->Release(this);
+}
+
+wxString wxZipInputStream::GetComment()
+{
+ if (m_position == wxInvalidOffset)
+ if (!LoadEndRecord())
+ return wxEmptyString;
+
+ if (!m_parentSeekable && Eof() && m_signature) {
+ m_lasterror = wxSTREAM_NO_ERROR;
+ m_lasterror = ReadLocal(true);
+ }
+
+ return m_Comment;
+}
+
+int wxZipInputStream::GetTotalEntries()
+{
+ if (m_position == wxInvalidOffset)
+ LoadEndRecord();
+ return m_TotalEntries;
+}
+
+wxZipStreamLink *wxZipInputStream::MakeLink(wxZipOutputStream *out)
+{
+ wxZipStreamLink *link = NULL;
+
+ if (!m_parentSeekable && (IsOpened() || !Eof())) {
+ link = new wxZipStreamLink(out);
+ if (m_streamlink)
+ m_streamlink->Release(this);
+ m_streamlink = link->AddRef();
+ }
+
+ return link;
+}
-#include "wx/log.h"
-#include "wx/intl.h"
-#include "wx/stream.h"
-#include "wx/wfstream.h"
-#include "wx/zipstrm.h"
-#include "wx/utils.h"
+bool wxZipInputStream::LoadEndRecord()
+{
+ wxCHECK(m_position == wxInvalidOffset, false);
+ if (!IsOk())
+ return false;
-/* Not the right solution (paths in makefiles) but... */
-#ifdef __BORLANDC__
-#include "../common/unzip.h"
-#else
-#include "unzip.h"
-#endif
+ m_position = 0;
+
+ // First find the end-of-central-directory record.
+ if (!FindEndRecord()) {
+ // failed, so either this is a non-seekable stream (ok), or not a zip
+ if (m_parentSeekable) {
+ m_lasterror = wxSTREAM_READ_ERROR;
+ wxLogError(_("invalid zip file"));
+ return false;
+ }
+ else {
+ wxLogNull nolog;
+ wxFileOffset pos = m_parent_i_stream->TellI();
+ // FIXME
+ //if (pos != wxInvalidOffset)
+ if (pos >= 0 && pos <= LONG_MAX)
+ m_offsetAdjustment = m_position = pos;
+ return true;
+ }
+ }
+
+ wxZipEndRec endrec;
+
+ // Read in the end record
+ wxFileOffset endPos = m_parent_i_stream->TellI() - 4;
+ if (!endrec.Read(*m_parent_i_stream, GetConv())) {
+ if (!*m_parent_i_stream) {
+ m_lasterror = wxSTREAM_READ_ERROR;
+ return false;
+ }
+ // TODO: try this out
+ wxLogWarning(_("assuming this is a multi-part zip concatenated"));
+ }
+
+ m_TotalEntries = endrec.GetTotalEntries();
+ m_Comment = endrec.GetComment();
+ // Now find the central-directory. we have the file offset of
+ // the CD, so look there first.
+ if (m_parent_i_stream->SeekI(endrec.GetOffset()) != wxInvalidOffset &&
+ ReadSignature() == CENTRAL_MAGIC) {
+ m_signature = CENTRAL_MAGIC;
+ m_position = endrec.GetOffset();
+ m_offsetAdjustment = 0;
+ return true;
+ }
+
+ // If it's not there, then it could be that the zip has been appended
+ // to a self extractor, so take the CD size (also in endrec), subtract
+ // it from the file offset of the end-central-directory and look there.
+ if (m_parent_i_stream->SeekI(endPos - endrec.GetSize())
+ != wxInvalidOffset && ReadSignature() == CENTRAL_MAGIC) {
+ m_signature = CENTRAL_MAGIC;
+ m_position = endPos - endrec.GetSize();
+ m_offsetAdjustment = m_position - endrec.GetOffset();
+ return true;
+ }
+
+ wxLogError(_("can't find central directory in zip"));
+ m_lasterror = wxSTREAM_READ_ERROR;
+ return false;
+}
-wxZipInputStream::wxZipInputStream(const wxString& archive, const wxString& file) : wxInputStream()
+// Find the end-of-central-directory record.
+// If found the stream will be positioned just past the 4 signature bytes.
+//
+bool wxZipInputStream::FindEndRecord()
{
- unz_file_info zinfo;
+ // usually it's 22 bytes in size and the last thing in the file
+ {
+ wxLogNull nolog;
+ if (m_parent_i_stream->SeekI(-END_SIZE, wxFromEnd) == wxInvalidOffset)
+ return false;
+ }
- m_Pos = 0;
- m_Size = 0;
- m_Archive = (void*) unzOpen(archive.mb_str(wxConvFile));
- if (m_Archive == NULL)
- {
- m_lasterror = wxSTREAM_READ_ERROR;
- return;
+ m_parentSeekable = true;
+ m_signature = 0;
+ char magic[4];
+ if (m_parent_i_stream->Read(magic, 4).LastRead() != 4)
+ return false;
+ if ((m_signature = CrackUint32(magic)) == END_MAGIC)
+ return true;
+
+ // unfortunately, the record has a comment field that can be up to 65535
+ // bytes in length, so if the signature not found then search backwards.
+ wxFileOffset pos = m_parent_i_stream->TellI();
+ const int BUFSIZE = 1024;
+ wxCharBuffer buf(BUFSIZE);
+
+ memcpy(buf.data(), magic, 3);
+ wxFileOffset minpos = wxMax(pos - 65535L, 0);
+
+ while (pos > minpos) {
+ size_t len = pos - wxMax(pos - (BUFSIZE - 3), minpos);
+ memcpy(buf.data() + len, buf, 3);
+ pos -= len;
+
+ if (m_parent_i_stream->SeekI(pos, wxFromStart) == wxInvalidOffset ||
+ m_parent_i_stream->Read(buf.data(), len).LastRead() != len)
+ return false;
+
+ char *p = buf.data() + len;
+
+ while (p-- > buf.data()) {
+ if ((m_signature = CrackUint32(p)) == END_MAGIC) {
+ size_t remainder = buf.data() + len - p;
+ if (remainder > 4)
+ m_parent_i_stream->Ungetch(p + 4, remainder - 4);
+ return true;
+ }
+ }
}
- // TODO what encoding does ZIP use?
- if (unzLocateFile((unzFile)m_Archive, file.ToAscii(), 0) != UNZ_OK)
- {
- m_lasterror = wxSTREAM_READ_ERROR;
- return;
+
+ return false;
+}
+
+wxZipEntry *wxZipInputStream::GetNextEntry()
+{
+ if (m_position == wxInvalidOffset)
+ if (!LoadEndRecord())
+ return NULL;
+
+ m_lasterror = m_parentSeekable ? ReadCentral() : ReadLocal();
+ if (!IsOk())
+ return NULL;
+
+ _wxZipEntryPtr entry(new wxZipEntry(m_entry));
+ entry->m_backlink = m_weaklinks->AddEntry(entry.get(), entry->GetKey());
+ return entry.release();
+}
+
+wxStreamError wxZipInputStream::ReadCentral()
+{
+ if (!AtHeader())
+ CloseEntry();
+
+ if (m_signature == END_MAGIC)
+ return wxSTREAM_EOF;
+
+ if (m_signature != CENTRAL_MAGIC) {
+ wxLogError(_("error reading zip central directory"));
+ return wxSTREAM_READ_ERROR;
}
- unzGetCurrentFileInfo((unzFile)m_Archive, &zinfo, (char*) NULL, 0, (void*) NULL, 0, (char*) NULL, 0);
+ if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset)
+ return wxSTREAM_READ_ERROR;
- if (unzOpenCurrentFile((unzFile)m_Archive) != UNZ_OK)
- {
- m_lasterror = wxSTREAM_READ_ERROR;
- return;
+ m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+ if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
+ m_signature = 0;
+ return wxSTREAM_READ_ERROR;
}
- m_Size = zinfo.uncompressed_size;
+
+ m_signature = ReadSignature();
+
+ if (m_offsetAdjustment)
+ m_entry.SetOffset(m_entry.GetOffset() + m_offsetAdjustment);
+ m_entry.SetKey(m_entry.GetOffset());
+
+ return wxSTREAM_NO_ERROR;
}
+wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/)
+{
+ if (!AtHeader())
+ CloseEntry();
+
+ if (!m_signature)
+ m_signature = ReadSignature();
+ if (m_signature == CENTRAL_MAGIC || m_signature == END_MAGIC) {
+ if (m_streamlink && !m_streamlink->GetOutputStream()) {
+ m_streamlink->Release(this);
+ m_streamlink = NULL;
+ }
+ }
-wxZipInputStream::~wxZipInputStream()
+ while (m_signature == CENTRAL_MAGIC) {
+ if (m_weaklinks->IsEmpty() && m_streamlink == NULL)
+ return wxSTREAM_EOF;
+
+ m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+ m_signature = 0;
+ if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR)
+ return wxSTREAM_READ_ERROR;
+
+ wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset());
+ if (entry) {
+ entry->SetSystemMadeBy(m_entry.GetSystemMadeBy());
+ entry->SetVersionMadeBy(m_entry.GetVersionMadeBy());
+ entry->SetComment(m_entry.GetComment());
+ entry->SetDiskStart(m_entry.GetDiskStart());
+ entry->SetInternalAttributes(m_entry.GetInternalAttributes());
+ entry->SetExternalAttributes(m_entry.GetExternalAttributes());
+ Copy(entry->m_Extra, m_entry.m_Extra);
+ entry->Notify();
+ m_weaklinks->RemoveEntry(entry->GetOffset());
+ }
+
+ m_signature = ReadSignature();
+ }
+
+ if (m_signature == END_MAGIC) {
+ if (readEndRec || m_streamlink) {
+ wxZipEndRec endrec;
+ endrec.Read(*m_parent_i_stream, GetConv());
+ m_Comment = endrec.GetComment();
+ m_signature = 0;
+ if (m_streamlink) {
+ m_streamlink->GetOutputStream()->SetComment(endrec.GetComment());
+ m_streamlink->Release(this);
+ m_streamlink = NULL;
+ }
+ }
+ return wxSTREAM_EOF;
+ }
+
+ if (m_signature != LOCAL_MAGIC) {
+ wxLogError(_("error reading zip local header"));
+ return wxSTREAM_READ_ERROR;
+ }
+
+ m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
+ m_signature = 0;
+ m_entry.SetOffset(m_position);
+ m_entry.SetKey(m_position);
+
+ if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
+ return wxSTREAM_READ_ERROR;
+ } else {
+ m_TotalEntries++;
+ return wxSTREAM_NO_ERROR;
+ }
+}
+
+wxUint32 wxZipInputStream::ReadSignature()
{
- if (m_Archive)
- {
- if (m_Size != 0)
- unzCloseCurrentFile((unzFile)m_Archive);
- unzClose((unzFile)m_Archive);
+ char magic[4];
+ m_parent_i_stream->Read(magic, 4);
+ return m_parent_i_stream->LastRead() == 4 ? CrackUint32(magic) : 0;
+}
+
+bool wxZipInputStream::OpenEntry(wxArchiveEntry& entry)
+{
+ wxZipEntry *zipEntry = wxStaticCast(&entry, wxZipEntry);
+ return zipEntry ? OpenEntry(*zipEntry) : false;
+}
+
+// Open an entry
+//
+bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw)
+{
+ if (m_position == wxInvalidOffset)
+ if (!LoadEndRecord())
+ return false;
+ if (m_lasterror == wxSTREAM_READ_ERROR)
+ return false;
+ wxCHECK(!IsOpened(), false);
+
+ m_raw = raw;
+
+ if (entry) {
+ if (AfterHeader() && entry->GetKey() == m_entry.GetOffset())
+ return true;
+ // can only open the current entry on a non-seekable stream
+ wxCHECK(m_parentSeekable, false);
+ }
+
+ m_lasterror = wxSTREAM_READ_ERROR;
+
+ if (entry)
+ m_entry = *entry;
+
+ if (m_parentSeekable) {
+ if (QuietSeek(*m_parent_i_stream, m_entry.GetOffset())
+ == wxInvalidOffset)
+ return false;
+ if (ReadSignature() != LOCAL_MAGIC) {
+ wxLogError(_("bad zipfile offset to entry"));
+ return false;
+ }
+ }
+
+ if (m_parentSeekable || AtHeader()) {
+ m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
+ if (m_parentSeekable) {
+ wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey());
+ if (ref) {
+ Copy(ref->m_LocalExtra, m_entry.m_LocalExtra);
+ ref->Notify();
+ m_weaklinks->RemoveEntry(ref->GetKey());
+ }
+ if (entry && entry != ref) {
+ Copy(entry->m_LocalExtra, m_entry.m_LocalExtra);
+ entry->Notify();
+ }
+ }
+ }
+
+ m_lasterror = m_parent_i_stream->GetLastError();
+ return IsOk();
+}
+
+bool wxZipInputStream::OpenDecompressor(bool raw /*=false*/)
+{
+ wxASSERT(AfterHeader());
+
+ wxFileOffset compressedSize = m_entry.GetCompressedSize();
+
+ if (raw)
+ m_raw = true;
+
+ if (m_raw) {
+ if (compressedSize != wxInvalidOffset) {
+ m_store->Open(compressedSize);
+ m_decomp = m_store;
+ } else {
+ if (!m_rawin)
+ m_rawin = new wxRawInputStream(*m_parent_i_stream);
+ m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
+ }
+ } else {
+ if (compressedSize != wxInvalidOffset &&
+ (m_entry.GetMethod() != wxZIP_METHOD_DEFLATE ||
+ wxZlibInputStream::CanHandleGZip())) {
+ m_store->Open(compressedSize);
+ m_decomp = OpenDecompressor(*m_store);
+ } else {
+ m_decomp = OpenDecompressor(*m_parent_i_stream);
+ }
}
+
+ m_crcAccumulator = crc32(0, Z_NULL, 0);
+ m_lasterror = m_decomp ? m_decomp->GetLastError() : wxSTREAM_READ_ERROR;
+ return IsOk();
}
-bool wxZipInputStream::Eof() const
+// Can be overriden to add support for additional decompression methods
+//
+wxInputStream *wxZipInputStream::OpenDecompressor(wxInputStream& stream)
{
- wxASSERT_MSG( m_Pos <= m_Size,
- _T("wxZipInputStream: invalid current position") );
+ switch (m_entry.GetMethod()) {
+ case wxZIP_METHOD_STORE:
+ if (m_entry.GetSize() == wxInvalidOffset) {
+ wxLogError(_("stored file length not in Zip header"));
+ break;
+ }
+ m_store->Open(m_entry.GetSize());
+ return m_store;
+
+ case wxZIP_METHOD_DEFLATE:
+ if (!m_inflate)
+ m_inflate = new wxZlibInputStream2(stream);
+ else
+ m_inflate->Open(stream);
+ return m_inflate;
- return m_Pos >= m_Size;
+ default:
+ wxLogError(_("unsupported Zip compression method"));
+ }
+
+ return NULL;
}
+bool wxZipInputStream::CloseDecompressor(wxInputStream *decomp)
+{
+ if (decomp && decomp == m_rawin)
+ return CloseDecompressor(m_rawin->GetFilterInputStream());
+ if (decomp != m_store && decomp != m_inflate)
+ delete decomp;
+ return true;
+}
-size_t wxZipInputStream::OnSysRead(void *buffer, size_t bufsize)
+// Closes the current entry and positions the underlying stream at the start
+// of the next entry
+//
+bool wxZipInputStream::CloseEntry()
{
- wxASSERT_MSG( m_Pos <= m_Size,
- _T("wxZipInputStream: invalid current position") );
+ if (AtHeader())
+ return true;
+ if (m_lasterror == wxSTREAM_READ_ERROR)
+ return false;
- if ( m_Pos >= m_Size )
- {
- m_lasterror = wxSTREAM_EOF;
- return 0;
+ if (!m_parentSeekable) {
+ if (!IsOpened() && !OpenDecompressor(true))
+ return false;
+
+ const int BUFSIZE = 8192;
+ wxCharBuffer buf(BUFSIZE);
+ while (IsOk())
+ Read(buf.data(), BUFSIZE);
+
+ m_position += m_headerSize + m_entry.GetCompressedSize();
}
- if (m_Pos + bufsize > m_Size + (size_t)0)
- bufsize = m_Size - m_Pos;
+ if (m_lasterror == wxSTREAM_EOF)
+ m_lasterror = wxSTREAM_NO_ERROR;
- unzReadCurrentFile((unzFile)m_Archive, buffer, bufsize);
- m_Pos += bufsize;
+ CloseDecompressor(m_decomp);
+ m_decomp = NULL;
+ m_entry = wxZipEntry();
+ m_headerSize = 0;
+ m_raw = false;
- return bufsize;
+ return IsOk();
}
+size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
+{
+ if (!IsOpened())
+ if ((AtHeader() && !DoOpen()) || !OpenDecompressor())
+ m_lasterror = wxSTREAM_READ_ERROR;
+ if (!IsOk() || !size)
+ return 0;
+
+ size_t count = m_decomp->Read(buffer, size).LastRead();
+ if (!m_raw)
+ m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count);
+ m_lasterror = m_decomp->GetLastError();
+
+ if (Eof()) {
+ if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) {
+ m_headerSize += m_entry.ReadDescriptor(*m_parent_i_stream);
+ wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetKey());
+
+ if (entry) {
+ entry->SetCrc(m_entry.GetCrc());
+ entry->SetCompressedSize(m_entry.GetCompressedSize());
+ entry->SetSize(m_entry.GetSize());
+ entry->Notify();
+ }
+ }
+
+ if (!m_raw) {
+ m_lasterror = wxSTREAM_READ_ERROR;
+
+ if (m_parent_i_stream->IsOk()) {
+ if (m_entry.GetSize() != TellI())
+ wxLogError(_("reading zip stream (entry %s): bad length"),
+ m_entry.GetName().c_str());
+ else if (m_crcAccumulator != m_entry.GetCrc())
+ wxLogError(_("reading zip stream (entry %s): bad crc"),
+ m_entry.GetName().c_str());
+ else
+ m_lasterror = wxSTREAM_EOF;
+ }
+ }
+ }
+ return count;
+}
+// Borrowed from VS's zip stream (c) 1999 Vaclav Slavik
+//
wxFileOffset wxZipInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
{
+ if (!m_ffile || AtHeader())
+ return wxInvalidOffset;
+
// NB: since ZIP files don't natively support seeking, we have to
// implement a brute force workaround -- reading all the data
// between current and the new position (or between beginning of
// the file and new position...)
wxFileOffset nextpos;
+ wxFileOffset pos = TellI();
switch ( mode )
{
- case wxFromCurrent : nextpos = seek + m_Pos; break;
+ case wxFromCurrent : nextpos = seek + pos; break;
case wxFromStart : nextpos = seek; break;
- case wxFromEnd : nextpos = m_Size - 1 + seek; break;
- default : nextpos = m_Pos; break; /* just to fool compiler, never happens */
+ case wxFromEnd : nextpos = GetSize() - 1 + seek; break;
+ default : nextpos = pos; break; /* just to fool compiler, never happens */
}
size_t toskip;
- if ( nextpos > m_Pos )
+ if ( nextpos >= pos )
{
- toskip = nextpos - m_Pos;
+ toskip = nextpos - pos;
}
else
{
- unzCloseCurrentFile((unzFile)m_Archive);
- if (unzOpenCurrentFile((unzFile)m_Archive) != UNZ_OK)
+ wxZipEntry current(m_entry);
+ CloseEntry();
+ if (!OpenEntry(current))
{
m_lasterror = wxSTREAM_READ_ERROR;
- return m_Pos;
+ return pos;
}
toskip = nextpos;
}
while ( toskip > 0 )
{
sz = wxMin(toskip, BUFSIZE);
- unzReadCurrentFile((unzFile)m_Archive, buffer, sz);
+ Read(buffer, sz);
toskip -= sz;
}
}
- m_Pos = nextpos;
- return m_Pos;
+ pos = nextpos;
+ return pos;
}
-#endif
- // wxUSE_STREAMS && wxUSE_ZIPSTREAM && wxUSE_ZLIB
+
+/////////////////////////////////////////////////////////////////////////////
+// Output stream
+
+#include <wx/listimpl.cpp>
+WX_DEFINE_LIST(_wxZipEntryList);
+
+wxZipOutputStream::wxZipOutputStream(wxOutputStream& stream,
+ int level /*=-1*/,
+ wxMBConv& conv /*=wxConvLocal*/)
+ : wxArchiveOutputStream(stream, conv),
+ m_store(new wxStoredOutputStream(stream)),
+ m_deflate(NULL),
+ m_backlink(NULL),
+ m_initialData(new char[OUTPUT_LATENCY]),
+ m_initialSize(0),
+ m_pending(NULL),
+ m_raw(false),
+ m_headerOffset(0),
+ m_headerSize(0),
+ m_entrySize(0),
+ m_comp(NULL),
+ m_level(level),
+ m_offsetAdjustment(wxInvalidOffset)
+{
+}
+
+wxZipOutputStream::~wxZipOutputStream()
+{
+ Close();
+ WX_CLEAR_LIST(_wxZipEntryList, m_entries);
+ delete m_store;
+ delete m_deflate;
+ delete m_pending;
+ delete [] m_initialData;
+ if (m_backlink)
+ m_backlink->Release(this);
+}
+
+bool wxZipOutputStream::PutNextEntry(
+ const wxString& name,
+ const wxDateTime& dt /*=wxDateTime::Now()*/,
+ wxFileOffset size /*=wxInvalidOffset*/)
+{
+ return PutNextEntry(new wxZipEntry(name, dt, size));
+}
+
+bool wxZipOutputStream::PutNextDirEntry(
+ const wxString& name,
+ const wxDateTime& dt /*=wxDateTime::Now()*/)
+{
+ wxZipEntry *entry = new wxZipEntry(name, dt);
+ entry->SetIsDir();
+ return PutNextEntry(entry);
+}
+
+bool wxZipOutputStream::CopyEntry(wxZipEntry *entry,
+ wxZipInputStream& inputStream)
+{
+ _wxZipEntryPtr e(entry);
+
+ return
+ inputStream.DoOpen(e.get(), true) &&
+ DoCreate(e.release(), true) &&
+ Write(inputStream).IsOk() && inputStream.Eof();
+}
+
+bool wxZipOutputStream::PutNextEntry(wxArchiveEntry *entry)
+{
+ wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
+ if (!zipEntry)
+ delete entry;
+ return PutNextEntry(zipEntry);
+}
+
+bool wxZipOutputStream::CopyEntry(wxArchiveEntry *entry,
+ wxArchiveInputStream& stream)
+{
+ wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
+
+ if (!zipEntry || !stream.OpenEntry(*zipEntry)) {
+ delete entry;
+ return false;
+ }
+
+ return CopyEntry(zipEntry, wx_static_cast(wxZipInputStream&, stream));
+}
+
+bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream)
+{
+ m_Comment = inputStream.GetComment();
+ if (m_backlink)
+ m_backlink->Release(this);
+ m_backlink = inputStream.MakeLink(this);
+ return true;
+}
+
+bool wxZipOutputStream::CopyArchiveMetaData(wxArchiveInputStream& stream)
+{
+ return CopyArchiveMetaData(wx_static_cast(wxZipInputStream&, stream));
+}
+
+void wxZipOutputStream::SetLevel(int level)
+{
+ if (level != m_level) {
+ if (m_comp != m_deflate)
+ delete m_deflate;
+ m_deflate = NULL;
+ m_level = level;
+ }
+}
+
+bool wxZipOutputStream::DoCreate(wxZipEntry *entry, bool raw /*=false*/)
+{
+ CloseEntry();
+
+ m_pending = entry;
+ if (!m_pending)
+ return false;
+
+ // write the signature bytes right away
+ wxDataOutputStream ds(*m_parent_o_stream);
+ ds << LOCAL_MAGIC;
+
+ // and if this is the first entry test for seekability
+ if (m_headerOffset == 0) {
+ bool logging = wxLog::IsEnabled();
+ wxLogNull nolog;
+ wxFileOffset here = m_parent_o_stream->TellO();
+
+ if (here != wxInvalidOffset && here >= 4) {
+ if (m_parent_o_stream->SeekO(here - 4) == here - 4) {
+ m_offsetAdjustment = here - 4;
+ wxLog::EnableLogging(logging);
+ m_parent_o_stream->SeekO(here);
+ }
+ }
+ }
+
+ m_pending->SetOffset(m_headerOffset);
+
+ m_crcAccumulator = crc32(0, Z_NULL, 0);
+
+ if (raw)
+ m_raw = true;
+
+ m_lasterror = wxSTREAM_NO_ERROR;
+ return true;
+}
+
+// Can be overriden to add support for additional compression methods
+//
+wxOutputStream *wxZipOutputStream::OpenCompressor(
+ wxOutputStream& stream,
+ wxZipEntry& entry,
+ const Buffer bufs[])
+{
+ if (entry.GetMethod() == wxZIP_METHOD_DEFAULT) {
+ if (GetLevel() == 0
+ && (IsParentSeekable()
+ || entry.GetCompressedSize() != wxInvalidOffset
+ || entry.GetSize() != wxInvalidOffset)) {
+ entry.SetMethod(wxZIP_METHOD_STORE);
+ } else {
+ int size = 0;
+ for (int i = 0; bufs[i].m_data; ++i)
+ size += bufs[i].m_size;
+ entry.SetMethod(size <= 6 ?
+ wxZIP_METHOD_STORE : wxZIP_METHOD_DEFLATE);
+ }
+ }
+
+ switch (entry.GetMethod()) {
+ case wxZIP_METHOD_STORE:
+ if (entry.GetCompressedSize() == wxInvalidOffset)
+ entry.SetCompressedSize(entry.GetSize());
+ return m_store;
+
+ case wxZIP_METHOD_DEFLATE:
+ {
+ int defbits = wxZIP_DEFLATE_NORMAL;
+ switch (GetLevel()) {
+ case 0: case 1:
+ defbits = wxZIP_DEFLATE_SUPERFAST;
+ break;
+ case 2: case 3: case 4:
+ defbits = wxZIP_DEFLATE_FAST;
+ break;
+ case 8: case 9:
+ defbits = wxZIP_DEFLATE_EXTRA;
+ break;
+ }
+ entry.SetFlags((entry.GetFlags() & ~wxZIP_DEFLATE_MASK) |
+ defbits | wxZIP_SUMS_FOLLOW);
+
+ if (!m_deflate)
+ m_deflate = new wxZlibOutputStream2(stream, GetLevel());
+ else
+ m_deflate->Open(stream);
+
+ return m_deflate;
+ }
+
+ default:
+ wxLogError(_("unsupported Zip compression method"));
+ }
+
+ return NULL;
+}
+
+bool wxZipOutputStream::CloseCompressor(wxOutputStream *comp)
+{
+ if (comp == m_deflate)
+ m_deflate->Close();
+ else if (comp != m_store)
+ delete comp;
+ return true;
+}
+
+// This is called when OUPUT_LATENCY bytes has been written to the
+// wxZipOutputStream to actually create the zip entry.
+//
+void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size)
+{
+ wxASSERT(IsOk() && m_pending && !m_comp);
+ _wxZipEntryPtr spPending(m_pending);
+ m_pending = NULL;
+
+ Buffer bufs[] = {
+ { m_initialData, m_initialSize },
+ { (const char*)buffer, size },
+ { NULL, 0 }
+ };
+
+ if (m_raw)
+ m_comp = m_store;
+ else
+ m_comp = OpenCompressor(*m_store, *spPending,
+ m_initialSize ? bufs : bufs + 1);
+
+ if (IsParentSeekable()
+ || (spPending->m_Crc
+ && spPending->m_CompressedSize != wxInvalidOffset
+ && spPending->m_Size != wxInvalidOffset))
+ spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
+ else
+ if (spPending->m_CompressedSize != wxInvalidOffset)
+ spPending->m_Flags |= wxZIP_SUMS_FOLLOW;
+
+ m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
+ m_lasterror = m_parent_o_stream->GetLastError();
+
+ if (IsOk()) {
+ m_entries.push_back(spPending.release());
+ OnSysWrite(m_initialData, m_initialSize);
+ }
+
+ m_initialSize = 0;
+}
+
+// This is called to write out the zip entry when Close has been called
+// before OUTPUT_LATENCY bytes has been written to the wxZipOutputStream.
+//
+void wxZipOutputStream::CreatePendingEntry()
+{
+ wxASSERT(IsOk() && m_pending && !m_comp);
+ _wxZipEntryPtr spPending(m_pending);
+ m_pending = NULL;
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+
+ if (!m_raw) {
+ // Initially compresses the data to memory, then fall back to 'store'
+ // if the compressor makes the data larger rather than smaller.
+ wxMemoryOutputStream mem;
+ Buffer bufs[] = { { m_initialData, m_initialSize }, { NULL, 0 } };
+ wxOutputStream *comp = OpenCompressor(mem, *spPending, bufs);
+
+ if (!comp)
+ return;
+ if (comp != m_store) {
+ bool ok = comp->Write(m_initialData, m_initialSize).IsOk();
+ CloseCompressor(comp);
+ if (!ok)
+ return;
+ }
+
+ m_entrySize = m_initialSize;
+ m_crcAccumulator = crc32(0, (Byte*)m_initialData, m_initialSize);
+
+ if (mem.GetSize() > 0 && mem.GetSize() < m_initialSize) {
+ m_initialSize = mem.GetSize();
+ mem.CopyTo(m_initialData, m_initialSize);
+ } else {
+ spPending->SetMethod(wxZIP_METHOD_STORE);
+ }
+
+ spPending->SetSize(m_entrySize);
+ spPending->SetCrc(m_crcAccumulator);
+ spPending->SetCompressedSize(m_initialSize);
+ }
+
+ spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
+ m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
+
+ if (m_parent_o_stream->IsOk()) {
+ m_entries.push_back(spPending.release());
+ m_comp = m_store;
+ m_store->Write(m_initialData, m_initialSize);
+ }
+
+ m_initialSize = 0;
+ m_lasterror = m_parent_o_stream->GetLastError();
+}
+
+// Write the 'central directory' and the 'end-central-directory' records.
+//
+bool wxZipOutputStream::Close()
+{
+ CloseEntry();
+
+ if (m_lasterror == wxSTREAM_WRITE_ERROR || m_entries.size() == 0)
+ return false;
+
+ wxZipEndRec endrec;
+
+ endrec.SetEntriesHere(m_entries.size());
+ endrec.SetTotalEntries(m_entries.size());
+ endrec.SetOffset(m_headerOffset);
+ endrec.SetComment(m_Comment);
+
+ _wxZipEntryList::iterator it;
+ wxFileOffset size = 0;
+
+ for (it = m_entries.begin(); it != m_entries.end(); ++it) {
+ size += (*it)->WriteCentral(*m_parent_o_stream, GetConv());
+ delete *it;
+ }
+ m_entries.clear();
+
+ endrec.SetSize(size);
+ endrec.Write(*m_parent_o_stream, GetConv());
+
+ m_lasterror = m_parent_o_stream->GetLastError();
+ if (!IsOk())
+ return false;
+ m_lasterror = wxSTREAM_EOF;
+ return true;
+}
+
+// Finish writing the current entry
+//
+bool wxZipOutputStream::CloseEntry()
+{
+ if (IsOk() && m_pending)
+ CreatePendingEntry();
+ if (!IsOk())
+ return false;
+ if (!m_comp)
+ return true;
+
+ CloseCompressor(m_comp);
+ m_comp = NULL;
+
+ wxFileOffset compressedSize = m_store->TellO();
+
+ wxZipEntry& entry = *m_entries.back();
+
+ // When writing raw the crc and size can't be checked
+ if (m_raw) {
+ m_crcAccumulator = entry.GetCrc();
+ m_entrySize = entry.GetSize();
+ }
+
+ // Write the sums in the trailing 'data descriptor' if necessary
+ if (entry.m_Flags & wxZIP_SUMS_FOLLOW) {
+ wxASSERT(!IsParentSeekable());
+ m_headerOffset +=
+ entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
+ compressedSize, m_entrySize);
+ m_lasterror = m_parent_o_stream->GetLastError();
+ }
+
+ // If the local header didn't have the correct crc and size written to
+ // it then seek back and fix it
+ else if (m_crcAccumulator != entry.GetCrc()
+ || m_entrySize != entry.GetSize()
+ || compressedSize != entry.GetCompressedSize())
+ {
+ if (IsParentSeekable()) {
+ wxFileOffset here = m_parent_o_stream->TellO();
+ wxFileOffset headerOffset = m_headerOffset + m_offsetAdjustment;
+ m_parent_o_stream->SeekO(headerOffset + SUMS_OFFSET);
+ entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
+ compressedSize, m_entrySize);
+ m_parent_o_stream->SeekO(here);
+ m_lasterror = m_parent_o_stream->GetLastError();
+ } else {
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ }
+ }
+
+ m_headerOffset += m_headerSize + compressedSize;
+ m_headerSize = m_entrySize = 0;
+ m_store->Close();
+ m_raw = false;
+
+ if (IsOk())
+ m_lasterror = m_parent_o_stream->GetLastError();
+ else
+ wxLogError(_("error writing zip entry '%s': bad crc or length"),
+ entry.GetName().c_str());
+ return IsOk();
+}
+
+void wxZipOutputStream::Sync()
+{
+ if (IsOk() && m_pending)
+ CreatePendingEntry(NULL, 0);
+ if (!m_comp)
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ if (IsOk()) {
+ m_comp->Sync();
+ m_lasterror = m_comp->GetLastError();
+ }
+}
+
+size_t wxZipOutputStream::OnSysWrite(const void *buffer, size_t size)
+{
+ if (IsOk() && m_pending) {
+ if (m_initialSize + size < OUTPUT_LATENCY) {
+ memcpy(m_initialData + m_initialSize, buffer, size);
+ m_initialSize += size;
+ return size;
+ } else {
+ CreatePendingEntry(buffer, size);
+ }
+ }
+
+ if (!m_comp)
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ if (!IsOk() || !size)
+ return 0;
+
+ if (m_comp->Write(buffer, size).LastWrite() != size)
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+ m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, size);
+ m_entrySize += m_comp->LastWrite();
+
+ return m_comp->LastWrite();
+}
+
+#endif // wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: tests/archive/archive.cpp
+// Purpose: Test the archive classes
+// Author: Mike Wetherell
+// RCS-ID: $Id$
+// Copyright: (c) 2004 Mike Wetherell
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+# pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+# include "wx/wx.h"
+#endif
+
+#if wxUSE_STREAMS
+
+#define WX_TEST_ARCHIVE_ITERATOR
+
+#include "wx/zipstrm.h"
+#include "wx/mstream.h"
+#include "wx/wfstream.h"
+#include "wx/dir.h"
+#include "wx/cppunit.h"
+#include <string>
+#include <list>
+#include <sys/stat.h>
+
+// Check whether member templates can be used
+//
+#if defined __GNUC__ && \
+ (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined _MSC_VER && _MSC_VER >= 1310
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined __BORLANDC__ && __BORLANDC__ >= 0x530
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined __DMC__ && __DMC__ >= 0x832
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined __MWERKS__ && __MWERKS__ >= 0x2200
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined __HP_aCC && __HP_aCC > 33300
+# define WXARC_MEMBER_TEMPLATES
+#endif
+#if defined __SUNPRO_CC && __SUNPRO_CC > 0x500
+# define WXARC_MEMBER_TEMPLATES
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Bit flags for options for the tests
+
+enum Options
+{
+ PipeIn = 0x01, // input streams are non-seekable
+ PipeOut = 0x02, // output streams are non-seekable
+ Stub = 0x04, // the archive should be appended to a stub
+ AllOptions = 0x07
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// These structs are passed as the template parameter of the test case to
+// specify a set of classes to use in the test. This allows either the generic
+// wxArchiveXXX interface to be exercised or the specific interface for a
+// particular archive format e.g. wxZipXXX.
+
+struct ArchiveClasses
+{
+ typedef wxArchiveEntry EntryT;
+ typedef wxArchiveInputStream InputStreamT;
+ typedef wxArchiveOutputStream OutputStreamT;
+ typedef wxArchiveClassFactory ClassFactoryT;
+ typedef wxArchiveNotifier NotifierT;
+ typedef wxArchiveIter IterT;
+ typedef wxArchivePairIter PairIterT;
+};
+
+struct ZipClasses
+{
+ typedef wxZipEntry EntryT;
+ typedef wxZipInputStream InputStreamT;
+ typedef wxZipOutputStream OutputStreamT;
+ typedef wxZipClassFactory ClassFactoryT;
+ typedef wxZipNotifier NotifierT;
+ typedef wxZipIter IterT;
+ typedef wxZipPairIter PairIterT;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// A class to hold a test entry
+
+class TestEntry
+{
+public:
+ TestEntry(const wxDateTime& dt, int len, const char *data);
+ ~TestEntry() { delete [] m_data; }
+
+ wxDateTime GetDateTime() const { return m_dt; }
+ wxFileOffset GetLength() const { return m_len; }
+ size_t GetSize() const { return m_len; }
+ const char *GetData() const { return m_data; }
+ wxString GetComment() const { return m_comment; }
+ bool IsText() const { return m_isText; }
+
+ void SetComment(const wxString& comment) { m_comment = comment; }
+ void SetDateTime(const wxDateTime& dt) { m_dt = dt; }
+
+private:
+ wxDateTime m_dt;
+ size_t m_len;
+ const char *m_data;
+ wxString m_comment;
+ bool m_isText;
+};
+
+TestEntry::TestEntry(const wxDateTime& dt, int len, const char *data)
+ : m_dt(dt),
+ m_len(len),
+ m_isText(len > 0)
+{
+ char *d = new char[len];
+ memcpy(d, data, len);
+ m_data = d;
+
+ for (int i = 0; i < len && m_isText; i++)
+ m_isText = (signed char)m_data[i] > 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestOutputStream and TestInputStream are memory streams which can be
+// seekable or non-seekable.
+
+class TestOutputStream : public wxOutputStream
+{
+public:
+ TestOutputStream(int options);
+
+ ~TestOutputStream() { delete [] m_data; }
+
+ int GetOptions() const { return m_options; }
+ size_t GetSize() const { return m_size; }
+
+ // gives away the data, this stream is then empty, and can be reused
+ void GetData(const char*& data, size_t& size);
+
+ enum { STUB_SIZE = 2048, INITIAL_SIZE = 0x18000, SEEK_LIMIT = 0x100000 };
+
+private:
+ void Init();
+
+ wxFileOffset OnSysSeek(wxFileOffset pos, wxSeekMode mode);
+ wxFileOffset OnSysTell() const;
+ size_t OnSysWrite(const void *buffer, size_t size);
+
+ int m_options;
+ size_t m_pos;
+ size_t m_capacity;
+ size_t m_size;
+ char *m_data;
+};
+
+TestOutputStream::TestOutputStream(int options)
+ : m_options(options)
+{
+ Init();
+}
+
+void TestOutputStream::Init()
+{
+ m_data = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ m_pos = 0;
+
+ if (m_options & Stub) {
+ wxCharBuffer buf(STUB_SIZE);
+ memset(buf.data(), 0, STUB_SIZE);
+ Write(buf, STUB_SIZE);
+ }
+}
+
+wxFileOffset TestOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
+{
+ if ((m_options & PipeOut) == 0) {
+ switch (mode) {
+ case wxFromStart: break;
+ case wxFromCurrent: pos += m_pos; break;
+ case wxFromEnd: pos += m_size; break;
+ }
+ if (pos < 0 || pos > SEEK_LIMIT)
+ return wxInvalidOffset;
+ m_pos = pos;
+ return m_pos;
+ }
+ return wxInvalidOffset;
+}
+
+wxFileOffset TestOutputStream::OnSysTell() const
+{
+ return (m_options & PipeOut) == 0 ? m_pos : wxInvalidOffset;
+}
+
+size_t TestOutputStream::OnSysWrite(const void *buffer, size_t size)
+{
+ if (!IsOk() || !size)
+ return 0;
+ m_lasterror = wxSTREAM_WRITE_ERROR;
+
+ size_t newsize = m_pos + size;
+ wxCHECK(newsize > m_pos, 0);
+
+ if (m_capacity < newsize) {
+ size_t capacity = m_capacity ? m_capacity : INITIAL_SIZE;
+
+ while (capacity < newsize) {
+ capacity <<= 1;
+ wxCHECK(capacity > m_capacity, 0);
+ }
+
+ char *buf = new char[capacity];
+ if (m_data)
+ memcpy(buf, m_data, m_capacity);
+ delete [] m_data;
+ m_data = buf;
+ m_capacity = capacity;
+ }
+
+ memcpy(m_data + m_pos, buffer, size);
+ m_pos += size;
+ if (m_pos > m_size)
+ m_size = m_pos;
+ m_lasterror = wxSTREAM_NO_ERROR;
+
+ return size;
+}
+
+void TestOutputStream::GetData(const char*& data, size_t& size)
+{
+ data = m_data;
+ size = m_size;
+
+ if (m_options & Stub) {
+ char *d = m_data;
+ size += STUB_SIZE;
+
+ if (size > m_capacity) {
+ d = new char[size];
+ memcpy(d + STUB_SIZE, m_data, m_size);
+ delete [] m_data;
+ }
+ else {
+ memmove(d + STUB_SIZE, d, m_size);
+ }
+
+ memset(d, 0, STUB_SIZE);
+ data = d;
+ }
+
+ Init();
+ Reset();
+}
+
+class TestInputStream : public wxInputStream
+{
+public:
+ // ctor takes the data from the output stream, which is then empty
+ TestInputStream(TestOutputStream& out) : m_data(NULL) { SetData(out); }
+ // this ctor 'dups'
+ TestInputStream(const TestInputStream& in);
+ ~TestInputStream() { delete [] m_data; }
+
+ void Rewind();
+ size_t GetSize() const { return m_size; }
+ void SetData(TestOutputStream& out);
+
+private:
+ wxFileOffset OnSysSeek(wxFileOffset pos, wxSeekMode mode);
+ wxFileOffset OnSysTell() const;
+ size_t OnSysRead(void *buffer, size_t size);
+
+ int m_options;
+ size_t m_pos;
+ size_t m_size;
+ const char *m_data;
+};
+
+TestInputStream::TestInputStream(const TestInputStream& in)
+ : m_options(in.m_options),
+ m_pos(in.m_pos),
+ m_size(in.m_size)
+{
+ char *p = new char[m_size];
+ memcpy(p, in.m_data, m_size);
+ m_data = p;
+}
+
+void TestInputStream::Rewind()
+{
+ if ((m_options & Stub) && (m_options & PipeIn))
+ m_pos = TestOutputStream::STUB_SIZE * 2;
+ else
+ m_pos = 0;
+
+ if (m_wbacksize) {
+ free(m_wback);
+ m_wback = NULL;
+ m_wbacksize = 0;
+ m_wbackcur = 0;
+ }
+}
+
+void TestInputStream::SetData(TestOutputStream& out)
+{
+ delete [] m_data;
+ m_options = out.GetOptions();
+ out.GetData(m_data, m_size);
+ Rewind();
+ Reset();
+}
+
+wxFileOffset TestInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
+{
+ if ((m_options & PipeIn) == 0) {
+ switch (mode) {
+ case wxFromStart: break;
+ case wxFromCurrent: pos += m_pos; break;
+ case wxFromEnd: pos += m_size; break;
+ }
+ if (pos < 0 || pos > TestOutputStream::SEEK_LIMIT)
+ return wxInvalidOffset;
+ m_pos = pos;
+ return m_pos;
+ }
+ return wxInvalidOffset;
+}
+
+wxFileOffset TestInputStream::OnSysTell() const
+{
+ return (m_options & PipeIn) == 0 ? m_pos : wxInvalidOffset;
+}
+
+size_t TestInputStream::OnSysRead(void *buffer, size_t size)
+{
+ if (!IsOk() || !size)
+ return 0;
+ if (m_size <= m_pos) {
+ m_lasterror = wxSTREAM_EOF;
+ return 0;
+ }
+
+ if (m_size - m_pos < size)
+ size = m_size - m_pos;
+ memcpy(buffer, m_data + m_pos, size);
+ m_pos += size;
+ return size;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// minimal non-intrusive reference counting pointer for testing the iterators
+
+template <class T> class Ptr
+{
+public:
+ explicit Ptr(T* p = NULL) : m_p(p), m_count(new int) { *m_count = 1; }
+ Ptr(const Ptr& sp) : m_p(sp.m_p), m_count(sp.m_count) { ++*m_count; }
+ ~Ptr() { Free(); }
+
+ Ptr& operator =(const Ptr& sp) {
+ if (&sp != this) {
+ Free();
+ m_p = sp.m_p;
+ m_count = sp.m_count;
+ ++*m_count;
+ }
+ return *this;
+ }
+
+ T* get() const { return m_p; }
+ T* operator->() const { return m_p; }
+ T& operator*() const { return *m_p; }
+
+private:
+ void Free() {
+ if (--*m_count == 0) {
+ delete m_p;
+ delete m_count;
+ }
+ }
+
+ T *m_p;
+ int *m_count;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Clean-up for temp directory
+
+class TempDir
+{
+public:
+ TempDir();
+ ~TempDir();
+ wxString GetName() const { return m_tmp; }
+
+private:
+ void RemoveDir(wxString& path);
+ wxString m_tmp;
+ wxString m_original;
+};
+
+TempDir::TempDir()
+{
+ wxString tmp = wxFileName::CreateTempFileName(_T("arctest-"));
+ if (tmp != wxEmptyString) {
+ wxRemoveFile(tmp);
+ m_original = wxGetCwd();
+ CPPUNIT_ASSERT(wxMkdir(tmp, 0700));
+ m_tmp = tmp;
+ CPPUNIT_ASSERT(wxSetWorkingDirectory(tmp));
+ }
+}
+
+TempDir::~TempDir()
+{
+ if (m_tmp != wxEmptyString) {
+ wxSetWorkingDirectory(m_original);
+ RemoveDir(m_tmp);
+ }
+}
+
+void TempDir::RemoveDir(wxString& path)
+{
+ wxCHECK_RET(!m_tmp.empty() && path.substr(0, m_tmp.length()) == m_tmp,
+ _T("remove '") + path + _T("' fails safety check"));
+
+ const wxChar *files[] = {
+ _T("text/empty"),
+ _T("text/small"),
+ _T("bin/bin1000"),
+ _T("bin/bin4095"),
+ _T("bin/bin4096"),
+ _T("bin/bin4097"),
+ _T("bin/bin16384"),
+ _T("zero/zero5"),
+ _T("zero/zero1024"),
+ _T("zero/zero32768"),
+ _T("zero/zero16385"),
+ _T("zero/newname"),
+ _T("newfile"),
+ };
+
+ const wxChar *dirs[] = {
+ _T("text/"), _T("bin/"), _T("zero/"), _T("empty/")
+ };
+
+ wxString tmp = m_tmp + wxFileName::GetPathSeparator();
+ size_t i;
+
+ for (i = 0; i < WXSIZEOF(files); i++)
+ wxRemoveFile(tmp + wxFileName(files[i], wxPATH_UNIX).GetFullPath());
+
+ for (i = 0; i < WXSIZEOF(dirs); i++)
+ wxRmdir(tmp + wxFileName(dirs[i], wxPATH_UNIX).GetFullPath());
+
+ if (!wxRmdir(m_tmp))
+ wxLogSysError(_T("can't remove temporary dir '%s'"), m_tmp.c_str());
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// wxFFile streams for piping to/from an external program
+
+#if defined __UNIX__ || defined __MINGW32__
+# define WXARC_popen popen
+# define WXARC_pclose pclose
+#elif defined _MSC_VER || defined __BORLANDC__
+# define WXARC_popen _popen
+# define WXARC_pclose _pclose
+#else
+# define WXARC_NO_POPEN
+# define WXARC_popen(cmd, type) NULL
+# define WXARC_pclose(fp)
+#endif
+
+#ifdef __WXMSW__
+# define WXARC_b "b"
+#else
+# define WXARC_b
+#endif
+
+class PFileInputStream : public wxFFileInputStream
+{
+public:
+ PFileInputStream(const wxString& cmd) :
+ wxFFileInputStream(WXARC_popen(cmd.mb_str(), "r" WXARC_b)) { }
+ ~PFileInputStream()
+ { WXARC_pclose(m_file->fp()); m_file->Detach(); }
+};
+
+class PFileOutputStream : public wxFFileOutputStream
+{
+public:
+ PFileOutputStream(const wxString& cmd) :
+ wxFFileOutputStream(WXARC_popen(cmd.mb_str(), "w" WXARC_b)) { }
+ ~PFileOutputStream()
+ { WXARC_pclose(m_file->fp()); m_file->Detach(); }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// The test case
+
+template <class Classes>
+class ArchiveTestCase : public CppUnit::TestCase
+{
+public:
+ ArchiveTestCase(const wxString& name,
+ int id,
+ typename Classes::ClassFactoryT *factory,
+ int options,
+ const wxString& archiver = wxEmptyString,
+ const wxString& unarchiver = wxEmptyString);
+
+ ~ArchiveTestCase();
+
+protected:
+ // the classes to test
+ typedef typename Classes::EntryT EntryT;
+ typedef typename Classes::InputStreamT InputStreamT;
+ typedef typename Classes::OutputStreamT OutputStreamT;
+ typedef typename Classes::ClassFactoryT ClassFactoryT;
+ typedef typename Classes::NotifierT NotifierT;
+ typedef typename Classes::IterT IterT;
+ typedef typename Classes::PairIterT PairIterT;
+
+ // the entry point for the test
+ void runTest();
+
+ // create the test data
+ void CreateTestData();
+ TestEntry& Add(const char *name, const char *data, int len = -1);
+ TestEntry& Add(const char *name, int len = 0, int value = EOF);
+
+ // 'archive up' the test data
+ void CreateArchive(wxOutputStream& out);
+ void CreateArchive(wxOutputStream& out, const wxString& archiver);
+
+ // perform various modifications on the archive
+ void ModifyArchive(wxInputStream& in, wxOutputStream& out);
+
+ // extract the archive and verify its contents
+ void ExtractArchive(wxInputStream& in);
+ void ExtractArchive(wxInputStream& in, const wxString& unarchiver);
+ void VerifyDir(wxString& path, size_t rootlen = 0);
+
+ // tests for the iterators
+ void TestIterator(wxInputStream& in);
+ void TestPairIterator(wxInputStream& in);
+ void TestSmartIterator(wxInputStream& in);
+ void TestSmartPairIterator(wxInputStream& in);
+
+ // try reading two entries at the same time
+ void ReadSimultaneous(TestInputStream& in);
+
+ // overridables
+ virtual void OnCreateArchive(OutputStreamT& WXUNUSED(arc)) { }
+ virtual void OnSetNotifier(EntryT& entry);
+
+ virtual void OnArchiveExtracted(InputStreamT& WXUNUSED(arc),
+ int WXUNUSED(expectedTotal)) { }
+
+ virtual void OnCreateEntry( OutputStreamT& WXUNUSED(arc),
+ TestEntry& WXUNUSED(testEntry),
+ EntryT *entry = NULL) { (void)entry; }
+
+ virtual void OnEntryExtracted( EntryT& WXUNUSED(entry),
+ const TestEntry& WXUNUSED(testEntry),
+ InputStreamT *arc = NULL) { (void)arc; }
+
+ typedef std::map<wxString, TestEntry*> TestEntries;
+ TestEntries m_testEntries; // test data
+ std::auto_ptr<ClassFactoryT> m_factory; // factory to make classes
+ int m_options; // test options
+ wxDateTime m_timeStamp; // timestamp to give test entries
+ int m_id; // select between the possibilites
+ wxString m_archiver; // external archiver
+ wxString m_unarchiver; // external unarchiver
+};
+
+template <class Classes>
+ArchiveTestCase<Classes>::ArchiveTestCase(const wxString& name,
+ int id,
+ ClassFactoryT *factory,
+ int options,
+ const wxString& archiver,
+ const wxString& unarchiver)
+ : CppUnit::TestCase(std::string(name.mb_str())),
+ m_factory(factory),
+ m_options(options),
+ m_timeStamp(1, wxDateTime::Mar, 2005, 12, 0),
+ m_id(id),
+ m_archiver(archiver),
+ m_unarchiver(unarchiver)
+{
+}
+
+template <class Classes>
+ArchiveTestCase<Classes>::~ArchiveTestCase()
+{
+ TestEntries::iterator it;
+ for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it)
+ delete it->second;
+}
+
+template <class Classes>
+void ArchiveTestCase<Classes>::runTest()
+{
+ TestOutputStream out(m_options);
+
+ CreateTestData();
+
+ if (m_archiver.empty())
+ CreateArchive(out);
+ else
+ CreateArchive(out, m_archiver);
+
+ // check archive could be created
+ CPPUNIT_ASSERT(out.GetSize() > 0);
+
+ TestInputStream in(out);
+
+ TestIterator(in);
+ in.Rewind();
+ TestPairIterator(in);
+ in.Rewind();
+ TestSmartIterator(in);
+ in.Rewind();
+ TestSmartPairIterator(in);
+ in.Rewind();
+
+ if ((m_options & PipeIn) == 0) {
+ ReadSimultaneous(in);
+ in.Rewind();
+ }
+
+ ModifyArchive(in, out);
+ in.SetData(out);
+
+ if (m_unarchiver.empty())
+ ExtractArchive(in);
+ else
+ ExtractArchive(in, m_unarchiver);
+
+ // check that all the test entries were found in the archive
+ CPPUNIT_ASSERT(m_testEntries.empty());
+}
+
+template <class Classes>
+void ArchiveTestCase<Classes>::CreateTestData()
+{
+ Add("text/");
+ Add("text/empty", "");
+ Add("text/small", "Small text file for testing\n"
+ "archive streams in wxWidgets\n");
+
+ Add("bin/");
+ Add("bin/bin1000", 1000);
+ Add("bin/bin4095", 4095);
+ Add("bin/bin4096", 4096);
+ Add("bin/bin4097", 4097);
+ Add("bin/bin16384", 16384);
+
+ Add("zero/");
+ Add("zero/zero5", 5, 0);
+ Add("zero/zero1024", 1024, 109);
+ Add("zero/zero32768", 32768, 106);
+ Add("zero/zero16385", 16385, 119);
+
+ Add("empty/");
+}
+
+template <class Classes>
+TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
+ const char *data,
+ int len /*=-1*/)
+{
+ if (len == -1)
+ len = strlen(data);
+ TestEntry*& entry = m_testEntries[wxString(name, *wxConvCurrent)];
+ wxASSERT(entry == NULL);
+ entry = new TestEntry(m_timeStamp, len, data);
+ m_timeStamp += wxTimeSpan(0, 1, 30);
+ return *entry;
+}
+
+template <class Classes>
+TestEntry& ArchiveTestCase<Classes>::Add(const char *name,
+ int len /*=0*/,
+ int value /*=EOF*/)
+{
+ wxCharBuffer buf(len);
+ for (int i = 0; i < len; i++)
+ buf.data()[i] = value == EOF ? rand() : value;
+ return Add(name, buf, len);
+}
+
+// Create an archive using the wx archive classes, write it to 'out'
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out)
+{
+ std::auto_ptr<OutputStreamT> arc(m_factory->NewStream(out));
+ TestEntries::iterator it;
+
+ OnCreateArchive(*arc);
+
+ // We want to try creating entries in various different ways, 'choices'
+ // is just a number used to select between all the various possibilities.
+ int choices = m_id;
+
+ for (it = m_testEntries.begin(); it != m_testEntries.end(); ++it) {
+ choices += 5;
+ TestEntry& testEntry = *it->second;
+ wxString name = it->first;
+
+ // It should be possible to create a directory entry just by supplying
+ // a name that looks like a directory, or alternatively any old name
+ // can be identified as a directory using SetIsDir or PutNextDirEntry
+ bool setIsDir = name.Last() == _T('/') && (choices & 1);
+ if (setIsDir)
+ name.erase(name.length() - 1);
+
+ // provide some context for the error message so that we know which
+ // iteration of the loop we were on
+ std::string error_entry((_T(" '") + name + _T("'")).mb_str());
+ std::string error_context(" failed for entry" + error_entry);
+
+ if ((choices & 2) || testEntry.IsText()) {
+ // try PutNextEntry(EntryT *pEntry)
+ std::auto_ptr<EntryT> entry(m_factory->NewEntry());
+ entry->SetName(name, wxPATH_UNIX);
+ if (setIsDir)
+ entry->SetIsDir();
+ entry->SetDateTime(testEntry.GetDateTime());
+ entry->SetSize(testEntry.GetLength());
+ OnCreateEntry(*arc, testEntry, entry.get());
+ CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
+ arc->PutNextEntry(entry.release()));
+ }
+ else {
+ // try the convenience methods
+ OnCreateEntry(*arc, testEntry);
+ if (setIsDir)
+ CPPUNIT_ASSERT_MESSAGE("PutNextDirEntry" + error_context,
+ arc->PutNextDirEntry(name, testEntry.GetDateTime()));
+ else
+ CPPUNIT_ASSERT_MESSAGE("PutNextEntry" + error_context,
+ arc->PutNextEntry(name, testEntry.GetDateTime(),
+ testEntry.GetLength()));
+ }
+
+ if (name.Last() != _T('/')) {
+ // for non-dirs write the data
+ arc->Write(testEntry.GetData(), testEntry.GetSize());
+ CPPUNIT_ASSERT_MESSAGE("LastWrite check" + error_context,
+ arc->LastWrite() == testEntry.GetSize());
+ // should work with or without explicit CloseEntry
+ if (choices & 3)
+ CPPUNIT_ASSERT_MESSAGE("CloseEntry" + error_context,
+ arc->CloseEntry());
+ }
+
+ CPPUNIT_ASSERT_MESSAGE("IsOk" + error_context, arc->IsOk());
+ }
+
+ // should work with or without explicit Close
+ if (m_id % 2)
+ CPPUNIT_ASSERT(arc->Close());
+}
+
+// Create an archive using an external archive program
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::CreateArchive(wxOutputStream& out,
+ const wxString& archiver)
+{
+ // for an external archiver the test data need to be written to
+ // temp files
+ TempDir tmpdir;
+
+ // write the files
+ TestEntries::iterator i;
+ for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
+ wxFileName fn(i->first, wxPATH_UNIX);
+ TestEntry& entry = *i->second;
+
+ if (fn.IsDir()) {
+ fn.Mkdir(0777, wxPATH_MKDIR_FULL);
+ } else {
+ wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
+ wxFFileOutputStream fileout(fn.GetFullPath());
+ fileout.Write(entry.GetData(), entry.GetSize());
+ }
+ }
+
+ for (i = m_testEntries.begin(); i != m_testEntries.end(); ++i) {
+ wxFileName fn(i->first, wxPATH_UNIX);
+ TestEntry& entry = *i->second;
+ wxDateTime dt = entry.GetDateTime();
+#ifdef __WXMSW__
+ if (fn.IsDir())
+ entry.SetDateTime(wxDateTime());
+ else
+#endif
+ fn.SetTimes(NULL, &dt, NULL);
+ }
+
+ if ((m_options & PipeOut) == 0) {
+ wxFileName fn(tmpdir.GetName());
+ fn.SetExt(_T("arc"));
+ wxString tmparc = fn.GetFullPath();
+
+ // call the archiver to create an archive file
+ system(wxString::Format(archiver, tmparc.c_str()).mb_str());
+
+ // then load the archive file
+ {
+ wxFFileInputStream in(tmparc);
+ if (in.Ok())
+ out.Write(in);
+ }
+
+ wxRemoveFile(tmparc);
+ }
+ else {
+ // for the non-seekable test, have the archiver output to "-"
+ // and read the archive via a pipe
+ PFileInputStream in(wxString::Format(archiver, _T("-")));
+ if (in.Ok())
+ out.Write(in);
+ }
+}
+
+// Do a standard set of modification on an archive, delete an entry,
+// rename an entry and add an entry
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::ModifyArchive(wxInputStream& in,
+ wxOutputStream& out)
+{
+ std::auto_ptr<InputStreamT> arcIn(m_factory->NewStream(in));
+ std::auto_ptr<OutputStreamT> arcOut(m_factory->NewStream(out));
+ std::auto_ptr<EntryT> entry;
+
+ const wxString deleteName = _T("bin/bin1000");
+ const wxString renameFrom = _T("zero/zero1024");
+ const wxString renameTo = _T("zero/newname");
+ const wxString newName = _T("newfile");
+ const char *newData = "New file added as a test\n";
+
+ arcOut->CopyArchiveMetaData(*arcIn);
+
+ while (entry.reset(arcIn->GetNextEntry()), entry.get() != NULL) {
+ OnSetNotifier(*entry);
+ wxString name = entry->GetName(wxPATH_UNIX);
+
+ // provide some context for the error message so that we know which
+ // iteration of the loop we were on
+ std::string error_entry((_T(" '") + name + _T("'")).mb_str());
+ std::string error_context(" failed for entry" + error_entry);
+
+ if (name == deleteName) {
+ TestEntries::iterator it = m_testEntries.find(name);
+ CPPUNIT_ASSERT_MESSAGE(
+ "deletion failed (already deleted?) for" + error_entry,
+ it != m_testEntries.end());
+ TestEntry *p = it->second;
+ m_testEntries.erase(it);
+ delete p;
+ }
+ else {
+ if (name == renameFrom) {
+ entry->SetName(renameTo);
+ TestEntries::iterator it = m_testEntries.find(renameFrom);
+ CPPUNIT_ASSERT_MESSAGE(
+ "rename failed (already renamed?) for" + error_entry,
+ it != m_testEntries.end());
+ TestEntry *p = it->second;
+ m_testEntries.erase(it);
+ m_testEntries[renameTo] = p;
+ }
+
+ CPPUNIT_ASSERT_MESSAGE("CopyEntry" + error_context,
+ arcOut->CopyEntry(entry.release(), *arcIn));
+ }
+ }
+
+ // check that the deletion and rename were done
+ CPPUNIT_ASSERT(m_testEntries.count(deleteName) == 0);
+ CPPUNIT_ASSERT(m_testEntries.count(renameFrom) == 0);
+ CPPUNIT_ASSERT(m_testEntries.count(renameTo) == 1);
+
+ // check that the end of the input archive was reached without error
+ CPPUNIT_ASSERT(arcIn->Eof());
+
+ // try adding a new entry
+ TestEntry& testEntry = Add(newName.mb_str(), newData);
+ entry.reset(m_factory->NewEntry());
+ entry->SetName(newName);
+ entry->SetDateTime(testEntry.GetDateTime());
+ entry->SetSize(testEntry.GetLength());
+ OnCreateEntry(*arcOut, testEntry, entry.get());
+ OnSetNotifier(*entry);
+ CPPUNIT_ASSERT(arcOut->PutNextEntry(entry.release()));
+ CPPUNIT_ASSERT(arcOut->Write(newData, strlen(newData)).IsOk());
+
+ // should work with or without explicit Close
+ if (m_id % 2)
+ CPPUNIT_ASSERT(arcOut->Close());
+}
+
+// Extract an archive using the wx archive classes
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in)
+{
+ typedef Ptr<EntryT> EntryPtr;
+ typedef std::list<EntryPtr> Entries;
+ typedef typename Entries::iterator EntryIter;
+
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+ int expectedTotal = m_testEntries.size();
+ EntryPtr entry;
+ Entries entries;
+
+ if ((m_options & PipeIn) == 0)
+ OnArchiveExtracted(*arc, expectedTotal);
+
+ while (entry = EntryPtr(arc->GetNextEntry()), entry.get() != NULL) {
+ wxString name = entry->GetName(wxPATH_UNIX);
+
+ // provide some context for the error message so that we know which
+ // iteration of the loop we were on
+ std::string error_entry((_T(" '") + name + _T("'")).mb_str());
+ std::string error_context(" failed for entry" + error_entry);
+
+ TestEntries::iterator it = m_testEntries.find(name);
+ CPPUNIT_ASSERT_MESSAGE(
+ "archive contains an entry that shouldn't be there" + error_entry,
+ it != m_testEntries.end());
+
+ const TestEntry& testEntry = *it->second;
+
+ wxDateTime dt = testEntry.GetDateTime();
+ if (dt.IsValid())
+ CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
+ dt == entry->GetDateTime());
+
+ // non-seekable entries are allowed to have GetSize == wxInvalidOffset
+ // until the end of the entry's data has been read past
+ CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
+ testEntry.GetLength() == entry->GetSize() ||
+ ((m_options & PipeIn) != 0 && entry->GetSize() == wxInvalidOffset));
+ CPPUNIT_ASSERT_MESSAGE(
+ "arc->GetSize() == entry->GetSize()" + error_context,
+ arc->GetSize() == (size_t)entry->GetSize());
+
+ if (name.Last() != _T('/'))
+ {
+ CPPUNIT_ASSERT_MESSAGE("!IsDir" + error_context,
+ !entry->IsDir());
+ wxCharBuffer buf(testEntry.GetSize() + 1);
+ CPPUNIT_ASSERT_MESSAGE("Read until Eof" + error_context,
+ arc->Read(buf.data(), testEntry.GetSize() + 1).Eof());
+ CPPUNIT_ASSERT_MESSAGE("LastRead check" + error_context,
+ arc->LastRead() == testEntry.GetSize());
+ CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
+ !memcmp(buf.data(), testEntry.GetData(), testEntry.GetSize()));
+ } else {
+ CPPUNIT_ASSERT_MESSAGE("IsDir" + error_context, entry->IsDir());
+ }
+
+ // GetSize() must return the right result in all cases after all the
+ // data has been read
+ CPPUNIT_ASSERT_MESSAGE("entry size check" + error_context,
+ testEntry.GetLength() == entry->GetSize());
+ CPPUNIT_ASSERT_MESSAGE(
+ "arc->GetSize() == entry->GetSize()" + error_context,
+ arc->GetSize() == (size_t)entry->GetSize());
+
+ if ((m_options & PipeIn) == 0) {
+ OnEntryExtracted(*entry, testEntry, arc.get());
+ delete it->second;
+ m_testEntries.erase(it);
+ } else {
+ entries.push_back(entry);
+ }
+ }
+
+ // check that the end of the input archive was reached without error
+ CPPUNIT_ASSERT(arc->Eof());
+
+ // for non-seekable streams these data are only guaranteed to be
+ // available once the end of the archive has been reached
+ if (m_options & PipeIn) {
+ for (EntryIter i = entries.begin(); i != entries.end(); ++i) {
+ wxString name = (*i)->GetName(wxPATH_UNIX);
+ TestEntries::iterator j = m_testEntries.find(name);
+ OnEntryExtracted(**i, *j->second);
+ delete j->second;
+ m_testEntries.erase(j);
+ }
+ OnArchiveExtracted(*arc, expectedTotal);
+ }
+}
+
+// Extract an archive using an external unarchive program
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::ExtractArchive(wxInputStream& in,
+ const wxString& unarchiver)
+{
+ // for an external unarchiver, unarchive to a tempdir
+ TempDir tmpdir;
+
+ if ((m_options & PipeIn) == 0) {
+ wxFileName fn(tmpdir.GetName());
+ fn.SetExt(_T("arc"));
+ wxString tmparc = fn.GetFullPath();
+
+ if (m_options & Stub)
+ in.SeekI(TestOutputStream::STUB_SIZE * 2);
+
+ // write the archive to a temporary file
+ {
+ wxFFileOutputStream out(tmparc);
+ if (out.Ok())
+ out.Write(in);
+ }
+
+ // call unarchiver
+ system(wxString::Format(unarchiver, tmparc.c_str()).mb_str());
+ wxRemoveFile(tmparc);
+ }
+ else {
+ // for the non-seekable test, have the archiver extract "-" and
+ // feed it the archive via a pipe
+ PFileOutputStream out(wxString::Format(unarchiver, _T("-")));
+ if (out.Ok())
+ out.Write(in);
+ }
+
+ wxString dir = tmpdir.GetName();
+ VerifyDir(dir);
+}
+
+// Verifies the files produced by an external unarchiver are as expected
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::VerifyDir(wxString& path, size_t rootlen /*=0*/)
+{
+ wxDir dir;
+ path += wxFileName::GetPathSeparator();
+ int pos = path.length();
+ wxString name;
+
+ if (!rootlen)
+ rootlen = pos;
+
+ if (dir.Open(path) && dir.GetFirst(&name)) {
+ do {
+ path.replace(pos, wxString::npos, name);
+ name = m_factory->GetInternalName(
+ path.substr(rootlen, wxString::npos));
+
+ bool isDir = wxDirExists(path);
+ if (isDir)
+ name += _T("/");
+
+ // provide some context for the error message so that we know which
+ // iteration of the loop we were on
+ std::string error_entry((_T(" '") + name + _T("'")).mb_str());
+ std::string error_context(" failed for entry" + error_entry);
+
+ TestEntries::iterator it = m_testEntries.find(name);
+ CPPUNIT_ASSERT_MESSAGE(
+ "archive contains an entry that shouldn't be there"
+ + error_entry,
+ it != m_testEntries.end());
+
+ const TestEntry& testEntry = *it->second;
+ size_t size = 0;
+
+#ifndef __WXMSW__
+ CPPUNIT_ASSERT_MESSAGE("timestamp check" + error_context,
+ testEntry.GetDateTime() ==
+ wxFileName(path).GetModificationTime());
+#endif
+ if (!isDir) {
+ wxFFileInputStream in(path);
+ CPPUNIT_ASSERT_MESSAGE(
+ "entry not found in archive" + error_entry, in.Ok());
+
+ size = in.GetSize();
+ wxCharBuffer buf(size);
+ CPPUNIT_ASSERT_MESSAGE("Read" + error_context,
+ in.Read(buf.data(), size).LastRead() == size);
+ CPPUNIT_ASSERT_MESSAGE("size check" + error_context,
+ testEntry.GetSize() == size);
+ CPPUNIT_ASSERT_MESSAGE("data compare" + error_context,
+ memcmp(buf.data(), testEntry.GetData(), size) == 0);
+ }
+ else {
+ VerifyDir(path, rootlen);
+ }
+
+ delete it->second;
+ m_testEntries.erase(it);
+ }
+ while (dir.GetNext(&name));
+ }
+}
+
+// test the simple iterators that give away ownership of an entry
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::TestIterator(wxInputStream& in)
+{
+ typedef std::list<EntryT*> ArchiveCatalog;
+ typedef typename ArchiveCatalog::iterator CatalogIter;
+
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+ size_t count = 0;
+
+#ifdef WXARC_MEMBER_TEMPLATES
+ ArchiveCatalog cat((IterT)*arc, IterT());
+#else
+ ArchiveCatalog cat;
+ for (IterT i(*arc); i != IterT(); ++i)
+ cat.push_back(*i);
+#endif
+
+ for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
+ std::auto_ptr<EntryT> entry(*it);
+ count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
+ }
+
+ CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
+ CPPUNIT_ASSERT(count == cat.size());
+}
+
+// test the pair iterators that can be used to load a std::map or wxHashMap
+// these also give away ownership of entries
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::TestPairIterator(wxInputStream& in)
+{
+ typedef std::map<wxString, EntryT*> ArchiveCatalog;
+ typedef typename ArchiveCatalog::iterator CatalogIter;
+
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+ size_t count = 0;
+
+#ifdef WXARC_MEMBER_TEMPLATES
+ ArchiveCatalog cat((PairIterT)*arc, PairIterT());
+#else
+ ArchiveCatalog cat;
+ for (PairIterT i(*arc); i != PairIterT(); ++i)
+ cat.push_back(*i);
+#endif
+
+ for (CatalogIter it = cat.begin(); it != cat.end(); ++it) {
+ std::auto_ptr<EntryT> entry(it->second);
+ count += m_testEntries.count(entry->GetName(wxPATH_UNIX));
+ }
+
+ CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
+ CPPUNIT_ASSERT(count == cat.size());
+}
+
+// simple iterators using smart pointers, no need to worry about ownership
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::TestSmartIterator(wxInputStream& in)
+{
+ typedef std::list<Ptr<EntryT> > ArchiveCatalog;
+ typedef typename ArchiveCatalog::iterator CatalogIter;
+ typedef wxArchiveIterator<InputStreamT, Ptr<EntryT> > Iter;
+
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+
+#ifdef WXARC_MEMBER_TEMPLATES
+ ArchiveCatalog cat((Iter)*arc, Iter());
+#else
+ ArchiveCatalog cat;
+ for (Iter i(*arc); i != Iter(); ++i)
+ cat.push_back(*i);
+#endif
+
+ CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
+
+ for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
+ CPPUNIT_ASSERT(m_testEntries.count((*it)->GetName(wxPATH_UNIX)));
+}
+
+// pair iterator using smart pointers
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::TestSmartPairIterator(wxInputStream& in)
+{
+ typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
+ typedef typename ArchiveCatalog::iterator CatalogIter;
+ typedef wxArchiveIterator<InputStreamT,
+ std::pair<wxString, Ptr<EntryT> > > PairIter;
+
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+
+#ifdef WXARC_MEMBER_TEMPLATES
+ ArchiveCatalog cat((PairIter)*arc, PairIter());
+#else
+ ArchiveCatalog cat;
+ for (PairIter i(*arc); i != PairIter(); ++i)
+ cat.push_back(*i);
+#endif
+
+ CPPUNIT_ASSERT(m_testEntries.size() == cat.size());
+
+ for (CatalogIter it = cat.begin(); it != cat.end(); ++it)
+ CPPUNIT_ASSERT(m_testEntries.count(it->second->GetName(wxPATH_UNIX)));
+}
+
+// try reading two entries at the same time
+//
+template <class Classes>
+void ArchiveTestCase<Classes>::ReadSimultaneous(TestInputStream& in)
+{
+ typedef std::map<wxString, Ptr<EntryT> > ArchiveCatalog;
+ typedef wxArchiveIterator<InputStreamT,
+ std::pair<wxString, Ptr<EntryT> > > PairIter;
+
+ // create two archive input streams
+ TestInputStream in2(in);
+ std::auto_ptr<InputStreamT> arc(m_factory->NewStream(in));
+ std::auto_ptr<InputStreamT> arc2(m_factory->NewStream(in2));
+
+ // load the catalog
+#ifdef WXARC_MEMBER_TEMPLATES
+ ArchiveCatalog cat((PairIter)*arc, PairIter());
+#else
+ ArchiveCatalog cat;
+ for (PairIter i(*arc); i != PairIter(); ++i)
+ cat.push_back(*i);
+#endif
+
+ // the names of two entries to read
+ const wxChar *name = _T("text/small");
+ const wxChar *name2 = _T("bin/bin1000");
+
+ // open them
+ typename ArchiveCatalog::iterator j;
+ CPPUNIT_ASSERT((j = cat.find(name)) != cat.end());
+ CPPUNIT_ASSERT(arc->OpenEntry(*j->second));
+ CPPUNIT_ASSERT((j = cat.find(name2)) != cat.end());
+ CPPUNIT_ASSERT(arc2->OpenEntry(*j->second));
+
+ // get pointers to the expected data
+ TestEntries::iterator k;
+ CPPUNIT_ASSERT((k = m_testEntries.find(name)) != m_testEntries.end());
+ TestEntry *entry = k->second;
+ CPPUNIT_ASSERT((k = m_testEntries.find(name2)) != m_testEntries.end());
+ TestEntry *entry2 = k->second;
+
+ size_t count = 0, count2 = 0;
+ size_t size = entry->GetSize(), size2 = entry2->GetSize();
+ const char *data = entry->GetData(), *data2 = entry2->GetData();
+
+ // read and check the two entries in parallel, character by character
+ while (arc->IsOk() || arc2->IsOk()) {
+ char ch = arc->GetC();
+ if (arc->LastRead() == 1) {
+ CPPUNIT_ASSERT(count < size);
+ CPPUNIT_ASSERT(ch == data[count++]);
+ }
+ char ch2 = arc2->GetC();
+ if (arc2->LastRead() == 1) {
+ CPPUNIT_ASSERT(count2 < size2);
+ CPPUNIT_ASSERT(ch2 == data2[count2++]);
+ }
+ }
+
+ CPPUNIT_ASSERT(arc->Eof());
+ CPPUNIT_ASSERT(arc2->Eof());
+ CPPUNIT_ASSERT(count == size);
+ CPPUNIT_ASSERT(count2 == size2);
+}
+
+// Nothing useful can be done with a generic notifier yet, so just test one
+// can be set
+//
+template <class NotifierT, class EntryT>
+class ArchiveNotifier : public NotifierT
+{
+public:
+ void OnEntryUpdated(EntryT& WXUNUSED(entry)) { }
+};
+
+template <class Classes>
+void ArchiveTestCase<Classes>::OnSetNotifier(EntryT& entry)
+{
+ static ArchiveNotifier<NotifierT, EntryT> notifier;
+ entry.SetNotifier(notifier);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ArchiveTestCase<ZipClasses> could be used directly, but instead this
+// derived class is used so that zip specific features can be tested.
+
+class ZipTestCase : public ArchiveTestCase<ZipClasses>
+{
+public:
+ ZipTestCase(const wxString& name,
+ int id,
+ int options,
+ const wxString& archiver = wxEmptyString,
+ const wxString& unarchiver = wxEmptyString)
+ :
+ ArchiveTestCase<ZipClasses>(name, id, new wxZipClassFactory,
+ options, archiver, unarchiver),
+ m_count(0)
+ { }
+
+protected:
+ void OnCreateArchive(wxZipOutputStream& zip);
+
+ void OnArchiveExtracted(wxZipInputStream& zip, int expectedTotal);
+
+ void OnCreateEntry(wxZipOutputStream& zip,
+ TestEntry& testEntry,
+ wxZipEntry *entry);
+
+ void OnEntryExtracted(wxZipEntry& entry,
+ const TestEntry& testEntry,
+ wxZipInputStream *arc);
+
+ void OnSetNotifier(EntryT& entry);
+
+ int m_count;
+ wxString m_comment;
+};
+
+void ZipTestCase::OnCreateArchive(wxZipOutputStream& zip)
+{
+ m_comment << _T("Comment for test ") << m_id;
+ zip.SetComment(m_comment);
+}
+
+void ZipTestCase::OnArchiveExtracted(wxZipInputStream& zip, int expectedTotal)
+{
+ CPPUNIT_ASSERT(zip.GetComment() == m_comment);
+ CPPUNIT_ASSERT(zip.GetTotalEntries() == expectedTotal);
+}
+
+void ZipTestCase::OnCreateEntry(wxZipOutputStream& zip,
+ TestEntry& testEntry,
+ wxZipEntry *entry)
+{
+ zip.SetLevel((m_id + m_count) % 10);
+
+ if (entry) {
+ switch ((m_id + m_count) % 5) {
+ case 0:
+ {
+ wxString comment = _T("Comment for ") + entry->GetName();
+ entry->SetComment(comment);
+ // lowercase the expected result, and the notifier should do
+ // the same for the zip entries when ModifyArchive() runs
+ testEntry.SetComment(comment.Lower());
+ break;
+ }
+ case 2:
+ entry->SetMethod(wxZIP_METHOD_STORE);
+ break;
+ case 4:
+ entry->SetMethod(wxZIP_METHOD_DEFLATE);
+ break;
+ }
+ entry->SetIsText(testEntry.IsText());
+ }
+
+ m_count++;
+}
+
+void ZipTestCase::OnEntryExtracted(wxZipEntry& entry,
+ const TestEntry& testEntry,
+ wxZipInputStream *arc)
+{
+ // provide some context for the error message so that we know which
+ // iteration of the loop we were on
+ std::string error_entry((_T(" '") + entry.GetName() + _T("'")).mb_str());
+ std::string error_context(" failed for entry" + error_entry);
+
+ CPPUNIT_ASSERT_MESSAGE("GetComment" + error_context,
+ entry.GetComment() == testEntry.GetComment());
+
+ // for seekable streams, GetNextEntry() doesn't read the local header so
+ // call OpenEntry() to do it
+ if (arc && (m_options & PipeIn) == 0 && entry.IsDir())
+ arc->OpenEntry(entry);
+
+ CPPUNIT_ASSERT_MESSAGE("IsText" + error_context,
+ entry.IsText() == testEntry.IsText());
+
+ CPPUNIT_ASSERT_MESSAGE("Extra/LocalExtra mismatch for entry" + error_entry,
+ (entry.GetExtraLen() != 0 && entry.GetLocalExtraLen() != 0) ||
+ (entry.GetExtraLen() == 0 && entry.GetLocalExtraLen() == 0));
+}
+
+// check the notifier mechanism by using it to fold the entry comments to
+// lowercase
+//
+class ZipNotifier : public wxZipNotifier
+{
+public:
+ void OnEntryUpdated(wxZipEntry& entry);
+};
+
+void ZipNotifier::OnEntryUpdated(wxZipEntry& entry)
+{
+ entry.SetComment(entry.GetComment().Lower());
+}
+
+void ZipTestCase::OnSetNotifier(EntryT& entry)
+{
+ static ZipNotifier notifier;
+ entry.SetNotifier(notifier);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// 'zip - -' produces local headers without the size field set. This is a
+// case not covered by all the other tests, so this class tests it as a
+// special case
+
+class ZipPipeTestCase : public CppUnit::TestCase
+{
+public:
+ ZipPipeTestCase(const wxString& name, int options) :
+ CppUnit::TestCase(std::string(name.mb_str())), m_options(options) { }
+
+protected:
+ void runTest();
+ int m_options;
+};
+
+void ZipPipeTestCase::runTest()
+{
+ TestOutputStream out(m_options);
+
+ wxString testdata = _T("test data to pipe through zip");
+ wxString cmd = _T("echo ") + testdata + _T(" | zip -q - -");
+
+ {
+ PFileInputStream in(cmd);
+ if (in.Ok())
+ out.Write(in);
+ }
+
+ TestInputStream in(out);
+ wxZipInputStream zip(in);
+
+ std::auto_ptr<wxZipEntry> entry(zip.GetNextEntry());
+ CPPUNIT_ASSERT(entry.get() != NULL);
+
+ if ((m_options & PipeIn) == 0)
+ CPPUNIT_ASSERT(entry->GetSize() != wxInvalidOffset);
+
+ char buf[64];
+ size_t len = zip.Read(buf, sizeof(buf) - 1).LastRead();
+
+ while (len > 0 && buf[len - 1] <= 32)
+ --len;
+ buf[len] = 0;
+
+ CPPUNIT_ASSERT(zip.Eof());
+ CPPUNIT_ASSERT(wxString(buf, *wxConvCurrent) == testdata);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// The suite
+
+class ArchiveTestSuite : public CppUnit::TestSuite
+{
+public:
+ ArchiveTestSuite();
+ static CppUnit::Test *suite()
+ { return (new ArchiveTestSuite)->makeSuite(); }
+
+private:
+ int m_id;
+ wxPathList m_path;
+
+ ArchiveTestSuite *makeSuite();
+ void AddCmd(wxArrayString& cmdlist, const wxString& cmd);
+ bool IsInPath(const wxString& cmd);
+
+ wxString Description(const wxString& type,
+ int options,
+ bool genericInterface = false,
+ const wxString& archiver = wxEmptyString,
+ const wxString& unarchiver = wxEmptyString);
+};
+
+ArchiveTestSuite::ArchiveTestSuite()
+ : CppUnit::TestSuite("ArchiveTestSuite"),
+ m_id(0)
+{
+ m_path.AddEnvList(_T("PATH"));
+}
+
+// add the command for an external archiver to the list, testing for it in
+// the path first
+//
+void ArchiveTestSuite::AddCmd(wxArrayString& cmdlist, const wxString& cmd)
+{
+ if (cmdlist.empty())
+ cmdlist.push_back(_T(""));
+ if (IsInPath(cmd))
+ cmdlist.push_back(cmd);
+}
+
+bool ArchiveTestSuite::IsInPath(const wxString& cmd)
+{
+ wxString c = cmd.BeforeFirst(_T(' '));
+#ifdef __WXMSW__
+ c += _T(".exe");
+#endif
+ return !m_path.FindValidPath(c).empty();
+}
+
+// make the test suite
+//
+ArchiveTestSuite *ArchiveTestSuite::makeSuite()
+{
+ typedef wxArrayString::iterator Iter;
+ wxArrayString zippers;
+ wxArrayString unzippers;
+
+ AddCmd(zippers, _T("zip -qr %s *"));
+ AddCmd(unzippers, _T("unzip -q %s"));
+
+ for (int genInterface = 0; genInterface < 2; genInterface++)
+ for (Iter i = unzippers.begin(); i != unzippers.end(); ++i)
+ for (Iter j = zippers.begin(); j != zippers.end(); ++j)
+ for (int options = 0; options <= AllOptions; options++)
+ {
+ // unzip doesn't support piping in the zip
+ if ((options & PipeIn) && !i->empty())
+ continue;
+#ifdef WXARC_NO_POPEN
+ // if no popen then can use piped output of zip
+ if ((options & PipeOut) && !j->empty())
+ continue;
+#endif
+ wxString name = Description(_T("wxZip"), options,
+ genInterface != 0, *j, *i);
+
+ if (genInterface)
+ addTest(new ArchiveTestCase<ArchiveClasses>(
+ name, m_id,
+ new wxZipClassFactory,
+ options, *j, *i));
+ else
+ addTest(new ZipTestCase(name, m_id, options, *j, *i));
+
+ m_id++;
+ }
+
+#ifndef WXARC_NO_POPEN
+ // if have popen then can check the piped output of 'zip - -'
+ if (IsInPath(_T("zip")))
+ for (int options = 0; options <= PipeIn; options += PipeIn) {
+ wxString name = Description(_T("ZipPipeTestCase"), options);
+ addTest(new ZipPipeTestCase(name, options));
+ m_id++;
+ }
+#endif
+
+ return this;
+}
+
+// make a display string for the option bits
+//
+wxString ArchiveTestSuite::Description(const wxString& type,
+ int options,
+ bool genericInterface,
+ const wxString& archiver,
+ const wxString& unarchiver)
+{
+ wxString descr;
+ descr << m_id << _T(" ");
+
+ if (genericInterface)
+ descr << _T("wxArchive (") << type << _T(")");
+ else
+ descr << type;
+
+ if (!archiver.empty())
+ descr << _T(" ") << archiver.BeforeFirst(_T(' '));
+ if (!unarchiver.empty())
+ descr << _T(" ") << unarchiver.BeforeFirst(_T(' '));
+
+ wxString optstr;
+
+ if ((options & PipeIn) != 0)
+ optstr += _T("|PipeIn");
+ if ((options & PipeOut) != 0)
+ optstr += _T("|PipeOut");
+ if ((options & Stub) != 0)
+ optstr += _T("|Stub");
+ if (!optstr.empty())
+ optstr = _T(" (") + optstr.substr(1) + _T(")");
+
+ descr << optstr;
+
+ return descr;
+}
+
+// register in the unnamed registry so that these tests are run by default
+CPPUNIT_TEST_SUITE_REGISTRATION(ArchiveTestSuite);
+
+// also include in it's own registry so that these tests can be run alone
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ArchiveTestSuite, "ArchiveTestSuite");
+
+#endif // wxUSE_STREAMS
template_append="wx_append_base">
<sources>
test.cpp
+ archive/archivetest.cpp
arrays/arrays.cpp
datetime/datetimetest.cpp
fileconf/fileconftest.cpp