1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Streams for Tar files
4 // Author: Mike Wetherell
6 // Copyright: (c) 2004 Mike Wetherell
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
19 #include "wx/tarstrm.h"
27 #include "wx/buffer.h"
28 #include "wx/datetime.h"
29 #include "wx/ptr_scpd.h"
30 #include "wx/filename.h"
31 #include "wx/thread.h"
41 /////////////////////////////////////////////////////////////////////////////
78 TYPE_OLDTAR
, // fields after TAR_LINKNAME are invalid
79 TYPE_GNUTAR
, // all fields except TAR_PREFIX are valid
80 TYPE_USTAR
// all fields are valid
84 static const char *USTAR_MAGIC
= "ustar";
85 static const char *USTAR_VERSION
= "00";
86 static const char *GNU_MAGIC
= "ustar ";
87 static const char *GNU_VERION
= " ";
89 IMPLEMENT_DYNAMIC_CLASS(wxTarEntry
, wxArchiveEntry
)
90 IMPLEMENT_DYNAMIC_CLASS(wxTarClassFactory
, wxArchiveClassFactory
)
93 /////////////////////////////////////////////////////////////////////////////
96 static wxTarClassFactory g_wxTarClassFactory
;
98 wxTarClassFactory::wxTarClassFactory()
100 if (this == &g_wxTarClassFactory
)
104 const wxChar
* const *
105 wxTarClassFactory::GetProtocols(wxStreamProtocolType type
) const
107 static const wxChar
*protocols
[] = { _T("tar"), NULL
};
108 static const wxChar
*mimetypes
[] = { _T("application/x-tar"), NULL
};
109 static const wxChar
*fileexts
[] = { _T(".tar"), NULL
};
110 static const wxChar
*empty
[] = { NULL
};
113 case wxSTREAM_PROTOCOL
: return protocols
;
114 case wxSTREAM_MIMETYPE
: return mimetypes
;
115 case wxSTREAM_FILEEXT
: return fileexts
;
116 default: return empty
;
121 /////////////////////////////////////////////////////////////////////////////
124 typedef wxFileOffset wxTarNumber
;
126 struct wxTarField
{ const wxChar
*name
; int pos
; };
128 class wxTarHeaderBlock
132 { memset(data
, 0, sizeof(data
)); }
133 wxTarHeaderBlock(const wxTarHeaderBlock
& hb
)
134 { memcpy(data
, hb
.data
, sizeof(data
)); }
136 bool Read(wxInputStream
& in
);
137 bool Write(wxOutputStream
& out
);
138 inline bool WriteField(wxOutputStream
& out
, int id
);
140 bool IsAllZeros() const;
141 wxUint32
Sum(bool SignedSum
= false);
142 wxUint32
SumField(int id
);
144 char *Get(int id
) { return data
+ fields
[id
].pos
+ id
; }
145 static size_t Len(int id
) { return fields
[id
+ 1].pos
- fields
[id
].pos
; }
146 static const wxChar
*Name(int id
) { return fields
[id
].name
; }
147 static size_t Offset(int id
) { return fields
[id
].pos
; }
149 bool SetOctal(int id
, wxTarNumber n
);
150 wxTarNumber
GetOctal(int id
);
151 bool SetPath(const wxString
& name
, wxMBConv
& conv
);
154 char data
[TAR_BLOCKSIZE
+ TAR_NUMFIELDS
];
155 static const wxTarField fields
[];
159 wxDEFINE_SCOPED_PTR_TYPE(wxTarHeaderBlock
)
161 // A table giving the field names and offsets in a tar header block
162 const wxTarField
wxTarHeaderBlock::fields
[] =
164 { _T("name"), 0 }, // 100
165 { _T("mode"), 100 }, // 8
166 { _T("uid"), 108 }, // 8
167 { _T("gid"), 116 }, // 8
168 { _T("size"), 124 }, // 12
169 { _T("mtime"), 136 }, // 12
170 { _T("chksum"), 148 }, // 8
171 { _T("typeflag"), 156 }, // 1
172 { _T("linkname"), 157 }, // 100
173 { _T("magic"), 257 }, // 6
174 { _T("version"), 263 }, // 2
175 { _T("uname"), 265 }, // 32
176 { _T("gname"), 297 }, // 32
177 { _T("devmajor"), 329 }, // 8
178 { _T("devminor"), 337 }, // 8
179 { _T("prefix"), 345 }, // 155
180 { _T("unused"), 500 }, // 12
181 { NULL
, TAR_BLOCKSIZE
}
184 void wxTarHeaderBlock::check()
187 wxCOMPILE_TIME_ASSERT(
188 WXSIZEOF(fields
) == TAR_NUMFIELDS
+ 1,
189 Wrong_number_of_elements_in_fields_table
194 bool wxTarHeaderBlock::IsAllZeros() const
196 const char *p
= data
;
197 for (size_t i
= 0; i
< sizeof(data
); i
++)
203 wxUint32
wxTarHeaderBlock::Sum(bool SignedSum
/*=false*/)
205 // the chksum field itself should be blanks during the calculation
206 memset(Get(TAR_CHKSUM
), ' ', Len(TAR_CHKSUM
));
207 const char *p
= data
;
211 for (size_t i
= 0; i
< sizeof(data
); i
++)
212 n
+= (signed char)p
[i
];
214 for (size_t i
= 0; i
< sizeof(data
); i
++)
215 n
+= (unsigned char)p
[i
];
220 wxUint32
wxTarHeaderBlock::SumField(int id
)
222 unsigned char *p
= (unsigned char*)Get(id
);
223 unsigned char *q
= p
+ Len(id
);
232 bool wxTarHeaderBlock::Read(wxInputStream
& in
)
236 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
237 ok
= in
.Read(Get(id
), Len(id
)).LastRead() == Len(id
);
242 bool wxTarHeaderBlock::Write(wxOutputStream
& out
)
246 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
247 ok
= WriteField(out
, id
);
252 inline bool wxTarHeaderBlock::WriteField(wxOutputStream
& out
, int id
)
254 return out
.Write(Get(id
), Len(id
)).LastWrite() == Len(id
);
257 wxTarNumber
wxTarHeaderBlock::GetOctal(int id
)
260 const char *p
= Get(id
);
263 while (*p
>= '0' && *p
< '8')
264 n
= (n
<< 3) | (*p
++ - '0');
268 bool wxTarHeaderBlock::SetOctal(int id
, wxTarNumber n
)
270 // set an octal field, return true if the number fits
271 char *field
= Get(id
);
272 char *p
= field
+ Len(id
);
275 *--p
= char('0' + (n
& 7));
281 bool wxTarHeaderBlock::SetPath(const wxString
& name
, wxMBConv
& conv
)
283 bool badconv
= false;
286 wxCharBuffer nameBuf
= name
.mb_str(conv
);
288 // if the conversion fails make an approximation
291 size_t len
= name
.length();
292 wxCharBuffer
approx(len
);
293 for (size_t i
= 0; i
< len
; i
++)
296 approx
.data()[i
] = c
& ~0x7F ? '_' : c
;
301 const char *mbName
= nameBuf
;
303 const char *mbName
= name
.c_str();
308 bool notGoingToFit
= false;
309 size_t len
= strlen(mbName
);
310 size_t maxname
= Len(TAR_NAME
);
311 size_t maxprefix
= Len(TAR_PREFIX
);
316 fits
= i
< maxprefix
&& len
- i
<= maxname
;
319 const char *p
= strchr(mbName
+ i
, '/');
321 nexti
= p
- mbName
+ 1;
322 if (!p
|| nexti
- 1 > maxprefix
)
323 notGoingToFit
= true;
326 if (fits
|| notGoingToFit
) {
327 strncpy(Get(TAR_NAME
), mbName
+ i
, maxname
);
329 strncpy(Get(TAR_PREFIX
), mbName
, i
- 1);
336 return fits
&& !badconv
;
340 /////////////////////////////////////////////////////////////////////////////
343 static wxFileOffset
RoundUpSize(wxFileOffset size
, int factor
= 1)
345 wxFileOffset chunk
= TAR_BLOCKSIZE
* factor
;
346 return ((size
+ chunk
- 1) / chunk
) * chunk
;
351 static wxString
wxTarUserName(int uid
)
355 #ifdef HAVE_GETPWUID_R
356 #if defined HAVE_SYSCONF && defined _SC_GETPW_R_SIZE_MAX
357 long pwsize
= sysconf(_SC_GETPW_R_SIZE_MAX
);
358 size_t bufsize(wxMin(wxMax(1024l, pwsize
), 32768l));
360 size_t bufsize
= 1024;
362 wxCharBuffer
buf(bufsize
);
365 if (getpwuid_r(uid
, &pw
, buf
.data(), bufsize
, &ppw
) == 0)
366 return wxString(pw
.pw_name
, wxConvLibc
);
368 if ((ppw
= getpwuid(uid
)) != NULL
)
369 return wxString(ppw
->pw_name
, wxConvLibc
);
374 static wxString
wxTarGroupName(int gid
)
377 #ifdef HAVE_GETGRGID_R
378 #if defined HAVE_SYSCONF && defined _SC_GETGR_R_SIZE_MAX
379 long grsize
= sysconf(_SC_GETGR_R_SIZE_MAX
);
380 size_t bufsize(wxMin(wxMax(1024l, grsize
), 32768l));
382 size_t bufsize
= 1024;
384 wxCharBuffer
buf(bufsize
);
387 if (getgrgid_r(gid
, &gr
, buf
.data(), bufsize
, &pgr
) == 0)
388 return wxString(gr
.gr_name
, wxConvLibc
);
390 if ((pgr
= getgrgid(gid
)) != NULL
)
391 return wxString(pgr
->gr_name
, wxConvLibc
);
398 // Cache the user and group names since getting them can be expensive,
399 // get both names and ids at the same time.
404 ~wxTarUser() { delete [] uname
; delete [] gname
; }
413 wxTarUser::wxTarUser()
418 wxString usr
= wxTarUserName(uid
);
419 wxString grp
= wxTarGroupName(gid
);
423 wxString usr
= wxGetUserId();
424 wxString grp
= _("unknown");
427 uname
= new wxChar
[usr
.length() + 1];
428 wxStrcpy(uname
, usr
.c_str());
430 gname
= new wxChar
[grp
.length() + 1];
431 wxStrcpy(gname
, grp
.c_str());
434 static const wxTarUser
& wxGetTarUser()
437 static wxCriticalSection cs
;
438 wxCriticalSectionLocker
lock(cs
);
444 // ignore the size field for entry types 3, 4, 5 and 6
446 static inline wxFileOffset
GetDataSize(const wxTarEntry
& entry
)
448 switch (entry
.GetTypeFlag()) {
455 return entry
.GetSize();
460 /////////////////////////////////////////////////////////////////////////////
462 // Holds all the meta-data for a file in the tar
464 wxTarEntry::wxTarEntry(const wxString
& name
/*=wxEmptyString*/,
465 const wxDateTime
& dt
/*=wxDateTime::Now()*/,
466 wxFileOffset size
/*=0*/)
469 m_UserId(wxGetTarUser().uid
),
470 m_GroupId(wxGetTarUser().gid
),
472 m_Offset(wxInvalidOffset
),
474 m_TypeFlag(wxTAR_REGTYPE
),
475 m_UserName(wxGetTarUser().uname
),
476 m_GroupName(wxGetTarUser().gname
),
484 wxTarEntry::~wxTarEntry()
488 wxTarEntry::wxTarEntry(const wxTarEntry
& e
)
492 m_IsModeSet(e
.m_IsModeSet
),
493 m_UserId(e
.m_UserId
),
494 m_GroupId(e
.m_GroupId
),
496 m_Offset(e
.m_Offset
),
497 m_ModifyTime(e
.m_ModifyTime
),
498 m_AccessTime(e
.m_AccessTime
),
499 m_CreateTime(e
.m_CreateTime
),
500 m_TypeFlag(e
.m_TypeFlag
),
501 m_LinkName(e
.m_LinkName
),
502 m_UserName(e
.m_UserName
),
503 m_GroupName(e
.m_GroupName
),
504 m_DevMajor(e
.m_DevMajor
),
505 m_DevMinor(e
.m_DevMinor
)
509 wxTarEntry
& wxTarEntry::operator=(const wxTarEntry
& e
)
514 m_IsModeSet
= e
.m_IsModeSet
;
515 m_UserId
= e
.m_UserId
;
516 m_GroupId
= e
.m_GroupId
;
518 m_Offset
= e
.m_Offset
;
519 m_ModifyTime
= e
.m_ModifyTime
;
520 m_AccessTime
= e
.m_AccessTime
;
521 m_CreateTime
= e
.m_CreateTime
;
522 m_TypeFlag
= e
.m_TypeFlag
;
523 m_LinkName
= e
.m_LinkName
;
524 m_UserName
= e
.m_UserName
;
525 m_GroupName
= e
.m_GroupName
;
526 m_DevMajor
= e
.m_DevMajor
;
527 m_DevMinor
= e
.m_DevMinor
;
532 wxString
wxTarEntry::GetName(wxPathFormat format
/*=wxPATH_NATIVE*/) const
534 bool isDir
= IsDir() && !m_Name
.empty();
536 // optimisations for common (and easy) cases
537 switch (wxFileName::GetFormat(format
)) {
540 wxString
name(isDir
? m_Name
+ _T("\\") : m_Name
);
541 for (size_t i
= 0; i
< name
.length(); i
++)
542 if (name
[i
] == _T('/'))
548 return isDir
? m_Name
+ _T("/") : m_Name
;
557 fn
.AssignDir(m_Name
, wxPATH_UNIX
);
559 fn
.Assign(m_Name
, wxPATH_UNIX
);
561 return fn
.GetFullPath(format
);
564 void wxTarEntry::SetName(const wxString
& name
, wxPathFormat format
)
567 m_Name
= GetInternalName(name
, format
, &isDir
);
571 // Static - Internally tars and zips use forward slashes for the path
572 // separator, absolute paths aren't allowed, and directory names have a
573 // trailing slash. This function converts a path into this internal format,
574 // but without a trailing slash for a directory.
576 wxString
wxTarEntry::GetInternalName(const wxString
& name
,
577 wxPathFormat format
/*=wxPATH_NATIVE*/,
578 bool *pIsDir
/*=NULL*/)
582 if (wxFileName::GetFormat(format
) != wxPATH_UNIX
)
583 internal
= wxFileName(name
, format
).GetFullPath(wxPATH_UNIX
);
587 bool isDir
= !internal
.empty() && internal
.Last() == '/';
591 internal
.erase(internal
.length() - 1);
593 while (!internal
.empty() && *internal
.begin() == '/')
594 internal
.erase(0, 1);
595 while (!internal
.empty() && internal
.compare(0, 2, _T("./")) == 0)
596 internal
.erase(0, 2);
597 if (internal
== _T(".") || internal
== _T(".."))
598 internal
= wxEmptyString
;
603 bool wxTarEntry::IsDir() const
605 return m_TypeFlag
== wxTAR_DIRTYPE
;
608 void wxTarEntry::SetIsDir(bool isDir
)
611 m_TypeFlag
= wxTAR_DIRTYPE
;
612 else if (m_TypeFlag
== wxTAR_DIRTYPE
)
613 m_TypeFlag
= wxTAR_REGTYPE
;
616 void wxTarEntry::SetIsReadOnly(bool isReadOnly
)
624 int wxTarEntry::GetMode() const
626 if (m_IsModeSet
|| !IsDir())
629 return m_Mode
| 0111;
633 void wxTarEntry::SetMode(int mode
)
635 m_Mode
= mode
& 07777;
640 /////////////////////////////////////////////////////////////////////////////
643 wxDECLARE_SCOPED_PTR(wxTarEntry
, wxTarEntryPtr_
)
644 wxDEFINE_SCOPED_PTR (wxTarEntry
, wxTarEntryPtr_
)
646 wxTarInputStream::wxTarInputStream(wxInputStream
& stream
,
647 wxMBConv
& conv
/*=wxConvLocal*/)
648 : wxArchiveInputStream(stream
, conv
)
653 wxTarInputStream::wxTarInputStream(wxInputStream
*stream
,
654 wxMBConv
& conv
/*=wxConvLocal*/)
655 : wxArchiveInputStream(stream
, conv
)
660 void wxTarInputStream::Init()
662 m_pos
= wxInvalidOffset
;
664 m_size
= wxInvalidOffset
;
665 m_sumType
= SUM_UNKNOWN
;
666 m_tarType
= TYPE_USTAR
;
667 m_hdr
= new wxTarHeaderBlock
;
669 m_GlobalHeaderRecs
= NULL
;
670 m_lasterror
= m_parent_i_stream
->GetLastError();
673 wxTarInputStream::~wxTarInputStream()
677 delete m_GlobalHeaderRecs
;
680 wxTarEntry
*wxTarInputStream::GetNextEntry()
682 m_lasterror
= ReadHeaders();
687 wxTarEntryPtr_
entry(new wxTarEntry
);
689 entry
->SetMode(GetHeaderNumber(TAR_MODE
));
690 entry
->SetUserId(GetHeaderNumber(TAR_UID
));
691 entry
->SetGroupId(GetHeaderNumber(TAR_UID
));
692 entry
->SetSize(GetHeaderNumber(TAR_SIZE
));
694 entry
->SetOffset(m_offset
);
696 entry
->SetDateTime(GetHeaderDate(_T("mtime")));
697 entry
->SetAccessTime(GetHeaderDate(_T("atime")));
698 entry
->SetCreateTime(GetHeaderDate(_T("ctime")));
700 entry
->SetTypeFlag(*m_hdr
->Get(TAR_TYPEFLAG
));
701 bool isDir
= entry
->IsDir();
703 entry
->SetLinkName(GetHeaderString(TAR_LINKNAME
));
705 if (m_tarType
!= TYPE_OLDTAR
) {
706 entry
->SetUserName(GetHeaderString(TAR_UNAME
));
707 entry
->SetGroupName(GetHeaderString(TAR_GNAME
));
709 entry
->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR
));
710 entry
->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR
));
713 entry
->SetName(GetHeaderPath(), wxPATH_UNIX
);
718 m_HeaderRecs
->clear();
720 m_size
= GetDataSize(*entry
);
723 return entry
.release();
726 bool wxTarInputStream::OpenEntry(wxTarEntry
& entry
)
728 wxFileOffset offset
= entry
.GetOffset();
730 if (GetLastError() != wxSTREAM_READ_ERROR
731 && m_parent_i_stream
->IsSeekable()
732 && m_parent_i_stream
->SeekI(offset
) == offset
)
735 m_size
= GetDataSize(entry
);
737 m_lasterror
= wxSTREAM_NO_ERROR
;
740 m_lasterror
= wxSTREAM_READ_ERROR
;
745 bool wxTarInputStream::OpenEntry(wxArchiveEntry
& entry
)
747 wxTarEntry
*tarEntry
= wxStaticCast(&entry
, wxTarEntry
);
748 return tarEntry
? OpenEntry(*tarEntry
) : false;
751 bool wxTarInputStream::CloseEntry()
753 if (m_lasterror
== wxSTREAM_READ_ERROR
)
758 wxFileOffset size
= RoundUpSize(m_size
);
759 wxFileOffset remainder
= size
- m_pos
;
761 if (remainder
&& m_parent_i_stream
->IsSeekable()) {
763 if (m_parent_i_stream
->SeekI(remainder
, wxFromCurrent
)
769 const int BUFSIZE
= 8192;
770 wxCharBuffer
buf(BUFSIZE
);
772 while (remainder
> 0 && m_parent_i_stream
->IsOk())
773 remainder
-= m_parent_i_stream
->Read(
774 buf
.data(), wxMin(BUFSIZE
, remainder
)).LastRead();
777 m_pos
= wxInvalidOffset
;
779 m_lasterror
= m_parent_i_stream
->GetLastError();
784 wxStreamError
wxTarInputStream::ReadHeaders()
787 return wxSTREAM_READ_ERROR
;
792 m_hdr
->Read(*m_parent_i_stream
);
793 if (m_parent_i_stream
->Eof())
794 wxLogError(_("incomplete header block in tar"));
795 if (!*m_parent_i_stream
)
796 return wxSTREAM_READ_ERROR
;
797 m_offset
+= TAR_BLOCKSIZE
;
799 // an all-zero header marks the end of the tar
800 if (m_hdr
->IsAllZeros())
803 // the checksum is supposed to be the unsigned sum of the header bytes,
804 // but there have been versions of tar that used the signed sum, so
805 // accept that too, but only if used throughout.
806 wxUint32 chksum
= m_hdr
->GetOctal(TAR_CHKSUM
);
809 if (m_sumType
!= SUM_SIGNED
) {
810 ok
= chksum
== m_hdr
->Sum();
811 if (m_sumType
== SUM_UNKNOWN
)
812 m_sumType
= ok
? SUM_UNSIGNED
: SUM_SIGNED
;
814 if (m_sumType
== SUM_SIGNED
)
815 ok
= chksum
== m_hdr
->Sum(true);
817 wxLogError(_("checksum failure reading tar header block"));
818 return wxSTREAM_READ_ERROR
;
821 if (strcmp(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
) == 0)
822 m_tarType
= TYPE_USTAR
;
823 else if (strcmp(m_hdr
->Get(TAR_MAGIC
), GNU_MAGIC
) == 0 &&
824 strcmp(m_hdr
->Get(TAR_VERSION
), GNU_VERION
) == 0)
825 m_tarType
= TYPE_GNUTAR
;
827 m_tarType
= TYPE_OLDTAR
;
829 if (m_tarType
!= TYPE_USTAR
)
832 switch (*m_hdr
->Get(TAR_TYPEFLAG
)) {
833 case 'g': ReadExtendedHeader(m_GlobalHeaderRecs
); break;
834 case 'x': ReadExtendedHeader(m_HeaderRecs
); break;
835 default: done
= true;
839 return wxSTREAM_NO_ERROR
;
842 wxString
wxTarInputStream::GetExtendedHeader(const wxString
& key
) const
844 wxTarHeaderRecords::iterator it
;
846 // look at normal extended header records first
848 it
= m_HeaderRecs
->find(key
);
849 if (it
!= m_HeaderRecs
->end())
850 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
853 // if not found, look at the global header records
854 if (m_GlobalHeaderRecs
) {
855 it
= m_GlobalHeaderRecs
->find(key
);
856 if (it
!= m_GlobalHeaderRecs
->end())
857 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
860 return wxEmptyString
;
863 wxString
wxTarInputStream::GetHeaderPath() const
867 if ((path
= GetExtendedHeader(_T("path"))) != wxEmptyString
)
870 path
= wxString(m_hdr
->Get(TAR_NAME
), GetConv());
871 if (m_tarType
!= TYPE_USTAR
)
874 const char *prefix
= m_hdr
->Get(TAR_PREFIX
);
875 return *prefix
? wxString(prefix
, GetConv()) + _T("/") + path
: path
;
878 wxDateTime
wxTarInputStream::GetHeaderDate(const wxString
& key
) const
882 // try extended header, stored as decimal seconds since the epoch
883 if ((value
= GetExtendedHeader(key
)) != wxEmptyString
) {
885 ll
.Assign(wxAtof(value
) * 1000.0);
889 if (key
== _T("mtime"))
890 return wxLongLong(m_hdr
->GetOctal(TAR_MTIME
)) * 1000L;
895 wxTarNumber
wxTarInputStream::GetHeaderNumber(int id
) const
899 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
) {
901 wxString::const_iterator p
= value
.begin();
902 while (*p
== ' ' && p
!= value
.end())
905 n
= n
* 10 + (*p
++ - '0');
908 return m_hdr
->GetOctal(id
);
912 wxString
wxTarInputStream::GetHeaderString(int id
) const
916 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
)
919 return wxString(m_hdr
->Get(id
), GetConv());
922 // An extended header consists of one or more records, each constructed:
923 // "%d %s=%s\n", <length>, <keyword>, <value>
924 // <length> is the byte length, <keyword> and <value> are UTF-8
926 bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords
*& recs
)
929 recs
= new wxTarHeaderRecords
;
931 // round length up to a whole number of blocks
932 size_t len
= m_hdr
->GetOctal(TAR_SIZE
);
933 size_t size
= RoundUpSize(len
);
935 // read in the whole header since it should be small
936 wxCharBuffer
buf(size
);
937 size_t lastread
= m_parent_i_stream
->Read(buf
.data(), size
).LastRead();
941 m_offset
+= lastread
;
943 size_t recPos
, recSize
;
946 for (recPos
= 0; recPos
< len
; recPos
+= recSize
) {
947 char *pRec
= buf
.data() + recPos
;
950 // read the record size (byte count in ascii decimal)
952 while (isdigit((unsigned char) *p
))
953 recSize
= recSize
* 10 + *p
++ - '0';
956 if (recPos
+ recSize
> len
)
958 if (recSize
< p
- pRec
+ (size_t)3 || *p
!= ' '
959 || pRec
[recSize
- 1] != '\012') {
964 // replace the final '\n' with a nul, to terminate value
965 pRec
[recSize
- 1] = 0;
966 // the key is here, following the space
969 // look forward for the '=', the value follows
970 while (*p
&& *p
!= '=')
976 // replace the '=' with a nul, to terminate the key
979 wxString
key(wxConvUTF8
.cMB2WC(pKey
), GetConv());
980 wxString
value(wxConvUTF8
.cMB2WC(p
), GetConv());
982 // an empty value unsets a previously given value
986 (*recs
)[key
] = value
;
989 if (!ok
|| recPos
< len
|| size
!= lastread
) {
990 wxLogWarning(_("invalid data in extended tar header"));
997 wxFileOffset
wxTarInputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
1000 wxLogError(_("tar entry not open"));
1001 m_lasterror
= wxSTREAM_READ_ERROR
;
1004 return wxInvalidOffset
;
1007 case wxFromStart
: break;
1008 case wxFromCurrent
: pos
+= m_pos
; break;
1009 case wxFromEnd
: pos
+= m_size
; break;
1012 if (pos
< 0 || m_parent_i_stream
->SeekI(m_offset
+ pos
) == wxInvalidOffset
)
1013 return wxInvalidOffset
;
1019 size_t wxTarInputStream::OnSysRead(void *buffer
, size_t size
)
1022 wxLogError(_("tar entry not open"));
1023 m_lasterror
= wxSTREAM_READ_ERROR
;
1025 if (!IsOk() || !size
)
1028 if (m_pos
>= m_size
)
1030 else if (m_pos
+ size
> m_size
+ (size_t)0)
1031 size
= m_size
- m_pos
;
1033 size_t lastread
= m_parent_i_stream
->Read(buffer
, size
).LastRead();
1036 if (m_pos
>= m_size
) {
1037 m_lasterror
= wxSTREAM_EOF
;
1038 } else if (!m_parent_i_stream
->IsOk()) {
1039 // any other error will have been reported by the underlying stream
1040 if (m_parent_i_stream
->Eof())
1041 wxLogError(_("unexpected end of file"));
1042 m_lasterror
= wxSTREAM_READ_ERROR
;
1049 /////////////////////////////////////////////////////////////////////////////
1052 wxTarOutputStream::wxTarOutputStream(wxOutputStream
& stream
,
1053 wxTarFormat format
/*=wxTAR_PAX*/,
1054 wxMBConv
& conv
/*=wxConvLocal*/)
1055 : wxArchiveOutputStream(stream
, conv
)
1060 wxTarOutputStream::wxTarOutputStream(wxOutputStream
*stream
,
1061 wxTarFormat format
/*=wxTAR_PAX*/,
1062 wxMBConv
& conv
/*=wxConvLocal*/)
1063 : wxArchiveOutputStream(stream
, conv
)
1068 void wxTarOutputStream::Init(wxTarFormat format
)
1070 m_pos
= wxInvalidOffset
;
1071 m_maxpos
= wxInvalidOffset
;
1072 m_size
= wxInvalidOffset
;
1073 m_headpos
= wxInvalidOffset
;
1074 m_datapos
= wxInvalidOffset
;
1075 m_tarstart
= wxInvalidOffset
;
1077 m_pax
= format
== wxTAR_PAX
;
1078 m_BlockingFactor
= m_pax
? 10 : 20;
1081 m_hdr
= new wxTarHeaderBlock
;
1083 m_extendedHdr
= NULL
;
1085 m_lasterror
= m_parent_o_stream
->GetLastError();
1086 m_endrecWritten
= false;
1089 wxTarOutputStream::~wxTarOutputStream()
1094 delete [] m_extendedHdr
;
1097 bool wxTarOutputStream::PutNextEntry(wxTarEntry
*entry
)
1099 wxTarEntryPtr_
e(entry
);
1106 m_tarstart
= m_parent_o_stream
->TellO();
1109 if (m_tarstart
!= wxInvalidOffset
)
1110 m_headpos
= m_tarstart
+ m_tarsize
;
1112 if (WriteHeaders(*e
)) {
1115 m_size
= GetDataSize(*e
);
1116 if (m_tarstart
!= wxInvalidOffset
)
1117 m_datapos
= m_tarstart
+ m_tarsize
;
1119 // types that are not allowd any data
1120 const char nodata
[] = {
1121 wxTAR_LNKTYPE
, wxTAR_SYMTYPE
, wxTAR_CHRTYPE
, wxTAR_BLKTYPE
,
1122 wxTAR_DIRTYPE
, wxTAR_FIFOTYPE
, 0
1124 int typeflag
= e
->GetTypeFlag();
1126 // pax does now allow data for wxTAR_LNKTYPE
1127 if (!m_pax
|| typeflag
!= wxTAR_LNKTYPE
)
1128 if (strchr(nodata
, typeflag
) != NULL
)
1135 bool wxTarOutputStream::PutNextEntry(const wxString
& name
,
1136 const wxDateTime
& dt
,
1139 return PutNextEntry(new wxTarEntry(name
, dt
, size
));
1142 bool wxTarOutputStream::PutNextDirEntry(const wxString
& name
,
1143 const wxDateTime
& dt
)
1145 wxTarEntry
*entry
= new wxTarEntry(name
, dt
);
1147 return PutNextEntry(entry
);
1150 bool wxTarOutputStream::PutNextEntry(wxArchiveEntry
*entry
)
1152 wxTarEntry
*tarEntry
= wxStaticCast(entry
, wxTarEntry
);
1155 return PutNextEntry(tarEntry
);
1158 bool wxTarOutputStream::CopyEntry(wxTarEntry
*entry
,
1159 wxTarInputStream
& inputStream
)
1161 if (PutNextEntry(entry
))
1163 return IsOk() && inputStream
.Eof();
1166 bool wxTarOutputStream::CopyEntry(wxArchiveEntry
*entry
,
1167 wxArchiveInputStream
& inputStream
)
1169 if (PutNextEntry(entry
))
1171 return IsOk() && inputStream
.Eof();
1174 bool wxTarOutputStream::CloseEntry()
1179 if (m_pos
< m_maxpos
) {
1180 wxASSERT(m_parent_o_stream
->IsSeekable());
1181 m_parent_o_stream
->SeekO(m_datapos
+ m_maxpos
);
1182 m_lasterror
= m_parent_o_stream
->GetLastError();
1187 wxFileOffset size
= RoundUpSize(m_pos
);
1189 memset(m_hdr
, 0, size
- m_pos
);
1190 m_parent_o_stream
->Write(m_hdr
, size
- m_pos
);
1191 m_lasterror
= m_parent_o_stream
->GetLastError();
1196 if (IsOk() && m_pos
!= m_size
)
1199 m_pos
= wxInvalidOffset
;
1200 m_maxpos
= wxInvalidOffset
;
1201 m_size
= wxInvalidOffset
;
1202 m_headpos
= wxInvalidOffset
;
1203 m_datapos
= wxInvalidOffset
;
1208 bool wxTarOutputStream::Close()
1210 if (!CloseEntry() || (m_tarsize
== 0 && m_endrecWritten
))
1213 memset(m_hdr
, 0, sizeof(*m_hdr
));
1214 int count
= (RoundUpSize(m_tarsize
+ 2 * TAR_BLOCKSIZE
, m_BlockingFactor
)
1215 - m_tarsize
) / TAR_BLOCKSIZE
;
1217 m_parent_o_stream
->Write(m_hdr
, TAR_BLOCKSIZE
);
1220 m_tarstart
= wxInvalidOffset
;
1221 m_lasterror
= m_parent_o_stream
->GetLastError();
1222 m_endrecWritten
= true;
1226 bool wxTarOutputStream::WriteHeaders(wxTarEntry
& entry
)
1228 memset(m_hdr
, 0, sizeof(*m_hdr
));
1230 SetHeaderPath(entry
.GetName(wxPATH_UNIX
));
1232 SetHeaderNumber(TAR_MODE
, entry
.GetMode());
1233 SetHeaderNumber(TAR_UID
, entry
.GetUserId());
1234 SetHeaderNumber(TAR_GID
, entry
.GetGroupId());
1236 if (entry
.GetSize() == wxInvalidOffset
)
1238 m_large
= !SetHeaderNumber(TAR_SIZE
, entry
.GetSize());
1240 SetHeaderDate(_T("mtime"), entry
.GetDateTime());
1241 if (entry
.GetAccessTime().IsValid())
1242 SetHeaderDate(_T("atime"), entry
.GetAccessTime());
1243 if (entry
.GetCreateTime().IsValid())
1244 SetHeaderDate(_T("ctime"), entry
.GetCreateTime());
1246 *m_hdr
->Get(TAR_TYPEFLAG
) = char(entry
.GetTypeFlag());
1248 strcpy(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1249 strcpy(m_hdr
->Get(TAR_VERSION
), USTAR_VERSION
);
1251 SetHeaderString(TAR_LINKNAME
, entry
.GetLinkName());
1252 SetHeaderString(TAR_UNAME
, entry
.GetUserName());
1253 SetHeaderString(TAR_GNAME
, entry
.GetGroupName());
1255 if (~entry
.GetDevMajor())
1256 SetHeaderNumber(TAR_DEVMAJOR
, entry
.GetDevMajor());
1257 if (~entry
.GetDevMinor())
1258 SetHeaderNumber(TAR_DEVMINOR
, entry
.GetDevMinor());
1260 m_chksum
= m_hdr
->Sum();
1261 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1263 m_chksum
-= m_hdr
->SumField(TAR_SIZE
);
1265 // The main header is now fully prepared so we know what extended headers
1266 // (if any) will be needed. Output any extended headers before writing
1268 if (m_extendedHdr
&& *m_extendedHdr
) {
1270 // the extended headers are written to the tar as a file entry,
1271 // so prepare a regular header block for the pseudo-file.
1273 m_hdr2
= new wxTarHeaderBlock
;
1274 memset(m_hdr2
, 0, sizeof(*m_hdr2
));
1276 // an old tar that doesn't understand extended headers will
1277 // extract it as a file, so give these fields reasonable values
1278 // so that the user will have access to read and remove it.
1279 m_hdr2
->SetPath(PaxHeaderPath(_T("%d/PaxHeaders.%p/%f"),
1280 entry
.GetName(wxPATH_UNIX
)), GetConv());
1281 m_hdr2
->SetOctal(TAR_MODE
, 0600);
1282 strcpy(m_hdr2
->Get(TAR_UID
), m_hdr
->Get(TAR_UID
));
1283 strcpy(m_hdr2
->Get(TAR_GID
), m_hdr
->Get(TAR_GID
));
1284 size_t length
= strlen(m_extendedHdr
);
1285 m_hdr2
->SetOctal(TAR_SIZE
, length
);
1286 strcpy(m_hdr2
->Get(TAR_MTIME
), m_hdr
->Get(TAR_MTIME
));
1287 *m_hdr2
->Get(TAR_TYPEFLAG
) = 'x';
1288 strcpy(m_hdr2
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1289 strcpy(m_hdr2
->Get(TAR_VERSION
), USTAR_VERSION
);
1290 strcpy(m_hdr2
->Get(TAR_UNAME
), m_hdr
->Get(TAR_UNAME
));
1291 strcpy(m_hdr2
->Get(TAR_GNAME
), m_hdr
->Get(TAR_GNAME
));
1293 m_hdr2
->SetOctal(TAR_CHKSUM
, m_hdr2
->Sum());
1295 m_hdr2
->Write(*m_parent_o_stream
);
1296 m_tarsize
+= TAR_BLOCKSIZE
;
1298 size_t rounded
= RoundUpSize(length
);
1299 memset(m_extendedHdr
+ length
, 0, rounded
- length
);
1300 m_parent_o_stream
->Write(m_extendedHdr
, rounded
);
1301 m_tarsize
+= rounded
;
1306 // if don't have extended headers just report error
1307 if (!m_badfit
.empty()) {
1309 wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
1310 m_badfit
.c_str(), entry
.GetName().c_str());
1314 m_hdr
->Write(*m_parent_o_stream
);
1315 m_tarsize
+= TAR_BLOCKSIZE
;
1316 m_lasterror
= m_parent_o_stream
->GetLastError();
1321 wxString
wxTarOutputStream::PaxHeaderPath(const wxString
& format
,
1322 const wxString
& path
)
1324 wxString d
= path
.BeforeLast(_T('/'));
1325 wxString f
= path
.AfterLast(_T('/'));
1331 ret
.reserve(format
.length() + path
.length() + 16);
1337 end
= format
.find('%', begin
);
1338 if (end
== wxString::npos
|| end
+ 1 >= format
.length())
1340 ret
<< format
.substr(begin
, end
- begin
);
1341 switch ( format
[end
+ 1].GetValue() ) {
1342 case 'd': ret
<< d
; break;
1343 case 'f': ret
<< f
; break;
1344 case 'p': ret
<< wxGetProcessId(); break;
1345 case '%': ret
<< _T("%"); break;
1350 ret
<< format
.substr(begin
);
1355 bool wxTarOutputStream::ModifyHeader()
1357 wxFileOffset originalPos
= wxInvalidOffset
;
1358 wxFileOffset sizePos
= wxInvalidOffset
;
1360 if (!m_large
&& m_headpos
!= wxInvalidOffset
1361 && m_parent_o_stream
->IsSeekable())
1364 originalPos
= m_parent_o_stream
->TellO();
1365 if (originalPos
!= wxInvalidOffset
)
1367 m_parent_o_stream
->SeekO(m_headpos
+ m_hdr
->Offset(TAR_SIZE
));
1370 if (sizePos
== wxInvalidOffset
|| !m_hdr
->SetOctal(TAR_SIZE
, m_pos
)) {
1371 wxLogError(_("incorrect size given for tar entry"));
1372 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1376 m_chksum
+= m_hdr
->SumField(TAR_SIZE
);
1377 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1378 wxFileOffset sumPos
= m_headpos
+ m_hdr
->Offset(TAR_CHKSUM
);
1381 m_hdr
->WriteField(*m_parent_o_stream
, TAR_SIZE
) &&
1382 m_parent_o_stream
->SeekO(sumPos
) == sumPos
&&
1383 m_hdr
->WriteField(*m_parent_o_stream
, TAR_CHKSUM
) &&
1384 m_parent_o_stream
->SeekO(originalPos
) == originalPos
;
1387 void wxTarOutputStream::SetHeaderPath(const wxString
& name
)
1389 if (!m_hdr
->SetPath(name
, GetConv()) || (m_pax
&& !name
.IsAscii()))
1390 SetExtendedHeader(_T("path"), name
);
1393 bool wxTarOutputStream::SetHeaderNumber(int id
, wxTarNumber n
)
1395 if (m_hdr
->SetOctal(id
, n
)) {
1398 SetExtendedHeader(m_hdr
->Name(id
), wxLongLong(n
).ToString());
1403 void wxTarOutputStream::SetHeaderString(int id
, const wxString
& str
)
1405 strncpy(m_hdr
->Get(id
), str
.mb_str(GetConv()), m_hdr
->Len(id
));
1406 if (str
.length() > m_hdr
->Len(id
))
1407 SetExtendedHeader(m_hdr
->Name(id
), str
);
1410 void wxTarOutputStream::SetHeaderDate(const wxString
& key
,
1411 const wxDateTime
& datetime
)
1413 wxLongLong ll
= datetime
.IsValid() ? datetime
.GetValue() : wxLongLong(0);
1414 wxLongLong secs
= ll
/ 1000L;
1416 if (key
!= _T("mtime")
1417 || !m_hdr
->SetOctal(TAR_MTIME
, wxTarNumber(secs
.GetValue()))
1418 || secs
<= 0 || secs
>= 0x7fffffff)
1421 if (ll
>= LONG_MIN
&& ll
<= LONG_MAX
) {
1422 str
.Printf(_T("%g"), ll
.ToLong() / 1000.0);
1424 str
= ll
.ToString();
1425 str
.insert(str
.end() - 3, '.');
1427 SetExtendedHeader(key
, str
);
1431 void wxTarOutputStream::SetExtendedHeader(const wxString
& key
,
1432 const wxString
& value
)
1436 const wxCharBuffer utf_key
= key
.utf8_str();
1437 const wxCharBuffer utf_value
= value
.utf8_str();
1439 const wxWX2WCbuf wide_key
= key
.wc_str(GetConv());
1440 const wxCharBuffer utf_key
= wxConvUTF8
.cWC2MB(wide_key
);
1442 const wxWX2WCbuf wide_value
= value
.wc_str(GetConv());
1443 const wxCharBuffer utf_value
= wxConvUTF8
.cWC2MB(wide_value
);
1444 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
1446 // a small buffer to format the length field in
1448 // length of "99<space><key>=<value>\n"
1449 unsigned long length
= strlen(utf_value
) + strlen(utf_key
) + 5;
1450 sprintf(buf
, "%lu", length
);
1451 // the length includes itself
1452 size_t lenlen
= strlen(buf
);
1454 length
+= lenlen
- 2;
1455 sprintf(buf
, "%lu", length
);
1456 if (strlen(buf
) > lenlen
)
1457 sprintf(buf
, "%lu", ++length
);
1460 // reallocate m_extendedHdr if it's not big enough
1461 if (m_extendedSize
< length
) {
1462 size_t rounded
= RoundUpSize(length
);
1463 m_extendedSize
<<= 1;
1464 if (rounded
> m_extendedSize
)
1465 m_extendedSize
= rounded
;
1466 char *oldHdr
= m_extendedHdr
;
1467 m_extendedHdr
= new char[m_extendedSize
];
1469 strcpy(m_extendedHdr
, oldHdr
);
1476 // append the new record
1477 char *append
= strchr(m_extendedHdr
, 0);
1478 sprintf(append
, "%s %s=%s\012", buf
,
1479 (const char*)utf_key
, (const char*)utf_value
);
1482 // if not pax then make a list of fields to report as errors
1483 if (!m_badfit
.empty())
1484 m_badfit
+= _T(", ");
1489 void wxTarOutputStream::Sync()
1491 m_parent_o_stream
->Sync();
1494 wxFileOffset
wxTarOutputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
1497 wxLogError(_("tar entry not open"));
1498 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1500 if (!IsOk() || m_datapos
== wxInvalidOffset
)
1501 return wxInvalidOffset
;
1504 case wxFromStart
: break;
1505 case wxFromCurrent
: pos
+= m_pos
; break;
1506 case wxFromEnd
: pos
+= m_maxpos
; break;
1509 if (pos
< 0 || m_parent_o_stream
->SeekO(m_datapos
+ pos
) == wxInvalidOffset
)
1510 return wxInvalidOffset
;
1516 size_t wxTarOutputStream::OnSysWrite(const void *buffer
, size_t size
)
1519 wxLogError(_("tar entry not open"));
1520 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1522 if (!IsOk() || !size
)
1525 size_t lastwrite
= m_parent_o_stream
->Write(buffer
, size
).LastWrite();
1527 if (m_pos
> m_maxpos
)
1530 if (lastwrite
!= size
)
1531 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1536 #endif // wxUSE_TARSTREAM