Arithmetic operations on the elements of different enums are deprecated in C++20 and the latest compiler versions warn about them. While individual warnings could be fixed in wxWidgets itself (although it would be quite an effort to do it for all ~500 of them), it wouldn't help with the same warnings in the applications using wx, so prefer to do it by explicitly defining the operations on the enums that can be combined with each other by using wxALLOW_COMBINING_ENUMS() macro, except for a single warning in wxTar code where it's easier to just not use an anum at all.
1539 lines
40 KiB
C++
1539 lines
40 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/tarstrm.cpp
|
|
// Purpose: Streams for Tar files
|
|
// Author: Mike Wetherell
|
|
// Copyright: (c) 2004 Mike Wetherell
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
|
|
#if wxUSE_TARSTREAM
|
|
|
|
#include "wx/tarstrm.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/utils.h"
|
|
#endif
|
|
|
|
#include "wx/buffer.h"
|
|
#include "wx/datetime.h"
|
|
#include "wx/scopedptr.h"
|
|
#include "wx/filename.h"
|
|
#include "wx/thread.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifdef __UNIX__
|
|
#include <pwd.h>
|
|
#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
|
|
};
|
|
|
|
static const int 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 = " ";
|
|
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxTarEntry, wxArchiveEntry);
|
|
wxIMPLEMENT_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[] = { wxT("tar"), NULL };
|
|
static const wxChar *mimetypes[] = { wxT("application/x-tar"), NULL };
|
|
static const wxChar *fileexts[] = { wxT(".tar"), NULL };
|
|
static const wxChar *empty[] = { NULL };
|
|
|
|
switch (type) {
|
|
case wxSTREAM_PROTOCOL: return protocols;
|
|
case wxSTREAM_MIMETYPE: return mimetypes;
|
|
case wxSTREAM_FILEEXT: return fileexts;
|
|
default: return empty;
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// tar header block
|
|
|
|
typedef wxFileOffset wxTarNumber;
|
|
|
|
struct wxTarField { const wxChar *name; int pos; };
|
|
|
|
class wxTarHeaderBlock
|
|
{
|
|
public:
|
|
wxTarHeaderBlock()
|
|
{ Clear(); }
|
|
|
|
void Clear(size_t len = 0) { memset(data, 0, len ? len : 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();
|
|
};
|
|
|
|
// A table giving the field names and offsets in a tar header block
|
|
const wxTarField wxTarHeaderBlock::fields[] =
|
|
{
|
|
{ wxT("name"), 0 }, // 100
|
|
{ wxT("mode"), 100 }, // 8
|
|
{ wxT("uid"), 108 }, // 8
|
|
{ wxT("gid"), 116 }, // 8
|
|
{ wxT("size"), 124 }, // 12
|
|
{ wxT("mtime"), 136 }, // 12
|
|
{ wxT("chksum"), 148 }, // 8
|
|
{ wxT("typeflag"), 156 }, // 1
|
|
{ wxT("linkname"), 157 }, // 100
|
|
{ wxT("magic"), 257 }, // 6
|
|
{ wxT("version"), 263 }, // 2
|
|
{ wxT("uname"), 265 }, // 32
|
|
{ wxT("gname"), 297 }, // 32
|
|
{ wxT("devmajor"), 329 }, // 8
|
|
{ wxT("devminor"), 337 }, // 8
|
|
{ wxT("prefix"), 345 }, // 155
|
|
{ wxT("unused"), 500 }, // 12
|
|
{ NULL, TAR_BLOCKSIZE }
|
|
};
|
|
|
|
void wxTarHeaderBlock::check()
|
|
{
|
|
#if 0
|
|
wxCOMPILE_TIME_ASSERT(
|
|
WXSIZEOF(fields) == TAR_NUMFIELDS + 1,
|
|
Wrong_number_of_elements_in_fields_table
|
|
);
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
inline 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 = char('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++)
|
|
{
|
|
wxChar c = name[i];
|
|
approx.data()[i] = c & ~0x7F ? '_' : c;
|
|
}
|
|
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;
|
|
}
|
|
|
|
#ifdef __UNIX__
|
|
|
|
static wxString wxTarUserName(int uid)
|
|
{
|
|
struct passwd *ppw;
|
|
|
|
#ifdef HAVE_GETPWUID_R
|
|
#if defined HAVE_SYSCONF && defined _SC_GETPW_R_SIZE_MAX
|
|
long pwsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
|
size_t bufsize(wxMin(wxMax(1024l, pwsize), 32768l));
|
|
#else
|
|
size_t bufsize = 1024;
|
|
#endif
|
|
wxCharBuffer buf(bufsize);
|
|
struct passwd pw;
|
|
|
|
memset(&pw, 0, sizeof(pw));
|
|
if (getpwuid_r(uid, &pw, buf.data(), bufsize, &ppw) == 0 && pw.pw_name)
|
|
return wxString(pw.pw_name, wxConvLibc);
|
|
#else
|
|
if ((ppw = getpwuid(uid)) != NULL)
|
|
return wxString(ppw->pw_name, wxConvLibc);
|
|
#endif
|
|
return _("unknown");
|
|
}
|
|
|
|
static wxString wxTarGroupName(int gid)
|
|
{
|
|
struct group *pgr;
|
|
#ifdef HAVE_GETGRGID_R
|
|
#if defined HAVE_SYSCONF && defined _SC_GETGR_R_SIZE_MAX
|
|
long grsize = sysconf(_SC_GETGR_R_SIZE_MAX);
|
|
size_t bufsize(wxMin(wxMax(1024l, grsize), 32768l));
|
|
#else
|
|
size_t bufsize = 1024;
|
|
#endif
|
|
wxCharBuffer buf(bufsize);
|
|
struct group gr;
|
|
|
|
memset(&gr, 0, sizeof(gr));
|
|
if (getgrgid_r(gid, &gr, buf.data(), bufsize, &pgr) == 0 && gr.gr_name)
|
|
return wxString(gr.gr_name, wxConvLibc);
|
|
#else
|
|
if ((pgr = getgrgid(gid)) != NULL)
|
|
return wxString(pgr->gr_name, wxConvLibc);
|
|
#endif
|
|
return _("unknown");
|
|
}
|
|
|
|
#endif // __UNIX__
|
|
|
|
// Cache the user and group names since getting them can be expensive,
|
|
// get both names and ids at the same time.
|
|
//
|
|
struct wxTarUser
|
|
{
|
|
wxTarUser();
|
|
~wxTarUser() { delete [] uname; delete [] gname; }
|
|
|
|
int uid;
|
|
int gid;
|
|
|
|
wxChar *uname;
|
|
wxChar *gname;
|
|
};
|
|
|
|
wxTarUser::wxTarUser()
|
|
{
|
|
#ifdef __UNIX__
|
|
uid = getuid();
|
|
gid = getgid();
|
|
wxString usr = wxTarUserName(uid);
|
|
wxString grp = wxTarGroupName(gid);
|
|
#else
|
|
uid = 0;
|
|
gid = 0;
|
|
wxString usr = wxGetUserId();
|
|
wxString grp = _("unknown");
|
|
#endif
|
|
|
|
uname = new wxChar[usr.length() + 1];
|
|
wxStrcpy(uname, usr.c_str());
|
|
|
|
gname = new wxChar[grp.length() + 1];
|
|
wxStrcpy(gname, grp.c_str());
|
|
}
|
|
|
|
static const wxTarUser& wxGetTarUser()
|
|
{
|
|
#if wxUSE_THREADS
|
|
static wxCriticalSection cs;
|
|
wxCriticalSectionLocker lock(cs);
|
|
#endif
|
|
static wxTarUser tu;
|
|
return tu;
|
|
}
|
|
|
|
// 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(wxGetTarUser().uid),
|
|
m_GroupId(wxGetTarUser().gid),
|
|
m_Size(size),
|
|
m_Offset(wxInvalidOffset),
|
|
m_ModifyTime(dt),
|
|
m_TypeFlag(wxTAR_REGTYPE),
|
|
m_UserName(wxGetTarUser().uname),
|
|
m_GroupName(wxGetTarUser().gname),
|
|
m_DevMajor(~0),
|
|
m_DevMinor(~0)
|
|
{
|
|
if (!name.empty())
|
|
SetName(name);
|
|
}
|
|
|
|
wxTarEntry::~wxTarEntry()
|
|
{
|
|
}
|
|
|
|
wxTarEntry::wxTarEntry(const wxTarEntry& e)
|
|
: wxArchiveEntry(),
|
|
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 + wxT("\\") : m_Name);
|
|
for (size_t i = 0; i < name.length(); i++)
|
|
if (name[i] == wxT('/'))
|
|
name[i] = wxT('\\');
|
|
return name;
|
|
}
|
|
|
|
case wxPATH_UNIX:
|
|
return isDir ? m_Name + wxT("/") : 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, wxT("./")) == 0)
|
|
internal.erase(0, 2);
|
|
if (internal == wxT(".") || internal == wxT(".."))
|
|
internal.clear();
|
|
|
|
return internal;
|
|
}
|
|
|
|
bool wxTarEntry::IsDir() const
|
|
{
|
|
return m_TypeFlag == wxTAR_DIRTYPE;
|
|
}
|
|
|
|
void wxTarEntry::SetIsDir(bool isDir)
|
|
{
|
|
if (isDir)
|
|
m_TypeFlag = wxTAR_DIRTYPE;
|
|
else if (m_TypeFlag == wxTAR_DIRTYPE)
|
|
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
|
|
|
|
wxDEFINE_SCOPED_PTR_TYPE(wxTarEntry)
|
|
|
|
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(wxT("mtime")));
|
|
entry->SetAccessTime(GetHeaderDate(wxT("atime")));
|
|
entry->SetCreateTime(GetHeaderDate(wxT("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(GetExtendedHeader(wxS("path")));
|
|
|
|
if (!path.empty())
|
|
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()) + wxT("/") + path : path;
|
|
}
|
|
|
|
wxDateTime wxTarInputStream::GetHeaderDate(const wxString& key) const
|
|
{
|
|
wxString value(GetExtendedHeader(key));
|
|
|
|
// try extended header, stored as decimal seconds since the epoch
|
|
if (!value.empty()) {
|
|
wxLongLong ll;
|
|
ll.Assign(wxAtof(value) * 1000.0);
|
|
return ll;
|
|
}
|
|
|
|
if (key == wxT("mtime"))
|
|
return wxLongLong(m_hdr->GetOctal(TAR_MTIME)) * 1000L;
|
|
|
|
return wxDateTime();
|
|
}
|
|
|
|
wxTarNumber wxTarInputStream::GetHeaderNumber(int id) const
|
|
{
|
|
wxString value(GetExtendedHeader(m_hdr->Name(id)));
|
|
|
|
if (!value.empty()) {
|
|
wxTarNumber n = 0;
|
|
wxString::const_iterator p = value.begin();
|
|
while (p != value.end() && *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(GetExtendedHeader(m_hdr->Name(id)));
|
|
|
|
if (value.empty())
|
|
value = wxString(m_hdr->Get(id), GetConv());
|
|
|
|
return value;
|
|
}
|
|
|
|
// 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 && ok; recPos += recSize) {
|
|
char *pRec = buf.data() + recPos;
|
|
char *p = pRec;
|
|
|
|
// read the record size (byte count in ascii decimal)
|
|
recSize = 0;
|
|
while (isdigit((unsigned char) *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();
|
|
m_endrecWritten = false;
|
|
}
|
|
|
|
wxTarOutputStream::~wxTarOutputStream()
|
|
{
|
|
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 allowed any data
|
|
const char nodata[] = {
|
|
wxTAR_LNKTYPE, wxTAR_SYMTYPE, wxTAR_CHRTYPE, wxTAR_BLKTYPE,
|
|
wxTAR_DIRTYPE, wxTAR_FIFOTYPE, 0
|
|
};
|
|
int typeflag = e->GetTypeFlag();
|
|
|
|
// pax does now allow data for wxTAR_LNKTYPE
|
|
if (!m_pax || typeflag != wxTAR_LNKTYPE)
|
|
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) {
|
|
m_hdr->Clear(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() || (m_tarsize == 0 && m_endrecWritten))
|
|
return false;
|
|
|
|
m_hdr->Clear();
|
|
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();
|
|
m_endrecWritten = true;
|
|
return IsOk();
|
|
}
|
|
|
|
bool wxTarOutputStream::WriteHeaders(wxTarEntry& entry)
|
|
{
|
|
m_hdr->Clear();
|
|
|
|
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(wxT("mtime"), entry.GetDateTime());
|
|
if (entry.GetAccessTime().IsValid())
|
|
SetHeaderDate(wxT("atime"), entry.GetAccessTime());
|
|
if (entry.GetCreateTime().IsValid())
|
|
SetHeaderDate(wxT("ctime"), entry.GetCreateTime());
|
|
|
|
*m_hdr->Get(TAR_TYPEFLAG) = char(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;
|
|
m_hdr2->Clear();
|
|
|
|
// 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(wxT("%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;
|
|
|
|
// update m_headpos which is used to seek back to fix up the file
|
|
// length if it is not known in advance
|
|
if (m_tarstart != wxInvalidOffset)
|
|
m_headpos = m_tarstart + m_tarsize;
|
|
}
|
|
|
|
// 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(wxT('/'));
|
|
wxString f = path.AfterLast(wxT('/'));
|
|
wxString ret;
|
|
|
|
if (d.empty())
|
|
d = wxT(".");
|
|
|
|
ret.reserve(format.length() + path.length() + 16);
|
|
|
|
size_t begin = 0;
|
|
|
|
for (;;) {
|
|
size_t end;
|
|
end = format.find('%', begin);
|
|
if (end == wxString::npos || end + 1 >= format.length())
|
|
break;
|
|
ret << format.substr(begin, end - begin);
|
|
switch ( format[end + 1].GetValue() ) {
|
|
case 'd': ret << d; break;
|
|
case 'f': ret << f; break;
|
|
case 'p': ret << wxGetProcessId(); break;
|
|
case '%': ret << wxT("%"); 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(wxT("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 != wxT("mtime")
|
|
|| !m_hdr->SetOctal(TAR_MTIME, wxTarNumber(secs.GetValue()))
|
|
|| secs <= 0 || secs >= 0x7fffffff)
|
|
{
|
|
wxString str;
|
|
if (ll >= LONG_MIN && ll <= LONG_MAX) {
|
|
str.Printf(wxT("%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) {
|
|
#if wxUSE_UNICODE
|
|
const wxCharBuffer utf_key = key.utf8_str();
|
|
const wxCharBuffer utf_value = value.utf8_str();
|
|
#else
|
|
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);
|
|
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
|
|
|
|
// 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 += wxT(", ");
|
|
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
|