]> git.saurik.com Git - wxWidgets.git/commitdiff
Add tar streams.
authorMichael Wetherell <mike.wetherell@ntlworld.com>
Fri, 27 Oct 2006 09:28:54 +0000 (09:28 +0000)
committerMichael Wetherell <mike.wetherell@ntlworld.com>
Fri, 27 Oct 2006 09:28:54 +0000 (09:28 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42505 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/tarstrm.h [new file with mode: 0644]
src/common/tarstrm.cpp [new file with mode: 0644]

diff --git a/include/wx/tarstrm.h b/include/wx/tarstrm.h
new file mode 100644 (file)
index 0000000..8a5baf6
--- /dev/null
@@ -0,0 +1,349 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        tarstrm.h
+// Purpose:     Streams for Tar files
+// Author:      Mike Wetherell
+// RCS-ID:      $Id$
+// Copyright:   (c) 2004 Mike Wetherell
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_WXTARSTREAM_H__
+#define _WX_WXTARSTREAM_H__
+
+#include "wx/defs.h"
+
+#if wxUSE_TARSTREAM
+
+#include "wx/archive.h"
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Constants
+
+// TypeFlag values
+enum {
+    wxTAR_REGTYPE   = '0',      // regular file
+    wxTAR_LNKTYPE   = '1',      // hard link
+    wxTAR_SYMTYPE   = '2',      // symbolic link
+    wxTAR_CHRTYPE   = '3',      // character special
+    wxTAR_BLKTYPE   = '4',      // block special
+    wxTAR_DIRTYPE   = '5',      // directory
+    wxTAR_FIFOTYPE  = '6',      // named pipe
+    wxTAR_CONTTYPE  = '7'       // contiguous file
+};
+
+// Archive Formats (use wxTAR_PAX, it's backward compatible)
+enum wxTarFormat
+{
+    wxTAR_USTAR,                // POSIX.1-1990 tar format
+    wxTAR_PAX                   // POSIX.1-2001 tar format
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxTarNotifier
+
+class wxTarNotifier
+{
+public:
+    virtual ~wxTarNotifier() { }
+
+    virtual void OnEntryUpdated(class wxTarEntry& entry) = 0;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Tar Entry - hold the meta data for a file in the tar
+
+class wxTarEntry : public wxArchiveEntry
+{
+public:
+    wxTarEntry(const wxString& name = wxEmptyString,
+               const wxDateTime& dt = wxDateTime::Now(),
+               wxFileOffset size = wxInvalidOffset);
+    virtual ~wxTarEntry();
+
+    wxTarEntry(const wxTarEntry& entry);
+    wxTarEntry& operator=(const wxTarEntry& entry);
+
+    // Get accessors
+    wxString     GetName(wxPathFormat format = wxPATH_NATIVE) const;
+    wxString     GetInternalName() const        { return m_Name; }
+    wxPathFormat GetInternalFormat() const      { return wxPATH_UNIX; }
+    int          GetMode() const;
+    int          GetUserId() const              { return m_UserId; }
+    int          GetGroupId() const             { return m_GroupId; }
+    wxFileOffset GetSize() const                { return m_Size; }
+    wxFileOffset GetOffset() const              { return m_Offset; }
+    wxDateTime   GetDateTime() const            { return m_ModifyTime; }
+    wxDateTime   GetAccessTime() const          { return m_AccessTime; }
+    wxDateTime   GetCreateTime() const          { return m_CreateTime; }
+    int          GetTypeFlag() const            { return m_TypeFlag; }
+    wxString     GetLinkName() const            { return m_LinkName; }
+    wxString     GetUserName() const            { return m_UserName; }
+    wxString     GetGroupName() const           { return m_GroupName; }
+    int          GetDevMajor() const            { return m_DevMajor; }
+    int          GetDevMinor() const            { return m_DevMinor; }
+
+    // is accessors
+    bool IsDir() const;
+    bool IsReadOnly() const                     { return !(m_Mode & 0222); }
+
+    // set accessors
+    void SetName(const wxString& name, wxPathFormat format = wxPATH_NATIVE);
+    void SetUserId(int id)                      { m_UserId = id; }
+    void SetGroupId(int id)                     { m_GroupId = id; }
+    void SetMode(int mode);
+    void SetSize(wxFileOffset size)             { m_Size = size; }
+    void SetDateTime(const wxDateTime& dt)      { m_ModifyTime = dt; }
+    void SetAccessTime(const wxDateTime& dt)    { m_AccessTime = dt; }
+    void SetCreateTime(const wxDateTime& dt)    { m_CreateTime = dt; }
+    void SetTypeFlag(int type)                  { m_TypeFlag = type; }
+    void SetLinkName(const wxString& link)      { m_LinkName = link; }
+    void SetUserName(const wxString& user)      { m_UserName = user; }
+    void SetGroupName(const wxString& group)    { m_GroupName = group; }
+    void SetDevMajor(int dev)                   { m_DevMajor = dev; }
+    void SetDevMinor(int dev)                   { m_DevMinor = dev; }
+
+    // set is accessors
+    void SetIsDir(bool isDir = true);
+    void SetIsReadOnly(bool isReadOnly = true);
+
+    static wxString GetInternalName(const wxString& name,
+                                    wxPathFormat format = wxPATH_NATIVE,
+                                    bool *pIsDir = NULL);
+
+    wxTarEntry *Clone() const { return new wxTarEntry(*this); }
+
+    void SetNotifier(wxTarNotifier& WXUNUSED(notifier)) { }
+
+private:
+    void SetOffset(wxFileOffset offset)         { m_Offset = offset; }
+
+    virtual wxArchiveEntry* DoClone() const     { return Clone(); }
+
+    wxString     m_Name;
+    int          m_Mode;
+    bool         m_IsModeSet;
+    int          m_UserId;
+    int          m_GroupId;
+    wxFileOffset m_Size;
+    wxFileOffset m_Offset;
+    wxDateTime   m_ModifyTime;
+    wxDateTime   m_AccessTime;
+    wxDateTime   m_CreateTime;
+    wxChar       m_TypeFlag;
+    wxString     m_LinkName;
+    wxString     m_UserName;
+    wxString     m_GroupName;
+    int          m_DevMajor;
+    int          m_DevMinor;
+
+    friend class wxTarInputStream;
+
+    DECLARE_DYNAMIC_CLASS(wxTarEntry)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxTarInputStream
+
+class wxTarInputStream : public wxArchiveInputStream
+{
+public:
+    typedef wxTarEntry entry_type;
+
+    wxTarInputStream(wxInputStream& stream, wxMBConv& conv = wxConvLocal);
+    wxTarInputStream(wxInputStream *stream, wxMBConv& conv = wxConvLocal);
+    virtual ~wxTarInputStream();
+
+    bool OpenEntry(wxTarEntry& entry);
+    bool CloseEntry();
+
+    wxTarEntry *GetNextEntry();
+
+    wxFileOffset GetLength() const      { return m_size; }
+    bool IsSeekable() const { return m_parent_i_stream->IsSeekable(); }
+
+protected:
+    size_t OnSysRead(void *buffer, size_t size);
+    wxFileOffset OnSysTell() const      { return m_pos; }
+    wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode);
+
+private:
+    void Init();
+
+    wxArchiveEntry *DoGetNextEntry()    { return GetNextEntry(); }
+    bool OpenEntry(wxArchiveEntry& entry);
+    bool IsOpened() const               { return m_pos != wxInvalidOffset; }
+
+    wxStreamError ReadHeaders();
+    bool ReadExtendedHeader(class wxTarHeaderRecords*& recs);
+
+    wxString GetExtendedHeader(const wxString& key) const;
+    wxString GetHeaderPath() const;
+    wxFileOffset GetHeaderNumber(int id) const;
+    wxString GetHeaderString(int id) const;
+    wxDateTime GetHeaderDate(const wxString& key) const;
+
+    wxFileOffset m_pos;     // position within the current entry
+    wxFileOffset m_offset;  // offset to the start of the entry's data
+    wxFileOffset m_size;    // size of the current entry's data
+
+    int m_sumType;
+    int m_tarType;
+    class wxTarHeaderBlock *m_hdr;
+    class wxTarHeaderRecords *m_HeaderRecs;
+    class wxTarHeaderRecords *m_GlobalHeaderRecs;
+
+    DECLARE_NO_COPY_CLASS(wxTarInputStream)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxTarOutputStream
+
+class wxTarOutputStream : public wxArchiveOutputStream
+{
+public:
+    wxTarOutputStream(wxOutputStream& stream,
+                      wxTarFormat format = wxTAR_PAX,
+                      wxMBConv& conv = wxConvLocal);
+    wxTarOutputStream(wxOutputStream *stream,
+                      wxTarFormat format = wxTAR_PAX,
+                      wxMBConv& conv = wxConvLocal);
+    virtual ~wxTarOutputStream();
+
+    bool PutNextEntry(wxTarEntry *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(wxTarEntry *entry, wxTarInputStream& inputStream);
+    bool CopyArchiveMetaData(wxTarInputStream& WXUNUSED(s)) { return true; }
+
+    void Sync();
+    bool CloseEntry();
+    bool Close();
+
+    bool IsSeekable() const { return m_parent_o_stream->IsSeekable(); }
+
+    void SetBlockingFactor(int factor)  { m_BlockingFactor = factor; }
+    int GetBlockingFactor() const       { return m_BlockingFactor; }
+
+protected:
+    size_t OnSysWrite(const void *buffer, size_t size);
+    wxFileOffset OnSysTell() const      { return m_pos; }
+    wxFileOffset OnSysSeek(wxFileOffset pos, wxSeekMode mode);
+
+private:
+    void Init(wxTarFormat format);
+
+    bool PutNextEntry(wxArchiveEntry *entry);
+    bool CopyEntry(wxArchiveEntry *entry, wxArchiveInputStream& stream);
+    bool CopyArchiveMetaData(wxArchiveInputStream& WXUNUSED(s)) { return true; }
+    bool IsOpened() const               { return m_pos != wxInvalidOffset; }
+
+    bool WriteHeaders(wxTarEntry& entry);
+    bool ModifyHeader();
+    wxString PaxHeaderPath(const wxString& format, const wxString& path);
+
+    void SetExtendedHeader(const wxString& key, const wxString& value);
+    void SetHeaderPath(const wxString& name);
+    bool SetHeaderNumber(int id, wxFileOffset n);
+    void SetHeaderString(int id, const wxString& str);
+    void SetHeaderDate(const wxString& key, const wxDateTime& datetime);
+
+    wxFileOffset m_pos;     // position within the current entry
+    wxFileOffset m_maxpos;  // max pos written
+    wxFileOffset m_size;    // expected entry size
+
+    wxFileOffset m_headpos; // offset within the file to the entry's header
+    wxFileOffset m_datapos; // offset within the file to the entry's data
+
+    wxFileOffset m_tarstart;// offset within the file to the tar
+    wxFileOffset m_tarsize; // size of tar so far
+
+    bool m_pax;
+    int m_BlockingFactor;
+    wxUint32 m_chksum;
+    bool m_large;
+    class wxTarHeaderBlock *m_hdr;
+    class wxTarHeaderBlock *m_hdr2;
+    char *m_extendedHdr;
+    size_t m_extendedSize;
+    wxString m_badfit;
+
+    DECLARE_NO_COPY_CLASS(wxTarOutputStream)
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Iterators
+
+#if wxUSE_STL || defined WX_TEST_ARCHIVE_ITERATOR
+typedef wxArchiveIterator<wxTarInputStream> wxTarIter;
+typedef wxArchiveIterator<wxTarInputStream,
+         std::pair<wxString, wxTarEntry*> > wxTarPairIter;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// wxTarClassFactory
+
+class wxTarClassFactory : public wxArchiveClassFactory
+{
+public:
+    typedef wxTarEntry        entry_type;
+    typedef wxTarInputStream  instream_type;
+    typedef wxTarOutputStream outstream_type;
+    typedef wxTarNotifier     notifier_type;
+#if wxUSE_STL || defined WX_TEST_ARCHIVE_ITERATOR
+    typedef wxTarIter         iter_type;
+    typedef wxTarPairIter     pairiter_type;
+#endif
+
+    wxTarClassFactory();
+
+    wxTarEntry *NewEntry() const
+        { return new wxTarEntry; }
+    wxTarInputStream *NewStream(wxInputStream& stream) const
+        { return new wxTarInputStream(stream, GetConv()); }
+    wxTarOutputStream *NewStream(wxOutputStream& stream) const
+        { return new wxTarOutputStream(stream, wxTAR_PAX, GetConv()); }
+    wxTarInputStream *NewStream(wxInputStream *stream) const
+        { return new wxTarInputStream(stream, GetConv()); }
+    wxTarOutputStream *NewStream(wxOutputStream *stream) const
+        { return new wxTarOutputStream(stream, wxTAR_PAX, GetConv()); }
+
+    wxString GetInternalName(const wxString& name,
+                             wxPathFormat format = wxPATH_NATIVE) const
+        { return wxTarEntry::GetInternalName(name, format); }
+
+    const wxChar * const *GetProtocols(wxStreamProtocolType type
+                                       = wxSTREAM_PROTOCOL) const;
+
+protected:
+    wxArchiveEntry *DoNewEntry() const
+        { return NewEntry(); }
+    wxArchiveInputStream *DoNewStream(wxInputStream& stream) const
+        { return NewStream(stream); }
+    wxArchiveOutputStream *DoNewStream(wxOutputStream& stream) const
+        { return NewStream(stream); }
+    wxArchiveInputStream *DoNewStream(wxInputStream *stream) const
+        { return NewStream(stream); }
+    wxArchiveOutputStream *DoNewStream(wxOutputStream *stream) const
+        { return NewStream(stream); }
+
+private:
+    DECLARE_DYNAMIC_CLASS(wxTarClassFactory)
+};
+
+
+#endif // wxUSE_TARSTREAM
+
+#endif // _WX_WXTARSTREAM_H__
diff --git a/src/common/tarstrm.cpp b/src/common/tarstrm.cpp
new file mode 100644 (file)
index 0000000..f3a1cec
--- /dev/null
@@ -0,0 +1,1457 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        tarstrm.cpp
+// Purpose:     Streams for Tar files
+// Author:      Mike Wetherell
+// RCS-ID:      $Id$
+// Copyright:   (c) 2004 Mike Wetherell
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+  #pragma hdrstop
+#endif
+
+#if wxUSE_TARSTREAM
+
+#include "wx/tarstrm.h"
+
+#ifndef WX_PRECOMP
+    #include "wx/hashmap.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/utils.h"
+#endif
+
+#include "wx/buffer.h"
+#include "wx/datetime.h"
+#include "wx/ptr_scpd.h"
+#include "wx/filename.h"
+
+#include <ctype.h>
+
+#ifdef __UNIX__
+#include <grp.h>
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// constants
+
+enum {
+    TAR_NAME,
+    TAR_MODE,
+    TAR_UID,
+    TAR_GID,
+    TAR_SIZE,
+    TAR_MTIME,
+    TAR_CHKSUM,
+    TAR_TYPEFLAG,
+    TAR_LINKNAME,
+    TAR_MAGIC,
+    TAR_VERSION,
+    TAR_UNAME,
+    TAR_GNAME,
+    TAR_DEVMAJOR,
+    TAR_DEVMINOR,
+    TAR_PREFIX,
+    TAR_UNUSED,
+    TAR_NUMFIELDS
+};
+
+enum {
+    TAR_BLOCKSIZE = 512,
+};
+
+// checksum type
+enum {
+    SUM_UNKNOWN,
+    SUM_UNSIGNED,
+    SUM_SIGNED
+};
+
+// type of input tar
+enum {
+    TYPE_OLDTAR,    // fields after TAR_LINKNAME are invalid
+    TYPE_GNUTAR,    // all fields except TAR_PREFIX are valid
+    TYPE_USTAR      // all fields are valid
+};
+
+// signatures
+static const char *USTAR_MAGIC   = "ustar";
+static const char *USTAR_VERSION = "00";
+static const char *GNU_MAGIC     = "ustar ";
+static const char *GNU_VERION    = " ";
+
+IMPLEMENT_DYNAMIC_CLASS(wxTarEntry, wxArchiveEntry)
+IMPLEMENT_DYNAMIC_CLASS(wxTarClassFactory, wxArchiveClassFactory)
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Class factory
+
+static wxTarClassFactory g_wxTarClassFactory;
+
+wxTarClassFactory::wxTarClassFactory()
+{
+    if (this == &g_wxTarClassFactory)
+        PushFront();
+}
+
+const wxChar * const *
+wxTarClassFactory::GetProtocols(wxStreamProtocolType type) const
+{
+    static const wxChar *protocols[] = { _T("tar"), NULL };
+    static const wxChar *mimetypes[] = { _T("application/x-tar"), NULL };
+    static const wxChar *fileexts[]  = { _T(".tar"), NULL };
+    static const wxChar *empty[]     = { NULL };
+
+    switch (type) {
+        case wxSTREAM_PROTOCOL:         return protocols;
+        case wxSTREAM_MIMETYPE:         return mimetypes;
+        case wxSTREAM_FILEEXTENSION:    return fileexts;
+        default:                        return empty;
+    }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// tar header block
+
+typedef wxFileOffset wxTarNumber;
+
+struct wxTarField { const wxChar *name; int pos; };
+
+class wxTarHeaderBlock
+{
+public:
+    wxTarHeaderBlock()
+        { memset(data, 0, sizeof(data)); }
+    wxTarHeaderBlock(const wxTarHeaderBlock& hb)
+        { memcpy(data, hb.data, sizeof(data)); }
+
+    bool Read(wxInputStream& in);
+    bool Write(wxOutputStream& out);
+    inline bool WriteField(wxOutputStream& out, int id);
+
+    bool IsAllZeros() const;
+    wxUint32 Sum(bool SignedSum = false);
+    wxUint32 SumField(int id);
+
+    char *Get(int id) { return data + fields[id].pos + id; }
+    static size_t Len(int id) { return fields[id + 1].pos - fields[id].pos; }
+    static const wxChar *Name(int id) { return fields[id].name; }
+    static size_t Offset(int id) { return fields[id].pos; }
+
+    bool SetOctal(int id, wxTarNumber n);
+    wxTarNumber GetOctal(int id);
+    bool SetPath(const wxString& name, wxMBConv& conv);
+
+private:
+    char data[TAR_BLOCKSIZE + TAR_NUMFIELDS];
+    static const wxTarField fields[];
+    static void check();
+};
+
+wxDEFINE_SCOPED_PTR_TYPE(wxTarHeaderBlock);
+
+// A table giving the field names and offsets in a tar header block
+const wxTarField wxTarHeaderBlock::fields[] =
+{
+    { _T("name"), 0 },       // 100
+    { _T("mode"), 100 },     // 8
+    { _T("uid"), 108 },      // 8
+    { _T("gid"), 116 },      // 8
+    { _T("size"), 124 },     // 12
+    { _T("mtime"), 136 },    // 12
+    { _T("chksum"), 148 },   // 8
+    { _T("typeflag"), 156 }, // 1
+    { _T("linkname"), 157 }, // 100
+    { _T("magic"), 257 },    // 6
+    { _T("version"), 263 },  // 2
+    { _T("uname"), 265 },    // 32
+    { _T("gname"), 297 },    // 32
+    { _T("devmajor"), 329 }, // 8
+    { _T("devminor"), 337 }, // 8
+    { _T("prefix"), 345 },   // 155
+    { _T("unused"), 500 },   // 12
+    { NULL, TAR_BLOCKSIZE }
+};
+
+void wxTarHeaderBlock::check()
+{
+    wxCOMPILE_TIME_ASSERT(
+        WXSIZEOF(fields) == TAR_NUMFIELDS + 1,
+        Wrong_number_of_elements_in_fields_table
+    );
+}
+
+bool wxTarHeaderBlock::IsAllZeros() const
+{
+    const char *p = data;
+    for (size_t i = 0; i < sizeof(data); i++)
+        if (p[i])
+            return false;
+    return true;
+}
+
+wxUint32 wxTarHeaderBlock::Sum(bool SignedSum /*=false*/)
+{
+    // the chksum field itself should be blanks during the calculation
+    memset(Get(TAR_CHKSUM), ' ', Len(TAR_CHKSUM));
+    const char *p = data;
+    wxUint32 n = 0;
+
+    if (SignedSum)
+        for (size_t i = 0; i < sizeof(data); i++)
+            n += (signed char)p[i];
+    else
+        for (size_t i = 0; i < sizeof(data); i++)
+            n += (unsigned char)p[i];
+
+    return n;
+}
+
+wxUint32 wxTarHeaderBlock::SumField(int id)
+{
+    unsigned char *p = (unsigned char*)Get(id);
+    unsigned char *q = p + Len(id);
+    wxUint32 n = 0;
+
+    while (p < q)
+        n += *p++;
+
+    return n;
+}
+
+bool wxTarHeaderBlock::Read(wxInputStream& in)
+{   
+    bool ok = true;
+
+    for (int id = 0; id < TAR_NUMFIELDS && ok; id++)
+        ok = in.Read(Get(id), Len(id)).LastRead() == Len(id);
+
+    return ok;
+}
+
+bool wxTarHeaderBlock::Write(wxOutputStream& out)
+{
+    bool ok = true;
+
+    for (int id = 0; id < TAR_NUMFIELDS && ok; id++)
+        ok = WriteField(out, id);
+
+    return ok;
+}
+
+bool wxTarHeaderBlock::WriteField(wxOutputStream& out, int id)
+{
+    return out.Write(Get(id), Len(id)).LastWrite() == Len(id);
+}
+
+wxTarNumber wxTarHeaderBlock::GetOctal(int id)
+{
+    wxTarNumber n = 0;
+    const char *p = Get(id);
+    while (*p == ' ')
+        p++;
+    while (*p >= '0' && *p < '8')
+        n = (n << 3) | (*p++ - '0');
+    return n;
+}
+
+bool wxTarHeaderBlock::SetOctal(int id, wxTarNumber n)
+{
+    // set an octal field, return true if the number fits
+    char *field = Get(id); 
+    char *p = field + Len(id);
+    *--p = 0;
+    while (p > field) {
+        *--p = '0' + (n & 7);
+        n >>= 3;
+    }
+    return n == 0;
+}
+
+bool wxTarHeaderBlock::SetPath(const wxString& name, wxMBConv& conv)
+{
+    bool badconv = false;
+
+#if wxUSE_UNICODE
+    wxCharBuffer nameBuf = name.mb_str(conv);
+
+    // if the conversion fails make an approximation
+    if (!nameBuf) {
+        badconv = true;
+        size_t len = name.length();
+        wxCharBuffer approx(len);
+        for (size_t i = 0; i < len; i++)
+            approx.data()[i] = name[i] & ~0x7F ? '_' : name[i];
+        nameBuf = approx;
+    }
+
+    const char *mbName = nameBuf;
+#else
+    const char *mbName = name.c_str();
+    (void)conv;
+#endif
+
+    bool fits;
+    bool notGoingToFit = false;
+    size_t len = strlen(mbName);
+    size_t maxname = Len(TAR_NAME);
+    size_t maxprefix = Len(TAR_PREFIX);
+    size_t i = 0;
+    size_t nexti = 0;
+
+    for (;;) {
+        fits = i < maxprefix && len - i <= maxname;
+        
+        if (!fits) {
+            const char *p = strchr(mbName + i, '/');
+            if (p)
+                nexti = p - mbName + 1;
+            if (!p || nexti - 1 > maxprefix)
+                notGoingToFit = true;
+        }
+
+        if (fits || notGoingToFit) {
+            strncpy(Get(TAR_NAME), mbName + i, maxname);
+            if (i > 0)
+                strncpy(Get(TAR_PREFIX), mbName, i - 1);
+            break;
+        }
+
+        i = nexti;
+    }
+
+    return fits && !badconv;
+}
+
+    
+/////////////////////////////////////////////////////////////////////////////
+// Some helpers
+
+static wxFileOffset RoundUpSize(wxFileOffset size, int factor = 1)
+{
+    wxFileOffset chunk = TAR_BLOCKSIZE * factor;
+    return ((size + chunk - 1) / chunk) * chunk;
+}
+
+static wxString GroupName()
+{
+#ifdef __UNIX__
+    group *gr;
+    if ((gr = getgrgid(getgid())) != NULL)
+        return wxString(gr->gr_name, wxConvLibc);
+#endif
+    return _("unknown");
+}
+
+static inline int UserId()
+{
+#ifdef __UNIX__
+    return getuid();
+#else
+    return 0;
+#endif
+}
+
+static inline int GroupId()
+{
+#ifdef __UNIX__
+    return getgid();
+#else
+    return 0;
+#endif
+}
+
+// ignore the size field for entry types 3, 4, 5 and 6
+//
+static inline wxFileOffset GetDataSize(const wxTarEntry& entry)
+{
+    switch (entry.GetTypeFlag()) {
+        case wxTAR_CHRTYPE:
+        case wxTAR_BLKTYPE:
+        case wxTAR_DIRTYPE:
+        case wxTAR_FIFOTYPE:
+            return 0;
+        default:
+            return entry.GetSize();
+    };
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Tar Entry
+// Holds all the meta-data for a file in the tar
+
+wxTarEntry::wxTarEntry(const wxString& name /*=wxEmptyString*/,
+                       const wxDateTime& dt /*=wxDateTime::Now()*/,
+                       wxFileOffset size    /*=0*/)
+  : m_Mode(0644),
+    m_IsModeSet(false),
+    m_UserId(UserId()),
+    m_GroupId(GroupId()),
+    m_Size(size),
+    m_Offset(wxInvalidOffset),
+    m_ModifyTime(dt),
+    m_TypeFlag(wxTAR_REGTYPE),
+    m_UserName(wxGetUserId()),
+    m_GroupName(GroupName()),
+    m_DevMajor(~0),
+    m_DevMinor(~0)
+{
+    if (!name.empty())
+        SetName(name);
+}
+
+wxTarEntry::~wxTarEntry()
+{
+}
+
+wxTarEntry::wxTarEntry(const wxTarEntry& e)
+  : m_Name(e.m_Name),
+    m_Mode(e.m_Mode),
+    m_IsModeSet(e.m_IsModeSet),
+    m_UserId(e.m_UserId),
+    m_GroupId(e.m_GroupId),
+    m_Size(e.m_Size),
+    m_Offset(e.m_Offset),
+    m_ModifyTime(e.m_ModifyTime),
+    m_AccessTime(e.m_AccessTime),
+    m_CreateTime(e.m_CreateTime),
+    m_TypeFlag(e.m_TypeFlag),
+    m_LinkName(e.m_LinkName),
+    m_UserName(e.m_UserName),
+    m_GroupName(e.m_GroupName),
+    m_DevMajor(e.m_DevMajor),
+    m_DevMinor(e.m_DevMinor)
+{
+}
+
+wxTarEntry& wxTarEntry::operator=(const wxTarEntry& e)
+{
+    if (&e != this) {
+        m_Name = e.m_Name;
+        m_Mode = e.m_Mode;
+        m_IsModeSet = e.m_IsModeSet;
+        m_UserId = e.m_UserId;
+        m_GroupId = e.m_GroupId;
+        m_Size = e.m_Size;
+        m_Offset = e.m_Offset;
+        m_ModifyTime = e.m_ModifyTime;
+        m_AccessTime = e.m_AccessTime;
+        m_CreateTime = e.m_CreateTime;
+        m_TypeFlag = e.m_TypeFlag;
+        m_LinkName = e.m_LinkName;
+        m_UserName = e.m_UserName;
+        m_GroupName = e.m_GroupName;
+        m_DevMajor = e.m_DevMajor;
+        m_DevMinor = e.m_DevMinor;
+    }
+    return *this;
+}
+
+wxString wxTarEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
+{
+    bool isDir = IsDir() && !m_Name.empty();
+
+    // optimisations for common (and easy) cases
+    switch (wxFileName::GetFormat(format)) {
+        case wxPATH_DOS:
+        {
+            wxString name(isDir ? m_Name + _T("\\") : m_Name);
+            for (size_t i = 0; i < name.length(); 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);
+}
+
+void wxTarEntry::SetName(const wxString& name, wxPathFormat format)
+{
+    bool isDir;
+    m_Name = GetInternalName(name, format, &isDir);
+    SetIsDir(isDir);
+}
+
+// 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 wxTarEntry::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;
+}
+
+bool wxTarEntry::IsDir() const
+{
+    return m_TypeFlag - wxTAR_DIRTYPE == 0;
+}
+
+void wxTarEntry::SetIsDir(bool isDir)
+{
+    if (isDir)
+        m_TypeFlag = wxTAR_DIRTYPE;
+    else if (m_TypeFlag - wxTAR_DIRTYPE == 0)
+        m_TypeFlag = wxTAR_REGTYPE;
+}
+
+void wxTarEntry::SetIsReadOnly(bool isReadOnly)
+{
+    if (isReadOnly)
+        m_Mode &= ~0222;
+    else
+        m_Mode |= 0200;
+}
+
+int wxTarEntry::GetMode() const
+{
+    if (m_IsModeSet || !IsDir())
+        return m_Mode;
+    else
+        return m_Mode | 0111;
+    
+}
+
+void wxTarEntry::SetMode(int mode)
+{
+    m_Mode = mode & 07777;
+    m_IsModeSet = true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Input stream
+
+wxDECLARE_SCOPED_PTR(wxTarEntry, wxTarEntryPtr_)
+wxDEFINE_SCOPED_PTR (wxTarEntry, wxTarEntryPtr_)
+
+WX_DECLARE_STRING_HASH_MAP(wxString, wxTarHeaderRecords);
+
+wxTarInputStream::wxTarInputStream(wxInputStream& stream,
+                                   wxMBConv& conv /*=wxConvLocal*/)
+ :  wxArchiveInputStream(stream, conv)
+{
+    Init();
+}
+
+wxTarInputStream::wxTarInputStream(wxInputStream *stream,
+                                   wxMBConv& conv /*=wxConvLocal*/)
+ :  wxArchiveInputStream(stream, conv)
+{
+    Init();
+}
+
+void wxTarInputStream::Init()
+{
+    m_pos = wxInvalidOffset;
+    m_offset = 0;
+    m_size = wxInvalidOffset;
+    m_sumType = SUM_UNKNOWN;
+    m_tarType = TYPE_USTAR;
+    m_hdr = new wxTarHeaderBlock;
+    m_HeaderRecs = NULL;
+    m_GlobalHeaderRecs = NULL;
+    m_lasterror = m_parent_i_stream->GetLastError();
+}
+
+wxTarInputStream::~wxTarInputStream()
+{
+    delete m_hdr;
+    delete m_HeaderRecs;
+    delete m_GlobalHeaderRecs;
+}
+
+wxTarEntry *wxTarInputStream::GetNextEntry()
+{
+    m_lasterror = ReadHeaders();
+
+    if (!IsOk())
+        return NULL;
+
+    wxTarEntryPtr_ entry(new wxTarEntry);
+
+    entry->SetMode(GetHeaderNumber(TAR_MODE));
+    entry->SetUserId(GetHeaderNumber(TAR_UID));
+    entry->SetGroupId(GetHeaderNumber(TAR_UID));
+    entry->SetSize(GetHeaderNumber(TAR_SIZE));
+
+    entry->SetOffset(m_offset);
+
+    entry->SetDateTime(GetHeaderDate(_T("mtime")));
+    entry->SetAccessTime(GetHeaderDate(_T("atime")));
+    entry->SetCreateTime(GetHeaderDate(_T("ctime")));
+
+    entry->SetTypeFlag(*m_hdr->Get(TAR_TYPEFLAG));
+    bool isDir = entry->IsDir();
+
+    entry->SetLinkName(GetHeaderString(TAR_LINKNAME));
+
+    if (m_tarType != TYPE_OLDTAR) {
+        entry->SetUserName(GetHeaderString(TAR_UNAME));
+        entry->SetGroupName(GetHeaderString(TAR_GNAME));
+
+        entry->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR));
+        entry->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR));
+    }
+
+    entry->SetName(GetHeaderPath(), wxPATH_UNIX);
+    if (isDir)
+        entry->SetIsDir();
+
+    if (m_HeaderRecs)
+        m_HeaderRecs->clear();
+
+    m_size = GetDataSize(*entry);
+    m_pos = 0;
+
+    return entry.release();
+}
+
+bool wxTarInputStream::OpenEntry(wxTarEntry& entry)
+{
+    wxFileOffset offset = entry.GetOffset();
+
+    if (GetLastError() != wxSTREAM_READ_ERROR
+            && m_parent_i_stream->IsSeekable()
+            && m_parent_i_stream->SeekI(offset) == offset)
+    {
+        m_offset = offset;
+        m_size = GetDataSize(entry);
+        m_pos = 0;
+        m_lasterror = wxSTREAM_NO_ERROR;
+        return true;
+    } else {
+        m_lasterror = wxSTREAM_READ_ERROR;
+        return false;
+    }
+}
+
+bool wxTarInputStream::OpenEntry(wxArchiveEntry& entry)
+{
+    wxTarEntry *tarEntry = wxStaticCast(&entry, wxTarEntry);
+    return tarEntry ? OpenEntry(*tarEntry) : false;
+}
+
+bool wxTarInputStream::CloseEntry()
+{
+    if (m_lasterror == wxSTREAM_READ_ERROR)
+        return false;
+    if (!IsOpened())
+        return true;
+
+    wxFileOffset size = RoundUpSize(m_size);
+    wxFileOffset remainder = size - m_pos;
+
+    if (remainder && m_parent_i_stream->IsSeekable()) {
+        wxLogNull nolog;
+        if (m_parent_i_stream->SeekI(remainder, wxFromCurrent)
+                != wxInvalidOffset)
+            remainder = 0;
+    }
+
+    if (remainder) {
+        const int BUFSIZE = 8192;
+        wxCharBuffer buf(BUFSIZE);
+
+        while (remainder > 0 && m_parent_i_stream->IsOk())
+            remainder -= m_parent_i_stream->Read(
+                    buf.data(), wxMin(BUFSIZE, remainder)).LastRead();
+    }
+
+    m_pos = wxInvalidOffset;
+    m_offset += size;
+    m_lasterror = m_parent_i_stream->GetLastError();
+
+    return IsOk();
+}
+
+wxStreamError wxTarInputStream::ReadHeaders()
+{
+    if (!CloseEntry())
+        return wxSTREAM_READ_ERROR;
+
+    bool done = false;
+
+    while (!done) {
+        m_hdr->Read(*m_parent_i_stream);
+        if (m_parent_i_stream->Eof())
+            wxLogError(_("incomplete header block in tar"));
+        if (!*m_parent_i_stream)
+            return wxSTREAM_READ_ERROR;
+        m_offset += TAR_BLOCKSIZE;
+
+        // an all-zero header marks the end of the tar
+        if (m_hdr->IsAllZeros())
+            return wxSTREAM_EOF;
+
+        // the checksum is supposed to be the unsigned sum of the header bytes,
+        // but there have been versions of tar that used the signed sum, so
+        // accept that too, but only if used throughout.
+        wxUint32 chksum = m_hdr->GetOctal(TAR_CHKSUM);
+        bool ok = false;
+
+        if (m_sumType != SUM_SIGNED) {
+            ok = chksum == m_hdr->Sum();
+            if (m_sumType == SUM_UNKNOWN)
+                m_sumType = ok ? SUM_UNSIGNED : SUM_SIGNED;
+        }
+        if (m_sumType == SUM_SIGNED)
+            ok = chksum == m_hdr->Sum(true);
+        if (!ok) {
+            wxLogError(_("checksum failure reading tar header block"));
+            return wxSTREAM_READ_ERROR;
+        }
+
+        if (strcmp(m_hdr->Get(TAR_MAGIC), USTAR_MAGIC) == 0)
+            m_tarType = TYPE_USTAR;
+        else if (strcmp(m_hdr->Get(TAR_MAGIC), GNU_MAGIC) == 0 &&
+                 strcmp(m_hdr->Get(TAR_VERSION), GNU_VERION) == 0)
+            m_tarType = TYPE_GNUTAR;
+        else
+            m_tarType = TYPE_OLDTAR;
+
+        if (m_tarType != TYPE_USTAR)
+            break;
+
+        switch (*m_hdr->Get(TAR_TYPEFLAG)) {
+            case 'g': ReadExtendedHeader(m_GlobalHeaderRecs); break;
+            case 'x': ReadExtendedHeader(m_HeaderRecs); break;
+            default:  done = true;
+        }
+    }
+
+    return wxSTREAM_NO_ERROR;
+}
+
+wxString wxTarInputStream::GetExtendedHeader(const wxString& key) const
+{
+    wxTarHeaderRecords::iterator it;
+
+    // look at normal extended header records first
+    if (m_HeaderRecs) {
+        it = m_HeaderRecs->find(key);
+        if (it != m_HeaderRecs->end())
+            return wxString(it->second.wc_str(wxConvUTF8), GetConv());
+    }
+
+    // if not found, look at the global header records
+    if (m_GlobalHeaderRecs) {
+        it = m_GlobalHeaderRecs->find(key);
+        if (it != m_GlobalHeaderRecs->end())
+            return wxString(it->second.wc_str(wxConvUTF8), GetConv());
+    }
+
+    return wxEmptyString;
+}
+
+wxString wxTarInputStream::GetHeaderPath() const
+{
+    wxString path;
+
+    if ((path = GetExtendedHeader(_T("path"))) != wxEmptyString)
+        return path;
+
+    path = wxString(m_hdr->Get(TAR_NAME), GetConv());
+    if (m_tarType != TYPE_USTAR)
+        return path;
+
+    const char *prefix = m_hdr->Get(TAR_PREFIX);
+    return *prefix ? wxString(prefix, GetConv()) + _T("/") + path : path;
+}
+
+wxDateTime wxTarInputStream::GetHeaderDate(const wxString& key) const
+{
+    wxString value;
+
+    // try extended header, stored as decimal seconds since the epoch
+    if ((value = GetExtendedHeader(key)) != wxEmptyString) {
+        wxLongLong ll;
+        ll.Assign(wxAtof(value) * 1000.0);
+        return ll;
+    }
+
+    if (key == _T("mtime"))
+        return wxLongLong(m_hdr->GetOctal(TAR_MTIME)) * 1000L;
+
+    return wxDateTime();
+}
+
+wxTarNumber wxTarInputStream::GetHeaderNumber(int id) const
+{
+    wxString value;
+
+    if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString) {
+        wxTarNumber n = 0;
+        const wxChar *p = value;
+        while (*p == ' ')
+            p++;
+        while (isdigit(*p))
+            n = n * 10 + (*p++ - '0');
+        return n;
+    } else {
+        return m_hdr->GetOctal(id);
+    }
+}
+
+wxString wxTarInputStream::GetHeaderString(int id) const
+{
+    wxString value;
+
+    if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString)
+        return value;
+
+    return wxString(m_hdr->Get(id), GetConv());
+}
+
+// An extended header consists of one or more records, each constructed:
+// "%d %s=%s\n", <length>, <keyword>, <value>
+// <length> is the byte length, <keyword> and <value> are UTF-8
+
+bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords*& recs)
+{
+    if (!recs)
+        recs = new wxTarHeaderRecords;
+
+    // round length up to a whole number of blocks
+    size_t len = m_hdr->GetOctal(TAR_SIZE);
+    size_t size = RoundUpSize(len);
+
+    // read in the whole header since it should be small
+    wxCharBuffer buf(size);
+    size_t lastread = m_parent_i_stream->Read(buf.data(), size).LastRead();
+    if (lastread < len)
+        len = lastread;
+    buf.data()[len] = 0;
+    m_offset += lastread;
+
+    size_t recPos, recSize;
+    bool ok = true;
+
+    for (recPos = 0; recPos < len; recPos += recSize) {
+        char *pRec = buf.data() + recPos;
+        char *p = pRec;
+
+        // read the record size (byte count in ascii decimal)
+        recSize = 0;
+        while (isdigit(*p))
+            recSize = recSize * 10 + *p++ - '0';
+
+        // validity checks
+        if (recPos + recSize > len)
+            break;
+        if (recSize < p - pRec + (size_t)3 || *p != ' '
+                || pRec[recSize - 1] != '\012') {
+            ok = false;
+            continue;
+        }
+
+        // replace the final '\n' with a nul, to terminate value
+        pRec[recSize - 1] = 0;
+        // the key is here, following the space
+        char *pKey = ++p;
+
+        // look forward for the '=', the value follows
+        while (*p && *p != '=')
+            p++;
+        if (!*p) {
+            ok = false;
+            continue;
+        }
+        // replace the '=' with a nul, to terminate the key
+        *p++ = 0;
+
+        wxString key(wxConvUTF8.cMB2WC(pKey), GetConv());
+        wxString value(wxConvUTF8.cMB2WC(p), GetConv());
+
+        // an empty value unsets a previously given value
+        if (value.empty())
+            recs->erase(key);
+        else
+            (*recs)[key] = value; 
+    }
+
+    if (!ok || recPos < len || size != lastread) {
+        wxLogWarning(_("invalid data in extended tar header"));
+        return false;
+    }
+
+    return true;
+}
+
+wxFileOffset wxTarInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
+{
+    if (!IsOpened()) {
+        wxLogError(_("tar entry not open"));
+        m_lasterror = wxSTREAM_READ_ERROR;
+    }
+    if (!IsOk())
+        return wxInvalidOffset;
+
+    switch (mode) {
+        case wxFromStart:   break;
+        case wxFromCurrent: pos += m_pos; break;
+        case wxFromEnd:     pos += m_size; break;
+    }
+
+    if (pos < 0 || m_parent_i_stream->SeekI(m_offset + pos) == wxInvalidOffset)
+        return wxInvalidOffset;
+
+    m_pos = pos;
+    return m_pos;
+}
+
+size_t wxTarInputStream::OnSysRead(void *buffer, size_t size)
+{
+    if (!IsOpened()) {
+        wxLogError(_("tar entry not open"));
+        m_lasterror = wxSTREAM_READ_ERROR;
+    }
+    if (!IsOk() || !size)
+        return 0;
+
+    if (m_pos >= m_size)
+        size = 0;
+    else if (m_pos + size > m_size + (size_t)0)
+        size = m_size - m_pos;
+
+    size_t lastread = m_parent_i_stream->Read(buffer, size).LastRead();
+    m_pos += lastread;
+    
+    if (m_pos >= m_size) {
+        m_lasterror = wxSTREAM_EOF;
+    } else if (!m_parent_i_stream->IsOk()) {
+        // any other error will have been reported by the underlying stream
+        if (m_parent_i_stream->Eof())
+            wxLogError(_("unexpected end of file"));
+        m_lasterror = wxSTREAM_READ_ERROR;
+    }
+
+    return lastread;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Output stream
+
+wxTarOutputStream::wxTarOutputStream(wxOutputStream& stream,
+                                     wxTarFormat format /*=wxTAR_PAX*/,
+                                     wxMBConv& conv     /*=wxConvLocal*/)
+ :  wxArchiveOutputStream(stream, conv)
+{
+    Init(format);
+}
+
+wxTarOutputStream::wxTarOutputStream(wxOutputStream *stream,
+                                     wxTarFormat format /*=wxTAR_PAX*/,
+                                     wxMBConv& conv     /*=wxConvLocal*/)
+ :  wxArchiveOutputStream(stream, conv)
+{
+    Init(format);
+}
+
+void wxTarOutputStream::Init(wxTarFormat format)
+{
+    m_pos = wxInvalidOffset;
+    m_maxpos = wxInvalidOffset;
+    m_size = wxInvalidOffset;
+    m_headpos = wxInvalidOffset;
+    m_datapos = wxInvalidOffset;
+    m_tarstart = wxInvalidOffset;
+    m_tarsize = 0;
+    m_pax = format == wxTAR_PAX;
+    m_BlockingFactor = m_pax ? 10 : 20;
+    m_chksum = 0;
+    m_large = false;
+    m_hdr = new wxTarHeaderBlock;
+    m_hdr2 = NULL;
+    m_extendedHdr = NULL;
+    m_extendedSize = 0;
+    m_lasterror = m_parent_o_stream->GetLastError();
+}
+
+wxTarOutputStream::~wxTarOutputStream()
+{
+    if (m_tarsize)
+        Close();
+    delete m_hdr;
+    delete m_hdr2;
+    delete [] m_extendedHdr;
+}
+
+bool wxTarOutputStream::PutNextEntry(wxTarEntry *entry)
+{
+    wxTarEntryPtr_ e(entry);
+
+    if (!CloseEntry())
+        return false;
+
+    if (!m_tarsize) {
+        wxLogNull nolog;
+        m_tarstart = m_parent_o_stream->TellO();
+    }
+
+    if (m_tarstart != wxInvalidOffset)
+        m_headpos = m_tarstart + m_tarsize;
+
+    if (WriteHeaders(*e)) {
+        m_pos = 0;
+        m_maxpos = 0;
+        m_size = GetDataSize(*e);
+        if (m_tarstart != wxInvalidOffset)
+            m_datapos = m_tarstart + m_tarsize;
+
+        // types that are not allowd any data
+        const char nodata[] = {
+            wxTAR_LNKTYPE, wxTAR_SYMTYPE, wxTAR_CHRTYPE, wxTAR_BLKTYPE,
+            wxTAR_DIRTYPE, wxTAR_FIFOTYPE, 0
+        };
+        char typeflag = e->GetTypeFlag();
+
+        // pax does now allow data for wxTAR_LNKTYPE
+        if (!m_pax || typeflag - wxTAR_LNKTYPE != 0)
+            if (strchr(nodata, typeflag) != NULL)
+                CloseEntry();
+    }
+
+    return IsOk();
+}
+
+bool wxTarOutputStream::PutNextEntry(const wxString& name,
+                                     const wxDateTime& dt,
+                                     wxFileOffset size)
+{
+    return PutNextEntry(new wxTarEntry(name, dt, size));
+}
+
+bool wxTarOutputStream::PutNextDirEntry(const wxString& name,
+                                        const wxDateTime& dt)
+{
+    wxTarEntry *entry = new wxTarEntry(name, dt);
+    entry->SetIsDir();
+    return PutNextEntry(entry);
+}
+
+bool wxTarOutputStream::PutNextEntry(wxArchiveEntry *entry)
+{
+    wxTarEntry *tarEntry = wxStaticCast(entry, wxTarEntry);
+    if (!tarEntry)
+        delete entry;
+    return PutNextEntry(tarEntry);
+}
+
+bool wxTarOutputStream::CopyEntry(wxTarEntry *entry,
+                                  wxTarInputStream& inputStream)
+{
+    if (PutNextEntry(entry))
+        Write(inputStream);
+    return IsOk() && inputStream.Eof();
+}
+
+bool wxTarOutputStream::CopyEntry(wxArchiveEntry *entry,
+                                  wxArchiveInputStream& inputStream)
+{
+    if (PutNextEntry(entry))
+        Write(inputStream);
+    return IsOk() && inputStream.Eof();
+}
+
+bool wxTarOutputStream::CloseEntry()
+{
+    if (!IsOpened())
+        return true;
+
+    if (m_pos < m_maxpos) {
+        wxASSERT(m_parent_o_stream->IsSeekable());
+        m_parent_o_stream->SeekO(m_datapos + m_maxpos);
+        m_lasterror = m_parent_o_stream->GetLastError();
+        m_pos = m_maxpos;
+    }
+
+    if (IsOk()) {
+        wxFileOffset size = RoundUpSize(m_pos);
+        if (size > m_pos) {
+            memset(m_hdr, 0, size - m_pos);
+            m_parent_o_stream->Write(m_hdr, size - m_pos);
+            m_lasterror = m_parent_o_stream->GetLastError();
+        }
+        m_tarsize += size;
+    }
+
+    if (IsOk() && m_pos != m_size)
+        ModifyHeader();
+
+    m_pos = wxInvalidOffset;
+    m_maxpos = wxInvalidOffset;
+    m_size = wxInvalidOffset;
+    m_headpos = wxInvalidOffset;
+    m_datapos = wxInvalidOffset;
+
+    return IsOk();
+}
+
+bool wxTarOutputStream::Close()
+{
+    if (!CloseEntry())
+        return false;
+
+    memset(m_hdr, 0, sizeof(*m_hdr));
+    int count = (RoundUpSize(m_tarsize + 2 * TAR_BLOCKSIZE, m_BlockingFactor)
+                    - m_tarsize) / TAR_BLOCKSIZE;
+    while (count--)
+        m_parent_o_stream->Write(m_hdr, TAR_BLOCKSIZE);
+
+    m_tarsize = 0;
+    m_tarstart = wxInvalidOffset;
+    m_lasterror = m_parent_o_stream->GetLastError();
+    return IsOk();
+}
+
+bool wxTarOutputStream::WriteHeaders(wxTarEntry& entry)
+{
+    memset(m_hdr, 0, sizeof(*m_hdr));
+
+    SetHeaderPath(entry.GetName(wxPATH_UNIX));
+
+    SetHeaderNumber(TAR_MODE, entry.GetMode());
+    SetHeaderNumber(TAR_UID, entry.GetUserId());
+    SetHeaderNumber(TAR_GID, entry.GetGroupId());
+
+    if (entry.GetSize() == wxInvalidOffset)
+        entry.SetSize(0);
+    m_large = SetHeaderNumber(TAR_SIZE, entry.GetSize());
+
+    SetHeaderDate(_T("mtime"), entry.GetDateTime());
+    if (entry.GetAccessTime().IsValid())
+        SetHeaderDate(_T("atime"), entry.GetAccessTime());
+    if (entry.GetCreateTime().IsValid())
+        SetHeaderDate(_T("ctime"), entry.GetCreateTime());
+
+    *m_hdr->Get(TAR_TYPEFLAG) = entry.GetTypeFlag();
+
+    strcpy(m_hdr->Get(TAR_MAGIC), USTAR_MAGIC);
+    strcpy(m_hdr->Get(TAR_VERSION), USTAR_VERSION); 
+
+    SetHeaderString(TAR_LINKNAME, entry.GetLinkName());
+    SetHeaderString(TAR_UNAME, entry.GetUserName());
+    SetHeaderString(TAR_GNAME, entry.GetGroupName());
+
+    if (~entry.GetDevMajor())
+        SetHeaderNumber(TAR_DEVMAJOR, entry.GetDevMajor());
+    if (~entry.GetDevMinor())
+        SetHeaderNumber(TAR_DEVMINOR, entry.GetDevMinor()); 
+
+    m_chksum = m_hdr->Sum();
+    m_hdr->SetOctal(TAR_CHKSUM, m_chksum);
+    if (!m_large)
+        m_chksum -= m_hdr->SumField(TAR_SIZE);
+
+    // The main header is now fully prepared so we know what extended headers
+    // (if any) will be needed. Output any extended headers before writing
+    // the main header.
+    if (m_extendedHdr && *m_extendedHdr) {
+        wxASSERT(m_pax);
+        // the extended headers are written to the tar as a file entry,
+        // so prepare a regular header block for the pseudo-file.
+        if (!m_hdr2)
+            m_hdr2 = new wxTarHeaderBlock;
+        memset(m_hdr2, 0, sizeof(*m_hdr2));
+
+        // an old tar that doesn't understand extended headers will
+        // extract it as a file, so give these fields reasonable values
+        // so that the user will have access to read and remove it.
+        m_hdr2->SetPath(PaxHeaderPath(_T("%d/PaxHeaders.%p/%f"),
+                                      entry.GetName(wxPATH_UNIX)), GetConv());
+        m_hdr2->SetOctal(TAR_MODE, 0600);
+        strcpy(m_hdr2->Get(TAR_UID), m_hdr->Get(TAR_UID));
+        strcpy(m_hdr2->Get(TAR_GID), m_hdr->Get(TAR_GID));
+        size_t length = strlen(m_extendedHdr);
+        m_hdr2->SetOctal(TAR_SIZE, length);
+        strcpy(m_hdr2->Get(TAR_MTIME), m_hdr->Get(TAR_MTIME));
+        *m_hdr2->Get(TAR_TYPEFLAG) = 'x';
+        strcpy(m_hdr2->Get(TAR_MAGIC), USTAR_MAGIC);
+        strcpy(m_hdr2->Get(TAR_VERSION), USTAR_VERSION); 
+        strcpy(m_hdr2->Get(TAR_UNAME), m_hdr->Get(TAR_UNAME));
+        strcpy(m_hdr2->Get(TAR_GNAME), m_hdr->Get(TAR_GNAME));
+
+        m_hdr2->SetOctal(TAR_CHKSUM, m_hdr2->Sum());
+
+        m_hdr2->Write(*m_parent_o_stream);
+        m_tarsize += TAR_BLOCKSIZE;
+
+        size_t rounded = RoundUpSize(length);
+        memset(m_extendedHdr + length, 0, rounded - length);
+        m_parent_o_stream->Write(m_extendedHdr, rounded);
+        m_tarsize += rounded;
+
+        *m_extendedHdr = 0;
+    }
+
+    // if don't have extended headers just report error
+    if (!m_badfit.empty()) {
+        wxASSERT(!m_pax);
+        wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
+                     m_badfit.c_str(), entry.GetName().c_str());
+        m_badfit.clear();
+    }
+                         
+    m_hdr->Write(*m_parent_o_stream);
+    m_tarsize += TAR_BLOCKSIZE;
+    m_lasterror = m_parent_o_stream->GetLastError();
+
+    return IsOk();
+}
+
+wxString wxTarOutputStream::PaxHeaderPath(const wxString& format,
+                                          const wxString& path)
+{
+    wxString d = path.BeforeLast(_T('/'));
+    wxString f = path.AfterLast(_T('/'));
+    wxString ret;
+    
+    if (d.empty())
+        d = _T(".");
+
+    ret.reserve(format.length() + path.length() + 16);
+
+    size_t begin = 0;
+    size_t end;
+
+    for (;;) {
+        end = format.find('%', begin);
+        if (end == wxString::npos || end + 1 >= format.length())
+            break;
+        ret << format.substr(begin, end - begin);
+        switch (format[end + 1]) {
+            case 'd': ret << d; break;
+            case 'f': ret << f; break;
+            case 'p': ret << wxGetProcessId(); break;
+            case '%': ret << _T("%"); break;
+        }
+        begin = end + 2;
+    }
+
+    ret << format.substr(begin);
+
+    return ret;
+}
+
+bool wxTarOutputStream::ModifyHeader()
+{
+    wxFileOffset originalPos = wxInvalidOffset;
+    wxFileOffset sizePos = wxInvalidOffset;
+
+    if (!m_large && m_headpos != wxInvalidOffset
+            && m_parent_o_stream->IsSeekable())
+    {
+        wxLogNull nolog;
+        originalPos = m_parent_o_stream->TellO();
+        if (originalPos != wxInvalidOffset)
+            sizePos =
+                m_parent_o_stream->SeekO(m_headpos + m_hdr->Offset(TAR_SIZE));
+    }
+
+    if (sizePos == wxInvalidOffset || !m_hdr->SetOctal(TAR_SIZE, m_pos)) {
+        wxLogError(_("incorrect size given for tar entry"));
+        m_lasterror = wxSTREAM_WRITE_ERROR;
+        return false;
+    }
+
+    m_chksum += m_hdr->SumField(TAR_SIZE);
+    m_hdr->SetOctal(TAR_CHKSUM, m_chksum);
+    wxFileOffset sumPos = m_headpos + m_hdr->Offset(TAR_CHKSUM);
+
+    return
+        m_hdr->WriteField(*m_parent_o_stream, TAR_SIZE) &&
+        m_parent_o_stream->SeekO(sumPos) == sumPos &&
+        m_hdr->WriteField(*m_parent_o_stream, TAR_CHKSUM) &&
+        m_parent_o_stream->SeekO(originalPos) == originalPos;
+}
+
+void wxTarOutputStream::SetHeaderPath(const wxString& name)
+{
+    if (!m_hdr->SetPath(name, GetConv()) || (m_pax && !name.IsAscii()))
+        SetExtendedHeader(_T("path"), name);
+}
+
+bool wxTarOutputStream::SetHeaderNumber(int id, wxTarNumber n)
+{
+    if (m_hdr->SetOctal(id, n)) {
+        return true;
+    } else {
+        SetExtendedHeader(m_hdr->Name(id), wxLongLong(n).ToString());
+        return false;
+    }
+}
+
+void wxTarOutputStream::SetHeaderString(int id, const wxString& str)
+{
+    strncpy(m_hdr->Get(id), str.mb_str(GetConv()), m_hdr->Len(id));
+    if (str.length() > m_hdr->Len(id))
+        SetExtendedHeader(m_hdr->Name(id), str);
+}
+
+void wxTarOutputStream::SetHeaderDate(const wxString& key,
+                                      const wxDateTime& datetime)
+{
+    wxLongLong ll = datetime.IsValid() ? datetime.GetValue() : wxLongLong(0);
+    wxLongLong secs = ll / 1000L;
+
+    if (key != _T("mtime") || !m_hdr->SetOctal(TAR_MTIME, secs.GetValue())
+        || secs <= 0 || secs >= 0x7fffffff)
+    {
+        wxString str;
+        if (ll >= LONG_MIN && ll <= LONG_MAX) {
+            str.Printf(_T("%g"), ll.ToLong() / 1000.0);
+        } else {
+            str = ll.ToString();
+            str.insert(str.end() - 3, '.');
+        }
+        SetExtendedHeader(key, str);
+    }
+}
+
+void wxTarOutputStream::SetExtendedHeader(const wxString& key,
+                                          const wxString& value)
+{
+    if (m_pax) {
+        const wxWX2WCbuf wide_key = key.wc_str(GetConv());
+        const wxCharBuffer utf_key = wxConvUTF8.cWC2MB(wide_key);
+
+        const wxWX2WCbuf wide_value = value.wc_str(GetConv());
+        const wxCharBuffer utf_value = wxConvUTF8.cWC2MB(wide_value);
+
+        // a small buffer to format the length field in
+        char buf[32];
+        // length of "99<space><key>=<value>\n"
+        unsigned long length = strlen(utf_value) + strlen(utf_key) + 5;
+        sprintf(buf, "%lu", length);
+        // the length includes itself
+        size_t lenlen = strlen(buf);
+        if (lenlen != 2) {
+            length += lenlen - 2;
+            sprintf(buf, "%lu", length);
+            if (strlen(buf) > lenlen)
+                sprintf(buf, "%lu", ++length);
+        }
+
+        // reallocate m_extendedHdr if it's not big enough
+        if (m_extendedSize < length) {
+            size_t rounded = RoundUpSize(length);
+            m_extendedSize <<= 1;
+            if (rounded > m_extendedSize)
+                m_extendedSize = rounded;
+            char *oldHdr = m_extendedHdr;
+            m_extendedHdr = new char[m_extendedSize];
+            if (oldHdr) {
+                strcpy(m_extendedHdr, oldHdr);
+                delete oldHdr;
+            } else {
+                *m_extendedHdr = 0;
+            }
+        }
+
+        // append the new record
+        char *append = strchr(m_extendedHdr, 0);
+        sprintf(append, "%s %s=%s\012", buf,
+                (const char*)utf_key, (const char*)utf_value);
+    }
+    else {
+        // if not pax then make a list of fields to report as errors
+        if (!m_badfit.empty())
+            m_badfit += _T(", ");
+        m_badfit += key;
+    }
+}
+
+void wxTarOutputStream::Sync()
+{
+    m_parent_o_stream->Sync();
+}
+
+wxFileOffset wxTarOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
+{
+    if (!IsOpened()) {
+        wxLogError(_("tar entry not open"));
+        m_lasterror = wxSTREAM_WRITE_ERROR;
+    }
+    if (!IsOk() || m_datapos == wxInvalidOffset)
+        return wxInvalidOffset;
+
+    switch (mode) {
+        case wxFromStart:   break;
+        case wxFromCurrent: pos += m_pos; break;
+        case wxFromEnd:     pos += m_maxpos; break;
+    }
+
+    if (pos < 0 || m_parent_o_stream->SeekO(m_datapos + pos) == wxInvalidOffset)
+        return wxInvalidOffset;
+
+    m_pos = pos;
+    return m_pos;
+}
+
+size_t wxTarOutputStream::OnSysWrite(const void *buffer, size_t size)
+{
+    if (!IsOpened()) {
+        wxLogError(_("tar entry not open"));
+        m_lasterror = wxSTREAM_WRITE_ERROR;
+    }
+    if (!IsOk() || !size)
+        return 0;
+
+    size_t lastwrite = m_parent_o_stream->Write(buffer, size).LastWrite();
+    m_pos += lastwrite;
+    if (m_pos > m_maxpos)
+        m_maxpos = m_pos;
+
+    if (lastwrite != size)
+        m_lasterror = wxSTREAM_WRITE_ERROR;
+
+    return lastwrite;
+}
+
+#endif // wxUSE_TARSTREAM