1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/tarstrm.cpp
3 // Purpose: Streams for Tar files
4 // Author: Mike Wetherell
5 // Copyright: (c) 2004 Mike Wetherell
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
18 #include "wx/tarstrm.h"
26 #include "wx/buffer.h"
27 #include "wx/datetime.h"
28 #include "wx/scopedptr.h"
29 #include "wx/filename.h"
30 #include "wx/thread.h"
40 /////////////////////////////////////////////////////////////////////////////
77 TYPE_OLDTAR
, // fields after TAR_LINKNAME are invalid
78 TYPE_GNUTAR
, // all fields except TAR_PREFIX are valid
79 TYPE_USTAR
// all fields are valid
83 static const char *USTAR_MAGIC
= "ustar";
84 static const char *USTAR_VERSION
= "00";
85 static const char *GNU_MAGIC
= "ustar ";
86 static const char *GNU_VERION
= " ";
88 IMPLEMENT_DYNAMIC_CLASS(wxTarEntry
, wxArchiveEntry
)
89 IMPLEMENT_DYNAMIC_CLASS(wxTarClassFactory
, wxArchiveClassFactory
)
92 /////////////////////////////////////////////////////////////////////////////
95 static wxTarClassFactory g_wxTarClassFactory
;
97 wxTarClassFactory::wxTarClassFactory()
99 if (this == &g_wxTarClassFactory
)
103 const wxChar
* const *
104 wxTarClassFactory::GetProtocols(wxStreamProtocolType type
) const
106 static const wxChar
*protocols
[] = { wxT("tar"), NULL
};
107 static const wxChar
*mimetypes
[] = { wxT("application/x-tar"), NULL
};
108 static const wxChar
*fileexts
[] = { wxT(".tar"), NULL
};
109 static const wxChar
*empty
[] = { NULL
};
112 case wxSTREAM_PROTOCOL
: return protocols
;
113 case wxSTREAM_MIMETYPE
: return mimetypes
;
114 case wxSTREAM_FILEEXT
: return fileexts
;
115 default: return empty
;
120 /////////////////////////////////////////////////////////////////////////////
123 typedef wxFileOffset wxTarNumber
;
125 struct wxTarField
{ const wxChar
*name
; int pos
; };
127 class wxTarHeaderBlock
131 { memset(data
, 0, sizeof(data
)); }
132 wxTarHeaderBlock(const wxTarHeaderBlock
& hb
)
133 { memcpy(data
, hb
.data
, sizeof(data
)); }
135 bool Read(wxInputStream
& in
);
136 bool Write(wxOutputStream
& out
);
137 inline bool WriteField(wxOutputStream
& out
, int id
);
139 bool IsAllZeros() const;
140 wxUint32
Sum(bool SignedSum
= false);
141 wxUint32
SumField(int id
);
143 char *Get(int id
) { return data
+ fields
[id
].pos
+ id
; }
144 static size_t Len(int id
) { return fields
[id
+ 1].pos
- fields
[id
].pos
; }
145 static const wxChar
*Name(int id
) { return fields
[id
].name
; }
146 static size_t Offset(int id
) { return fields
[id
].pos
; }
148 bool SetOctal(int id
, wxTarNumber n
);
149 wxTarNumber
GetOctal(int id
);
150 bool SetPath(const wxString
& name
, wxMBConv
& conv
);
153 char data
[TAR_BLOCKSIZE
+ TAR_NUMFIELDS
];
154 static const wxTarField fields
[];
158 wxDEFINE_SCOPED_PTR_TYPE(wxTarHeaderBlock
)
160 // A table giving the field names and offsets in a tar header block
161 const wxTarField
wxTarHeaderBlock::fields
[] =
163 { wxT("name"), 0 }, // 100
164 { wxT("mode"), 100 }, // 8
165 { wxT("uid"), 108 }, // 8
166 { wxT("gid"), 116 }, // 8
167 { wxT("size"), 124 }, // 12
168 { wxT("mtime"), 136 }, // 12
169 { wxT("chksum"), 148 }, // 8
170 { wxT("typeflag"), 156 }, // 1
171 { wxT("linkname"), 157 }, // 100
172 { wxT("magic"), 257 }, // 6
173 { wxT("version"), 263 }, // 2
174 { wxT("uname"), 265 }, // 32
175 { wxT("gname"), 297 }, // 32
176 { wxT("devmajor"), 329 }, // 8
177 { wxT("devminor"), 337 }, // 8
178 { wxT("prefix"), 345 }, // 155
179 { wxT("unused"), 500 }, // 12
180 { NULL
, TAR_BLOCKSIZE
}
183 void wxTarHeaderBlock::check()
186 wxCOMPILE_TIME_ASSERT(
187 WXSIZEOF(fields
) == TAR_NUMFIELDS
+ 1,
188 Wrong_number_of_elements_in_fields_table
193 bool wxTarHeaderBlock::IsAllZeros() const
195 const char *p
= data
;
196 for (size_t i
= 0; i
< sizeof(data
); i
++)
202 wxUint32
wxTarHeaderBlock::Sum(bool SignedSum
/*=false*/)
204 // the chksum field itself should be blanks during the calculation
205 memset(Get(TAR_CHKSUM
), ' ', Len(TAR_CHKSUM
));
206 const char *p
= data
;
210 for (size_t i
= 0; i
< sizeof(data
); i
++)
211 n
+= (signed char)p
[i
];
213 for (size_t i
= 0; i
< sizeof(data
); i
++)
214 n
+= (unsigned char)p
[i
];
219 wxUint32
wxTarHeaderBlock::SumField(int id
)
221 unsigned char *p
= (unsigned char*)Get(id
);
222 unsigned char *q
= p
+ Len(id
);
231 bool wxTarHeaderBlock::Read(wxInputStream
& in
)
235 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
236 ok
= in
.Read(Get(id
), Len(id
)).LastRead() == Len(id
);
241 bool wxTarHeaderBlock::Write(wxOutputStream
& out
)
245 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
246 ok
= WriteField(out
, id
);
251 inline bool wxTarHeaderBlock::WriteField(wxOutputStream
& out
, int id
)
253 return out
.Write(Get(id
), Len(id
)).LastWrite() == Len(id
);
256 wxTarNumber
wxTarHeaderBlock::GetOctal(int id
)
259 const char *p
= Get(id
);
262 while (*p
>= '0' && *p
< '8')
263 n
= (n
<< 3) | (*p
++ - '0');
267 bool wxTarHeaderBlock::SetOctal(int id
, wxTarNumber n
)
269 // set an octal field, return true if the number fits
270 char *field
= Get(id
);
271 char *p
= field
+ Len(id
);
274 *--p
= char('0' + (n
& 7));
280 bool wxTarHeaderBlock::SetPath(const wxString
& name
, wxMBConv
& conv
)
282 bool badconv
= false;
285 wxCharBuffer nameBuf
= name
.mb_str(conv
);
287 // if the conversion fails make an approximation
290 size_t len
= name
.length();
291 wxCharBuffer
approx(len
);
292 for (size_t i
= 0; i
< len
; i
++)
295 approx
.data()[i
] = c
& ~0x7F ? '_' : c
;
300 const char *mbName
= nameBuf
;
302 const char *mbName
= name
.c_str();
307 bool notGoingToFit
= false;
308 size_t len
= strlen(mbName
);
309 size_t maxname
= Len(TAR_NAME
);
310 size_t maxprefix
= Len(TAR_PREFIX
);
315 fits
= i
< maxprefix
&& len
- i
<= maxname
;
318 const char *p
= strchr(mbName
+ i
, '/');
320 nexti
= p
- mbName
+ 1;
321 if (!p
|| nexti
- 1 > maxprefix
)
322 notGoingToFit
= true;
325 if (fits
|| notGoingToFit
) {
326 strncpy(Get(TAR_NAME
), mbName
+ i
, maxname
);
328 strncpy(Get(TAR_PREFIX
), mbName
, i
- 1);
335 return fits
&& !badconv
;
339 /////////////////////////////////////////////////////////////////////////////
342 static wxFileOffset
RoundUpSize(wxFileOffset size
, int factor
= 1)
344 wxFileOffset chunk
= TAR_BLOCKSIZE
* factor
;
345 return ((size
+ chunk
- 1) / chunk
) * chunk
;
350 static wxString
wxTarUserName(int uid
)
354 #ifdef HAVE_GETPWUID_R
355 #if defined HAVE_SYSCONF && defined _SC_GETPW_R_SIZE_MAX
356 long pwsize
= sysconf(_SC_GETPW_R_SIZE_MAX
);
357 size_t bufsize(wxMin(wxMax(1024l, pwsize
), 32768l));
359 size_t bufsize
= 1024;
361 wxCharBuffer
buf(bufsize
);
364 memset(&pw
, 0, sizeof(pw
));
365 if (getpwuid_r(uid
, &pw
, buf
.data(), bufsize
, &ppw
) == 0 && pw
.pw_name
)
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 memset(&gr
, 0, sizeof(gr
));
388 if (getgrgid_r(gid
, &gr
, buf
.data(), bufsize
, &pgr
) == 0 && gr
.gr_name
)
389 return wxString(gr
.gr_name
, wxConvLibc
);
391 if ((pgr
= getgrgid(gid
)) != NULL
)
392 return wxString(pgr
->gr_name
, wxConvLibc
);
399 // Cache the user and group names since getting them can be expensive,
400 // get both names and ids at the same time.
405 ~wxTarUser() { delete [] uname
; delete [] gname
; }
414 wxTarUser::wxTarUser()
419 wxString usr
= wxTarUserName(uid
);
420 wxString grp
= wxTarGroupName(gid
);
424 wxString usr
= wxGetUserId();
425 wxString grp
= _("unknown");
428 uname
= new wxChar
[usr
.length() + 1];
429 wxStrcpy(uname
, usr
.c_str());
431 gname
= new wxChar
[grp
.length() + 1];
432 wxStrcpy(gname
, grp
.c_str());
435 static const wxTarUser
& wxGetTarUser()
438 static wxCriticalSection cs
;
439 wxCriticalSectionLocker
lock(cs
);
445 // ignore the size field for entry types 3, 4, 5 and 6
447 static inline wxFileOffset
GetDataSize(const wxTarEntry
& entry
)
449 switch (entry
.GetTypeFlag()) {
456 return entry
.GetSize();
461 /////////////////////////////////////////////////////////////////////////////
463 // Holds all the meta-data for a file in the tar
465 wxTarEntry::wxTarEntry(const wxString
& name
/*=wxEmptyString*/,
466 const wxDateTime
& dt
/*=wxDateTime::Now()*/,
467 wxFileOffset size
/*=0*/)
470 m_UserId(wxGetTarUser().uid
),
471 m_GroupId(wxGetTarUser().gid
),
473 m_Offset(wxInvalidOffset
),
475 m_TypeFlag(wxTAR_REGTYPE
),
476 m_UserName(wxGetTarUser().uname
),
477 m_GroupName(wxGetTarUser().gname
),
485 wxTarEntry::~wxTarEntry()
489 wxTarEntry::wxTarEntry(const wxTarEntry
& e
)
493 m_IsModeSet(e
.m_IsModeSet
),
494 m_UserId(e
.m_UserId
),
495 m_GroupId(e
.m_GroupId
),
497 m_Offset(e
.m_Offset
),
498 m_ModifyTime(e
.m_ModifyTime
),
499 m_AccessTime(e
.m_AccessTime
),
500 m_CreateTime(e
.m_CreateTime
),
501 m_TypeFlag(e
.m_TypeFlag
),
502 m_LinkName(e
.m_LinkName
),
503 m_UserName(e
.m_UserName
),
504 m_GroupName(e
.m_GroupName
),
505 m_DevMajor(e
.m_DevMajor
),
506 m_DevMinor(e
.m_DevMinor
)
510 wxTarEntry
& wxTarEntry::operator=(const wxTarEntry
& e
)
515 m_IsModeSet
= e
.m_IsModeSet
;
516 m_UserId
= e
.m_UserId
;
517 m_GroupId
= e
.m_GroupId
;
519 m_Offset
= e
.m_Offset
;
520 m_ModifyTime
= e
.m_ModifyTime
;
521 m_AccessTime
= e
.m_AccessTime
;
522 m_CreateTime
= e
.m_CreateTime
;
523 m_TypeFlag
= e
.m_TypeFlag
;
524 m_LinkName
= e
.m_LinkName
;
525 m_UserName
= e
.m_UserName
;
526 m_GroupName
= e
.m_GroupName
;
527 m_DevMajor
= e
.m_DevMajor
;
528 m_DevMinor
= e
.m_DevMinor
;
533 wxString
wxTarEntry::GetName(wxPathFormat format
/*=wxPATH_NATIVE*/) const
535 bool isDir
= IsDir() && !m_Name
.empty();
537 // optimisations for common (and easy) cases
538 switch (wxFileName::GetFormat(format
)) {
541 wxString
name(isDir
? m_Name
+ wxT("\\") : m_Name
);
542 for (size_t i
= 0; i
< name
.length(); i
++)
543 if (name
[i
] == wxT('/'))
549 return isDir
? m_Name
+ wxT("/") : m_Name
;
558 fn
.AssignDir(m_Name
, wxPATH_UNIX
);
560 fn
.Assign(m_Name
, wxPATH_UNIX
);
562 return fn
.GetFullPath(format
);
565 void wxTarEntry::SetName(const wxString
& name
, wxPathFormat format
)
568 m_Name
= GetInternalName(name
, format
, &isDir
);
572 // Static - Internally tars and zips use forward slashes for the path
573 // separator, absolute paths aren't allowed, and directory names have a
574 // trailing slash. This function converts a path into this internal format,
575 // but without a trailing slash for a directory.
577 wxString
wxTarEntry::GetInternalName(const wxString
& name
,
578 wxPathFormat format
/*=wxPATH_NATIVE*/,
579 bool *pIsDir
/*=NULL*/)
583 if (wxFileName::GetFormat(format
) != wxPATH_UNIX
)
584 internal
= wxFileName(name
, format
).GetFullPath(wxPATH_UNIX
);
588 bool isDir
= !internal
.empty() && internal
.Last() == '/';
592 internal
.erase(internal
.length() - 1);
594 while (!internal
.empty() && *internal
.begin() == '/')
595 internal
.erase(0, 1);
596 while (!internal
.empty() && internal
.compare(0, 2, wxT("./")) == 0)
597 internal
.erase(0, 2);
598 if (internal
== wxT(".") || internal
== wxT(".."))
599 internal
= wxEmptyString
;
604 bool wxTarEntry::IsDir() const
606 return m_TypeFlag
== wxTAR_DIRTYPE
;
609 void wxTarEntry::SetIsDir(bool isDir
)
612 m_TypeFlag
= wxTAR_DIRTYPE
;
613 else if (m_TypeFlag
== wxTAR_DIRTYPE
)
614 m_TypeFlag
= wxTAR_REGTYPE
;
617 void wxTarEntry::SetIsReadOnly(bool isReadOnly
)
625 int wxTarEntry::GetMode() const
627 if (m_IsModeSet
|| !IsDir())
630 return m_Mode
| 0111;
634 void wxTarEntry::SetMode(int mode
)
636 m_Mode
= mode
& 07777;
641 /////////////////////////////////////////////////////////////////////////////
644 wxDECLARE_SCOPED_PTR(wxTarEntry
, wxTarEntryPtr_
)
645 wxDEFINE_SCOPED_PTR (wxTarEntry
, wxTarEntryPtr_
)
647 wxTarInputStream::wxTarInputStream(wxInputStream
& stream
,
648 wxMBConv
& conv
/*=wxConvLocal*/)
649 : wxArchiveInputStream(stream
, conv
)
654 wxTarInputStream::wxTarInputStream(wxInputStream
*stream
,
655 wxMBConv
& conv
/*=wxConvLocal*/)
656 : wxArchiveInputStream(stream
, conv
)
661 void wxTarInputStream::Init()
663 m_pos
= wxInvalidOffset
;
665 m_size
= wxInvalidOffset
;
666 m_sumType
= SUM_UNKNOWN
;
667 m_tarType
= TYPE_USTAR
;
668 m_hdr
= new wxTarHeaderBlock
;
670 m_GlobalHeaderRecs
= NULL
;
671 m_lasterror
= m_parent_i_stream
->GetLastError();
674 wxTarInputStream::~wxTarInputStream()
678 delete m_GlobalHeaderRecs
;
681 wxTarEntry
*wxTarInputStream::GetNextEntry()
683 m_lasterror
= ReadHeaders();
688 wxTarEntryPtr_
entry(new wxTarEntry
);
690 entry
->SetMode(GetHeaderNumber(TAR_MODE
));
691 entry
->SetUserId(GetHeaderNumber(TAR_UID
));
692 entry
->SetGroupId(GetHeaderNumber(TAR_UID
));
693 entry
->SetSize(GetHeaderNumber(TAR_SIZE
));
695 entry
->SetOffset(m_offset
);
697 entry
->SetDateTime(GetHeaderDate(wxT("mtime")));
698 entry
->SetAccessTime(GetHeaderDate(wxT("atime")));
699 entry
->SetCreateTime(GetHeaderDate(wxT("ctime")));
701 entry
->SetTypeFlag(*m_hdr
->Get(TAR_TYPEFLAG
));
702 bool isDir
= entry
->IsDir();
704 entry
->SetLinkName(GetHeaderString(TAR_LINKNAME
));
706 if (m_tarType
!= TYPE_OLDTAR
) {
707 entry
->SetUserName(GetHeaderString(TAR_UNAME
));
708 entry
->SetGroupName(GetHeaderString(TAR_GNAME
));
710 entry
->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR
));
711 entry
->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR
));
714 entry
->SetName(GetHeaderPath(), wxPATH_UNIX
);
719 m_HeaderRecs
->clear();
721 m_size
= GetDataSize(*entry
);
724 return entry
.release();
727 bool wxTarInputStream::OpenEntry(wxTarEntry
& entry
)
729 wxFileOffset offset
= entry
.GetOffset();
731 if (GetLastError() != wxSTREAM_READ_ERROR
732 && m_parent_i_stream
->IsSeekable()
733 && m_parent_i_stream
->SeekI(offset
) == offset
)
736 m_size
= GetDataSize(entry
);
738 m_lasterror
= wxSTREAM_NO_ERROR
;
741 m_lasterror
= wxSTREAM_READ_ERROR
;
746 bool wxTarInputStream::OpenEntry(wxArchiveEntry
& entry
)
748 wxTarEntry
*tarEntry
= wxStaticCast(&entry
, wxTarEntry
);
749 return tarEntry
? OpenEntry(*tarEntry
) : false;
752 bool wxTarInputStream::CloseEntry()
754 if (m_lasterror
== wxSTREAM_READ_ERROR
)
759 wxFileOffset size
= RoundUpSize(m_size
);
760 wxFileOffset remainder
= size
- m_pos
;
762 if (remainder
&& m_parent_i_stream
->IsSeekable()) {
764 if (m_parent_i_stream
->SeekI(remainder
, wxFromCurrent
)
770 const int BUFSIZE
= 8192;
771 wxCharBuffer
buf(BUFSIZE
);
773 while (remainder
> 0 && m_parent_i_stream
->IsOk())
774 remainder
-= m_parent_i_stream
->Read(
775 buf
.data(), wxMin(BUFSIZE
, remainder
)).LastRead();
778 m_pos
= wxInvalidOffset
;
780 m_lasterror
= m_parent_i_stream
->GetLastError();
785 wxStreamError
wxTarInputStream::ReadHeaders()
788 return wxSTREAM_READ_ERROR
;
793 m_hdr
->Read(*m_parent_i_stream
);
794 if (m_parent_i_stream
->Eof())
796 wxLogError(_("incomplete header block in tar"));
798 if (!*m_parent_i_stream
)
799 return wxSTREAM_READ_ERROR
;
800 m_offset
+= TAR_BLOCKSIZE
;
802 // an all-zero header marks the end of the tar
803 if (m_hdr
->IsAllZeros())
806 // the checksum is supposed to be the unsigned sum of the header bytes,
807 // but there have been versions of tar that used the signed sum, so
808 // accept that too, but only if used throughout.
809 wxUint32 chksum
= m_hdr
->GetOctal(TAR_CHKSUM
);
812 if (m_sumType
!= SUM_SIGNED
) {
813 ok
= chksum
== m_hdr
->Sum();
814 if (m_sumType
== SUM_UNKNOWN
)
815 m_sumType
= ok
? SUM_UNSIGNED
: SUM_SIGNED
;
817 if (m_sumType
== SUM_SIGNED
)
818 ok
= chksum
== m_hdr
->Sum(true);
820 wxLogError(_("checksum failure reading tar header block"));
821 return wxSTREAM_READ_ERROR
;
824 if (strcmp(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
) == 0)
825 m_tarType
= TYPE_USTAR
;
826 else if (strcmp(m_hdr
->Get(TAR_MAGIC
), GNU_MAGIC
) == 0 &&
827 strcmp(m_hdr
->Get(TAR_VERSION
), GNU_VERION
) == 0)
828 m_tarType
= TYPE_GNUTAR
;
830 m_tarType
= TYPE_OLDTAR
;
832 if (m_tarType
!= TYPE_USTAR
)
835 switch (*m_hdr
->Get(TAR_TYPEFLAG
)) {
836 case 'g': ReadExtendedHeader(m_GlobalHeaderRecs
); break;
837 case 'x': ReadExtendedHeader(m_HeaderRecs
); break;
838 default: done
= true;
842 return wxSTREAM_NO_ERROR
;
845 wxString
wxTarInputStream::GetExtendedHeader(const wxString
& key
) const
847 wxTarHeaderRecords::iterator it
;
849 // look at normal extended header records first
851 it
= m_HeaderRecs
->find(key
);
852 if (it
!= m_HeaderRecs
->end())
853 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
856 // if not found, look at the global header records
857 if (m_GlobalHeaderRecs
) {
858 it
= m_GlobalHeaderRecs
->find(key
);
859 if (it
!= m_GlobalHeaderRecs
->end())
860 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
863 return wxEmptyString
;
866 wxString
wxTarInputStream::GetHeaderPath() const
870 if ((path
= GetExtendedHeader(wxT("path"))) != wxEmptyString
)
873 path
= wxString(m_hdr
->Get(TAR_NAME
), GetConv());
874 if (m_tarType
!= TYPE_USTAR
)
877 const char *prefix
= m_hdr
->Get(TAR_PREFIX
);
878 return *prefix
? wxString(prefix
, GetConv()) + wxT("/") + path
: path
;
881 wxDateTime
wxTarInputStream::GetHeaderDate(const wxString
& key
) const
885 // try extended header, stored as decimal seconds since the epoch
886 if ((value
= GetExtendedHeader(key
)) != wxEmptyString
) {
888 ll
.Assign(wxAtof(value
) * 1000.0);
892 if (key
== wxT("mtime"))
893 return wxLongLong(m_hdr
->GetOctal(TAR_MTIME
)) * 1000L;
898 wxTarNumber
wxTarInputStream::GetHeaderNumber(int id
) const
902 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
) {
904 wxString::const_iterator p
= value
.begin();
905 while (*p
== ' ' && p
!= value
.end())
908 n
= n
* 10 + (*p
++ - '0');
911 return m_hdr
->GetOctal(id
);
915 wxString
wxTarInputStream::GetHeaderString(int id
) const
919 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
)
922 return wxString(m_hdr
->Get(id
), GetConv());
925 // An extended header consists of one or more records, each constructed:
926 // "%d %s=%s\n", <length>, <keyword>, <value>
927 // <length> is the byte length, <keyword> and <value> are UTF-8
929 bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords
*& recs
)
932 recs
= new wxTarHeaderRecords
;
934 // round length up to a whole number of blocks
935 size_t len
= m_hdr
->GetOctal(TAR_SIZE
);
936 size_t size
= RoundUpSize(len
);
938 // read in the whole header since it should be small
939 wxCharBuffer
buf(size
);
940 size_t lastread
= m_parent_i_stream
->Read(buf
.data(), size
).LastRead();
944 m_offset
+= lastread
;
946 size_t recPos
, recSize
;
949 for (recPos
= 0; recPos
< len
&& ok
; recPos
+= recSize
) {
950 char *pRec
= buf
.data() + recPos
;
953 // read the record size (byte count in ascii decimal)
955 while (isdigit((unsigned char) *p
))
956 recSize
= recSize
* 10 + *p
++ - '0';
959 if (recPos
+ recSize
> len
)
961 if (recSize
< p
- pRec
+ (size_t)3 || *p
!= ' '
962 || pRec
[recSize
- 1] != '\012') {
967 // replace the final '\n' with a nul, to terminate value
968 pRec
[recSize
- 1] = 0;
969 // the key is here, following the space
972 // look forward for the '=', the value follows
973 while (*p
&& *p
!= '=')
979 // replace the '=' with a nul, to terminate the key
982 wxString
key(wxConvUTF8
.cMB2WC(pKey
), GetConv());
983 wxString
value(wxConvUTF8
.cMB2WC(p
), GetConv());
985 // an empty value unsets a previously given value
989 (*recs
)[key
] = value
;
992 if (!ok
|| recPos
< len
|| size
!= lastread
) {
993 wxLogWarning(_("invalid data in extended tar header"));
1000 wxFileOffset
wxTarInputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
1003 wxLogError(_("tar entry not open"));
1004 m_lasterror
= wxSTREAM_READ_ERROR
;
1007 return wxInvalidOffset
;
1010 case wxFromStart
: break;
1011 case wxFromCurrent
: pos
+= m_pos
; break;
1012 case wxFromEnd
: pos
+= m_size
; break;
1015 if (pos
< 0 || m_parent_i_stream
->SeekI(m_offset
+ pos
) == wxInvalidOffset
)
1016 return wxInvalidOffset
;
1022 size_t wxTarInputStream::OnSysRead(void *buffer
, size_t size
)
1025 wxLogError(_("tar entry not open"));
1026 m_lasterror
= wxSTREAM_READ_ERROR
;
1028 if (!IsOk() || !size
)
1031 if (m_pos
>= m_size
)
1033 else if (m_pos
+ size
> m_size
+ (size_t)0)
1034 size
= m_size
- m_pos
;
1036 size_t lastread
= m_parent_i_stream
->Read(buffer
, size
).LastRead();
1039 if (m_pos
>= m_size
) {
1040 m_lasterror
= wxSTREAM_EOF
;
1041 } else if (!m_parent_i_stream
->IsOk()) {
1042 // any other error will have been reported by the underlying stream
1043 if (m_parent_i_stream
->Eof())
1045 wxLogError(_("unexpected end of file"));
1047 m_lasterror
= wxSTREAM_READ_ERROR
;
1054 /////////////////////////////////////////////////////////////////////////////
1057 wxTarOutputStream::wxTarOutputStream(wxOutputStream
& stream
,
1058 wxTarFormat format
/*=wxTAR_PAX*/,
1059 wxMBConv
& conv
/*=wxConvLocal*/)
1060 : wxArchiveOutputStream(stream
, conv
)
1065 wxTarOutputStream::wxTarOutputStream(wxOutputStream
*stream
,
1066 wxTarFormat format
/*=wxTAR_PAX*/,
1067 wxMBConv
& conv
/*=wxConvLocal*/)
1068 : wxArchiveOutputStream(stream
, conv
)
1073 void wxTarOutputStream::Init(wxTarFormat format
)
1075 m_pos
= wxInvalidOffset
;
1076 m_maxpos
= wxInvalidOffset
;
1077 m_size
= wxInvalidOffset
;
1078 m_headpos
= wxInvalidOffset
;
1079 m_datapos
= wxInvalidOffset
;
1080 m_tarstart
= wxInvalidOffset
;
1082 m_pax
= format
== wxTAR_PAX
;
1083 m_BlockingFactor
= m_pax
? 10 : 20;
1086 m_hdr
= new wxTarHeaderBlock
;
1088 m_extendedHdr
= NULL
;
1090 m_lasterror
= m_parent_o_stream
->GetLastError();
1091 m_endrecWritten
= false;
1094 wxTarOutputStream::~wxTarOutputStream()
1099 delete [] m_extendedHdr
;
1102 bool wxTarOutputStream::PutNextEntry(wxTarEntry
*entry
)
1104 wxTarEntryPtr_
e(entry
);
1111 m_tarstart
= m_parent_o_stream
->TellO();
1114 if (m_tarstart
!= wxInvalidOffset
)
1115 m_headpos
= m_tarstart
+ m_tarsize
;
1117 if (WriteHeaders(*e
)) {
1120 m_size
= GetDataSize(*e
);
1121 if (m_tarstart
!= wxInvalidOffset
)
1122 m_datapos
= m_tarstart
+ m_tarsize
;
1124 // types that are not allowd any data
1125 const char nodata
[] = {
1126 wxTAR_LNKTYPE
, wxTAR_SYMTYPE
, wxTAR_CHRTYPE
, wxTAR_BLKTYPE
,
1127 wxTAR_DIRTYPE
, wxTAR_FIFOTYPE
, 0
1129 int typeflag
= e
->GetTypeFlag();
1131 // pax does now allow data for wxTAR_LNKTYPE
1132 if (!m_pax
|| typeflag
!= wxTAR_LNKTYPE
)
1133 if (strchr(nodata
, typeflag
) != NULL
)
1140 bool wxTarOutputStream::PutNextEntry(const wxString
& name
,
1141 const wxDateTime
& dt
,
1144 return PutNextEntry(new wxTarEntry(name
, dt
, size
));
1147 bool wxTarOutputStream::PutNextDirEntry(const wxString
& name
,
1148 const wxDateTime
& dt
)
1150 wxTarEntry
*entry
= new wxTarEntry(name
, dt
);
1152 return PutNextEntry(entry
);
1155 bool wxTarOutputStream::PutNextEntry(wxArchiveEntry
*entry
)
1157 wxTarEntry
*tarEntry
= wxStaticCast(entry
, wxTarEntry
);
1160 return PutNextEntry(tarEntry
);
1163 bool wxTarOutputStream::CopyEntry(wxTarEntry
*entry
,
1164 wxTarInputStream
& inputStream
)
1166 if (PutNextEntry(entry
))
1168 return IsOk() && inputStream
.Eof();
1171 bool wxTarOutputStream::CopyEntry(wxArchiveEntry
*entry
,
1172 wxArchiveInputStream
& inputStream
)
1174 if (PutNextEntry(entry
))
1176 return IsOk() && inputStream
.Eof();
1179 bool wxTarOutputStream::CloseEntry()
1184 if (m_pos
< m_maxpos
) {
1185 wxASSERT(m_parent_o_stream
->IsSeekable());
1186 m_parent_o_stream
->SeekO(m_datapos
+ m_maxpos
);
1187 m_lasterror
= m_parent_o_stream
->GetLastError();
1192 wxFileOffset size
= RoundUpSize(m_pos
);
1194 memset(m_hdr
, 0, size
- m_pos
);
1195 m_parent_o_stream
->Write(m_hdr
, size
- m_pos
);
1196 m_lasterror
= m_parent_o_stream
->GetLastError();
1201 if (IsOk() && m_pos
!= m_size
)
1204 m_pos
= wxInvalidOffset
;
1205 m_maxpos
= wxInvalidOffset
;
1206 m_size
= wxInvalidOffset
;
1207 m_headpos
= wxInvalidOffset
;
1208 m_datapos
= wxInvalidOffset
;
1213 bool wxTarOutputStream::Close()
1215 if (!CloseEntry() || (m_tarsize
== 0 && m_endrecWritten
))
1218 memset(m_hdr
, 0, sizeof(*m_hdr
));
1219 int count
= (RoundUpSize(m_tarsize
+ 2 * TAR_BLOCKSIZE
, m_BlockingFactor
)
1220 - m_tarsize
) / TAR_BLOCKSIZE
;
1222 m_parent_o_stream
->Write(m_hdr
, TAR_BLOCKSIZE
);
1225 m_tarstart
= wxInvalidOffset
;
1226 m_lasterror
= m_parent_o_stream
->GetLastError();
1227 m_endrecWritten
= true;
1231 bool wxTarOutputStream::WriteHeaders(wxTarEntry
& entry
)
1233 memset(m_hdr
, 0, sizeof(*m_hdr
));
1235 SetHeaderPath(entry
.GetName(wxPATH_UNIX
));
1237 SetHeaderNumber(TAR_MODE
, entry
.GetMode());
1238 SetHeaderNumber(TAR_UID
, entry
.GetUserId());
1239 SetHeaderNumber(TAR_GID
, entry
.GetGroupId());
1241 if (entry
.GetSize() == wxInvalidOffset
)
1243 m_large
= !SetHeaderNumber(TAR_SIZE
, entry
.GetSize());
1245 SetHeaderDate(wxT("mtime"), entry
.GetDateTime());
1246 if (entry
.GetAccessTime().IsValid())
1247 SetHeaderDate(wxT("atime"), entry
.GetAccessTime());
1248 if (entry
.GetCreateTime().IsValid())
1249 SetHeaderDate(wxT("ctime"), entry
.GetCreateTime());
1251 *m_hdr
->Get(TAR_TYPEFLAG
) = char(entry
.GetTypeFlag());
1253 strcpy(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1254 strcpy(m_hdr
->Get(TAR_VERSION
), USTAR_VERSION
);
1256 SetHeaderString(TAR_LINKNAME
, entry
.GetLinkName());
1257 SetHeaderString(TAR_UNAME
, entry
.GetUserName());
1258 SetHeaderString(TAR_GNAME
, entry
.GetGroupName());
1260 if (~entry
.GetDevMajor())
1261 SetHeaderNumber(TAR_DEVMAJOR
, entry
.GetDevMajor());
1262 if (~entry
.GetDevMinor())
1263 SetHeaderNumber(TAR_DEVMINOR
, entry
.GetDevMinor());
1265 m_chksum
= m_hdr
->Sum();
1266 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1268 m_chksum
-= m_hdr
->SumField(TAR_SIZE
);
1270 // The main header is now fully prepared so we know what extended headers
1271 // (if any) will be needed. Output any extended headers before writing
1273 if (m_extendedHdr
&& *m_extendedHdr
) {
1275 // the extended headers are written to the tar as a file entry,
1276 // so prepare a regular header block for the pseudo-file.
1278 m_hdr2
= new wxTarHeaderBlock
;
1279 memset(m_hdr2
, 0, sizeof(*m_hdr2
));
1281 // an old tar that doesn't understand extended headers will
1282 // extract it as a file, so give these fields reasonable values
1283 // so that the user will have access to read and remove it.
1284 m_hdr2
->SetPath(PaxHeaderPath(wxT("%d/PaxHeaders.%p/%f"),
1285 entry
.GetName(wxPATH_UNIX
)), GetConv());
1286 m_hdr2
->SetOctal(TAR_MODE
, 0600);
1287 strcpy(m_hdr2
->Get(TAR_UID
), m_hdr
->Get(TAR_UID
));
1288 strcpy(m_hdr2
->Get(TAR_GID
), m_hdr
->Get(TAR_GID
));
1289 size_t length
= strlen(m_extendedHdr
);
1290 m_hdr2
->SetOctal(TAR_SIZE
, length
);
1291 strcpy(m_hdr2
->Get(TAR_MTIME
), m_hdr
->Get(TAR_MTIME
));
1292 *m_hdr2
->Get(TAR_TYPEFLAG
) = 'x';
1293 strcpy(m_hdr2
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1294 strcpy(m_hdr2
->Get(TAR_VERSION
), USTAR_VERSION
);
1295 strcpy(m_hdr2
->Get(TAR_UNAME
), m_hdr
->Get(TAR_UNAME
));
1296 strcpy(m_hdr2
->Get(TAR_GNAME
), m_hdr
->Get(TAR_GNAME
));
1298 m_hdr2
->SetOctal(TAR_CHKSUM
, m_hdr2
->Sum());
1300 m_hdr2
->Write(*m_parent_o_stream
);
1301 m_tarsize
+= TAR_BLOCKSIZE
;
1303 size_t rounded
= RoundUpSize(length
);
1304 memset(m_extendedHdr
+ length
, 0, rounded
- length
);
1305 m_parent_o_stream
->Write(m_extendedHdr
, rounded
);
1306 m_tarsize
+= rounded
;
1310 // update m_headpos which is used to seek back to fix up the file
1311 // length if it is not known in advance
1312 if (m_tarstart
!= wxInvalidOffset
)
1313 m_headpos
= m_tarstart
+ m_tarsize
;
1316 // if don't have extended headers just report error
1317 if (!m_badfit
.empty()) {
1319 wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
1320 m_badfit
.c_str(), entry
.GetName().c_str());
1324 m_hdr
->Write(*m_parent_o_stream
);
1325 m_tarsize
+= TAR_BLOCKSIZE
;
1326 m_lasterror
= m_parent_o_stream
->GetLastError();
1331 wxString
wxTarOutputStream::PaxHeaderPath(const wxString
& format
,
1332 const wxString
& path
)
1334 wxString d
= path
.BeforeLast(wxT('/'));
1335 wxString f
= path
.AfterLast(wxT('/'));
1341 ret
.reserve(format
.length() + path
.length() + 16);
1347 end
= format
.find('%', begin
);
1348 if (end
== wxString::npos
|| end
+ 1 >= format
.length())
1350 ret
<< format
.substr(begin
, end
- begin
);
1351 switch ( format
[end
+ 1].GetValue() ) {
1352 case 'd': ret
<< d
; break;
1353 case 'f': ret
<< f
; break;
1354 case 'p': ret
<< wxGetProcessId(); break;
1355 case '%': ret
<< wxT("%"); break;
1360 ret
<< format
.substr(begin
);
1365 bool wxTarOutputStream::ModifyHeader()
1367 wxFileOffset originalPos
= wxInvalidOffset
;
1368 wxFileOffset sizePos
= wxInvalidOffset
;
1370 if (!m_large
&& m_headpos
!= wxInvalidOffset
1371 && m_parent_o_stream
->IsSeekable())
1374 originalPos
= m_parent_o_stream
->TellO();
1375 if (originalPos
!= wxInvalidOffset
)
1377 m_parent_o_stream
->SeekO(m_headpos
+ m_hdr
->Offset(TAR_SIZE
));
1380 if (sizePos
== wxInvalidOffset
|| !m_hdr
->SetOctal(TAR_SIZE
, m_pos
)) {
1381 wxLogError(_("incorrect size given for tar entry"));
1382 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1386 m_chksum
+= m_hdr
->SumField(TAR_SIZE
);
1387 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1388 wxFileOffset sumPos
= m_headpos
+ m_hdr
->Offset(TAR_CHKSUM
);
1391 m_hdr
->WriteField(*m_parent_o_stream
, TAR_SIZE
) &&
1392 m_parent_o_stream
->SeekO(sumPos
) == sumPos
&&
1393 m_hdr
->WriteField(*m_parent_o_stream
, TAR_CHKSUM
) &&
1394 m_parent_o_stream
->SeekO(originalPos
) == originalPos
;
1397 void wxTarOutputStream::SetHeaderPath(const wxString
& name
)
1399 if (!m_hdr
->SetPath(name
, GetConv()) || (m_pax
&& !name
.IsAscii()))
1400 SetExtendedHeader(wxT("path"), name
);
1403 bool wxTarOutputStream::SetHeaderNumber(int id
, wxTarNumber n
)
1405 if (m_hdr
->SetOctal(id
, n
)) {
1408 SetExtendedHeader(m_hdr
->Name(id
), wxLongLong(n
).ToString());
1413 void wxTarOutputStream::SetHeaderString(int id
, const wxString
& str
)
1415 strncpy(m_hdr
->Get(id
), str
.mb_str(GetConv()), m_hdr
->Len(id
));
1416 if (str
.length() > m_hdr
->Len(id
))
1417 SetExtendedHeader(m_hdr
->Name(id
), str
);
1420 void wxTarOutputStream::SetHeaderDate(const wxString
& key
,
1421 const wxDateTime
& datetime
)
1423 wxLongLong ll
= datetime
.IsValid() ? datetime
.GetValue() : wxLongLong(0);
1424 wxLongLong secs
= ll
/ 1000L;
1426 if (key
!= wxT("mtime")
1427 || !m_hdr
->SetOctal(TAR_MTIME
, wxTarNumber(secs
.GetValue()))
1428 || secs
<= 0 || secs
>= 0x7fffffff)
1431 if (ll
>= LONG_MIN
&& ll
<= LONG_MAX
) {
1432 str
.Printf(wxT("%g"), ll
.ToLong() / 1000.0);
1434 str
= ll
.ToString();
1435 str
.insert(str
.end() - 3, '.');
1437 SetExtendedHeader(key
, str
);
1441 void wxTarOutputStream::SetExtendedHeader(const wxString
& key
,
1442 const wxString
& value
)
1446 const wxCharBuffer utf_key
= key
.utf8_str();
1447 const wxCharBuffer utf_value
= value
.utf8_str();
1449 const wxWX2WCbuf wide_key
= key
.wc_str(GetConv());
1450 const wxCharBuffer utf_key
= wxConvUTF8
.cWC2MB(wide_key
);
1452 const wxWX2WCbuf wide_value
= value
.wc_str(GetConv());
1453 const wxCharBuffer utf_value
= wxConvUTF8
.cWC2MB(wide_value
);
1454 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
1456 // a small buffer to format the length field in
1458 // length of "99<space><key>=<value>\n"
1459 unsigned long length
= strlen(utf_value
) + strlen(utf_key
) + 5;
1460 sprintf(buf
, "%lu", length
);
1461 // the length includes itself
1462 size_t lenlen
= strlen(buf
);
1464 length
+= lenlen
- 2;
1465 sprintf(buf
, "%lu", length
);
1466 if (strlen(buf
) > lenlen
)
1467 sprintf(buf
, "%lu", ++length
);
1470 // reallocate m_extendedHdr if it's not big enough
1471 if (m_extendedSize
< length
) {
1472 size_t rounded
= RoundUpSize(length
);
1473 m_extendedSize
<<= 1;
1474 if (rounded
> m_extendedSize
)
1475 m_extendedSize
= rounded
;
1476 char *oldHdr
= m_extendedHdr
;
1477 m_extendedHdr
= new char[m_extendedSize
];
1479 strcpy(m_extendedHdr
, oldHdr
);
1486 // append the new record
1487 char *append
= strchr(m_extendedHdr
, 0);
1488 sprintf(append
, "%s %s=%s\012", buf
,
1489 (const char*)utf_key
, (const char*)utf_value
);
1492 // if not pax then make a list of fields to report as errors
1493 if (!m_badfit
.empty())
1494 m_badfit
+= wxT(", ");
1499 void wxTarOutputStream::Sync()
1501 m_parent_o_stream
->Sync();
1504 wxFileOffset
wxTarOutputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
1507 wxLogError(_("tar entry not open"));
1508 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1510 if (!IsOk() || m_datapos
== wxInvalidOffset
)
1511 return wxInvalidOffset
;
1514 case wxFromStart
: break;
1515 case wxFromCurrent
: pos
+= m_pos
; break;
1516 case wxFromEnd
: pos
+= m_maxpos
; break;
1519 if (pos
< 0 || m_parent_o_stream
->SeekO(m_datapos
+ pos
) == wxInvalidOffset
)
1520 return wxInvalidOffset
;
1526 size_t wxTarOutputStream::OnSysWrite(const void *buffer
, size_t size
)
1529 wxLogError(_("tar entry not open"));
1530 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1532 if (!IsOk() || !size
)
1535 size_t lastwrite
= m_parent_o_stream
->Write(buffer
, size
).LastWrite();
1537 if (m_pos
> m_maxpos
)
1540 if (lastwrite
!= size
)
1541 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1546 #endif // wxUSE_TARSTREAM