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"
39 /////////////////////////////////////////////////////////////////////////////
76 TYPE_OLDTAR
, // fields after TAR_LINKNAME are invalid
77 TYPE_GNUTAR
, // all fields except TAR_PREFIX are valid
78 TYPE_USTAR
// all fields are valid
82 static const char *USTAR_MAGIC
= "ustar";
83 static const char *USTAR_VERSION
= "00";
84 static const char *GNU_MAGIC
= "ustar ";
85 static const char *GNU_VERION
= " ";
87 IMPLEMENT_DYNAMIC_CLASS(wxTarEntry
, wxArchiveEntry
)
88 IMPLEMENT_DYNAMIC_CLASS(wxTarClassFactory
, wxArchiveClassFactory
)
91 /////////////////////////////////////////////////////////////////////////////
94 static wxTarClassFactory g_wxTarClassFactory
;
96 wxTarClassFactory::wxTarClassFactory()
98 if (this == &g_wxTarClassFactory
)
102 const wxChar
* const *
103 wxTarClassFactory::GetProtocols(wxStreamProtocolType type
) const
105 static const wxChar
*protocols
[] = { _T("tar"), NULL
};
106 static const wxChar
*mimetypes
[] = { _T("application/x-tar"), NULL
};
107 static const wxChar
*fileexts
[] = { _T(".tar"), NULL
};
108 static const wxChar
*empty
[] = { NULL
};
111 case wxSTREAM_PROTOCOL
: return protocols
;
112 case wxSTREAM_MIMETYPE
: return mimetypes
;
113 case wxSTREAM_FILEEXTENSION
: return fileexts
;
114 default: return empty
;
119 /////////////////////////////////////////////////////////////////////////////
122 typedef wxFileOffset wxTarNumber
;
124 struct wxTarField
{ const wxChar
*name
; int pos
; };
126 class wxTarHeaderBlock
130 { memset(data
, 0, sizeof(data
)); }
131 wxTarHeaderBlock(const wxTarHeaderBlock
& hb
)
132 { memcpy(data
, hb
.data
, sizeof(data
)); }
134 bool Read(wxInputStream
& in
);
135 bool Write(wxOutputStream
& out
);
136 inline bool WriteField(wxOutputStream
& out
, int id
);
138 bool IsAllZeros() const;
139 wxUint32
Sum(bool SignedSum
= false);
140 wxUint32
SumField(int id
);
142 char *Get(int id
) { return data
+ fields
[id
].pos
+ id
; }
143 static size_t Len(int id
) { return fields
[id
+ 1].pos
- fields
[id
].pos
; }
144 static const wxChar
*Name(int id
) { return fields
[id
].name
; }
145 static size_t Offset(int id
) { return fields
[id
].pos
; }
147 bool SetOctal(int id
, wxTarNumber n
);
148 wxTarNumber
GetOctal(int id
);
149 bool SetPath(const wxString
& name
, wxMBConv
& conv
);
152 char data
[TAR_BLOCKSIZE
+ TAR_NUMFIELDS
];
153 static const wxTarField fields
[];
157 wxDEFINE_SCOPED_PTR_TYPE(wxTarHeaderBlock
);
159 // A table giving the field names and offsets in a tar header block
160 const wxTarField
wxTarHeaderBlock::fields
[] =
162 { _T("name"), 0 }, // 100
163 { _T("mode"), 100 }, // 8
164 { _T("uid"), 108 }, // 8
165 { _T("gid"), 116 }, // 8
166 { _T("size"), 124 }, // 12
167 { _T("mtime"), 136 }, // 12
168 { _T("chksum"), 148 }, // 8
169 { _T("typeflag"), 156 }, // 1
170 { _T("linkname"), 157 }, // 100
171 { _T("magic"), 257 }, // 6
172 { _T("version"), 263 }, // 2
173 { _T("uname"), 265 }, // 32
174 { _T("gname"), 297 }, // 32
175 { _T("devmajor"), 329 }, // 8
176 { _T("devminor"), 337 }, // 8
177 { _T("prefix"), 345 }, // 155
178 { _T("unused"), 500 }, // 12
179 { NULL
, TAR_BLOCKSIZE
}
182 void wxTarHeaderBlock::check()
185 wxCOMPILE_TIME_ASSERT(
186 WXSIZEOF(fields
) == TAR_NUMFIELDS
+ 1,
187 Wrong_number_of_elements_in_fields_table
192 bool wxTarHeaderBlock::IsAllZeros() const
194 const char *p
= data
;
195 for (size_t i
= 0; i
< sizeof(data
); i
++)
201 wxUint32
wxTarHeaderBlock::Sum(bool SignedSum
/*=false*/)
203 // the chksum field itself should be blanks during the calculation
204 memset(Get(TAR_CHKSUM
), ' ', Len(TAR_CHKSUM
));
205 const char *p
= data
;
209 for (size_t i
= 0; i
< sizeof(data
); i
++)
210 n
+= (signed char)p
[i
];
212 for (size_t i
= 0; i
< sizeof(data
); i
++)
213 n
+= (unsigned char)p
[i
];
218 wxUint32
wxTarHeaderBlock::SumField(int id
)
220 unsigned char *p
= (unsigned char*)Get(id
);
221 unsigned char *q
= p
+ Len(id
);
230 bool wxTarHeaderBlock::Read(wxInputStream
& in
)
234 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
235 ok
= in
.Read(Get(id
), Len(id
)).LastRead() == Len(id
);
240 bool wxTarHeaderBlock::Write(wxOutputStream
& out
)
244 for (int id
= 0; id
< TAR_NUMFIELDS
&& ok
; id
++)
245 ok
= WriteField(out
, id
);
250 bool wxTarHeaderBlock::WriteField(wxOutputStream
& out
, int id
)
252 return out
.Write(Get(id
), Len(id
)).LastWrite() == Len(id
);
255 wxTarNumber
wxTarHeaderBlock::GetOctal(int id
)
258 const char *p
= Get(id
);
261 while (*p
>= '0' && *p
< '8')
262 n
= (n
<< 3) | (*p
++ - '0');
266 bool wxTarHeaderBlock::SetOctal(int id
, wxTarNumber n
)
268 // set an octal field, return true if the number fits
269 char *field
= Get(id
);
270 char *p
= field
+ Len(id
);
273 *--p
= '0' + (n
& 7);
279 bool wxTarHeaderBlock::SetPath(const wxString
& name
, wxMBConv
& conv
)
281 bool badconv
= false;
284 wxCharBuffer nameBuf
= name
.mb_str(conv
);
286 // if the conversion fails make an approximation
289 size_t len
= name
.length();
290 wxCharBuffer
approx(len
);
291 for (size_t i
= 0; i
< len
; i
++)
292 approx
.data()[i
] = name
[i
] & ~0x7F ? '_' : name
[i
];
296 const char *mbName
= nameBuf
;
298 const char *mbName
= name
.c_str();
303 bool notGoingToFit
= false;
304 size_t len
= strlen(mbName
);
305 size_t maxname
= Len(TAR_NAME
);
306 size_t maxprefix
= Len(TAR_PREFIX
);
311 fits
= i
< maxprefix
&& len
- i
<= maxname
;
314 const char *p
= strchr(mbName
+ i
, '/');
316 nexti
= p
- mbName
+ 1;
317 if (!p
|| nexti
- 1 > maxprefix
)
318 notGoingToFit
= true;
321 if (fits
|| notGoingToFit
) {
322 strncpy(Get(TAR_NAME
), mbName
+ i
, maxname
);
324 strncpy(Get(TAR_PREFIX
), mbName
, i
- 1);
331 return fits
&& !badconv
;
335 /////////////////////////////////////////////////////////////////////////////
338 static wxFileOffset
RoundUpSize(wxFileOffset size
, int factor
= 1)
340 wxFileOffset chunk
= TAR_BLOCKSIZE
* factor
;
341 return ((size
+ chunk
- 1) / chunk
) * chunk
;
344 static wxString
GroupName()
348 if ((gr
= getgrgid(getgid())) != NULL
)
349 return wxString(gr
->gr_name
, wxConvLibc
);
354 static inline int UserId()
363 static inline int GroupId()
372 // ignore the size field for entry types 3, 4, 5 and 6
374 static inline wxFileOffset
GetDataSize(const wxTarEntry
& entry
)
376 switch (entry
.GetTypeFlag()) {
383 return entry
.GetSize();
388 /////////////////////////////////////////////////////////////////////////////
390 // Holds all the meta-data for a file in the tar
392 wxTarEntry::wxTarEntry(const wxString
& name
/*=wxEmptyString*/,
393 const wxDateTime
& dt
/*=wxDateTime::Now()*/,
394 wxFileOffset size
/*=0*/)
398 m_GroupId(GroupId()),
400 m_Offset(wxInvalidOffset
),
402 m_TypeFlag(wxTAR_REGTYPE
),
403 m_UserName(wxGetUserId()),
404 m_GroupName(GroupName()),
412 wxTarEntry::~wxTarEntry()
416 wxTarEntry::wxTarEntry(const wxTarEntry
& e
)
419 m_IsModeSet(e
.m_IsModeSet
),
420 m_UserId(e
.m_UserId
),
421 m_GroupId(e
.m_GroupId
),
423 m_Offset(e
.m_Offset
),
424 m_ModifyTime(e
.m_ModifyTime
),
425 m_AccessTime(e
.m_AccessTime
),
426 m_CreateTime(e
.m_CreateTime
),
427 m_TypeFlag(e
.m_TypeFlag
),
428 m_LinkName(e
.m_LinkName
),
429 m_UserName(e
.m_UserName
),
430 m_GroupName(e
.m_GroupName
),
431 m_DevMajor(e
.m_DevMajor
),
432 m_DevMinor(e
.m_DevMinor
)
436 wxTarEntry
& wxTarEntry::operator=(const wxTarEntry
& e
)
441 m_IsModeSet
= e
.m_IsModeSet
;
442 m_UserId
= e
.m_UserId
;
443 m_GroupId
= e
.m_GroupId
;
445 m_Offset
= e
.m_Offset
;
446 m_ModifyTime
= e
.m_ModifyTime
;
447 m_AccessTime
= e
.m_AccessTime
;
448 m_CreateTime
= e
.m_CreateTime
;
449 m_TypeFlag
= e
.m_TypeFlag
;
450 m_LinkName
= e
.m_LinkName
;
451 m_UserName
= e
.m_UserName
;
452 m_GroupName
= e
.m_GroupName
;
453 m_DevMajor
= e
.m_DevMajor
;
454 m_DevMinor
= e
.m_DevMinor
;
459 wxString
wxTarEntry::GetName(wxPathFormat format
/*=wxPATH_NATIVE*/) const
461 bool isDir
= IsDir() && !m_Name
.empty();
463 // optimisations for common (and easy) cases
464 switch (wxFileName::GetFormat(format
)) {
467 wxString
name(isDir
? m_Name
+ _T("\\") : m_Name
);
468 for (size_t i
= 0; i
< name
.length(); i
++)
469 if (name
[i
] == _T('/'))
475 return isDir
? m_Name
+ _T("/") : m_Name
;
484 fn
.AssignDir(m_Name
, wxPATH_UNIX
);
486 fn
.Assign(m_Name
, wxPATH_UNIX
);
488 return fn
.GetFullPath(format
);
491 void wxTarEntry::SetName(const wxString
& name
, wxPathFormat format
)
494 m_Name
= GetInternalName(name
, format
, &isDir
);
498 // Static - Internally tars and zips use forward slashes for the path
499 // separator, absolute paths aren't allowed, and directory names have a
500 // trailing slash. This function converts a path into this internal format,
501 // but without a trailing slash for a directory.
503 wxString
wxTarEntry::GetInternalName(const wxString
& name
,
504 wxPathFormat format
/*=wxPATH_NATIVE*/,
505 bool *pIsDir
/*=NULL*/)
509 if (wxFileName::GetFormat(format
) != wxPATH_UNIX
)
510 internal
= wxFileName(name
, format
).GetFullPath(wxPATH_UNIX
);
514 bool isDir
= !internal
.empty() && internal
.Last() == '/';
518 internal
.erase(internal
.length() - 1);
520 while (!internal
.empty() && *internal
.begin() == '/')
521 internal
.erase(0, 1);
522 while (!internal
.empty() && internal
.compare(0, 2, _T("./")) == 0)
523 internal
.erase(0, 2);
524 if (internal
== _T(".") || internal
== _T(".."))
525 internal
= wxEmptyString
;
530 bool wxTarEntry::IsDir() const
532 return m_TypeFlag
- wxTAR_DIRTYPE
== 0;
535 void wxTarEntry::SetIsDir(bool isDir
)
538 m_TypeFlag
= wxTAR_DIRTYPE
;
539 else if (m_TypeFlag
- wxTAR_DIRTYPE
== 0)
540 m_TypeFlag
= wxTAR_REGTYPE
;
543 void wxTarEntry::SetIsReadOnly(bool isReadOnly
)
551 int wxTarEntry::GetMode() const
553 if (m_IsModeSet
|| !IsDir())
556 return m_Mode
| 0111;
560 void wxTarEntry::SetMode(int mode
)
562 m_Mode
= mode
& 07777;
567 /////////////////////////////////////////////////////////////////////////////
570 wxDECLARE_SCOPED_PTR(wxTarEntry
, wxTarEntryPtr_
)
571 wxDEFINE_SCOPED_PTR (wxTarEntry
, wxTarEntryPtr_
)
573 wxTarInputStream::wxTarInputStream(wxInputStream
& stream
,
574 wxMBConv
& conv
/*=wxConvLocal*/)
575 : wxArchiveInputStream(stream
, conv
)
580 wxTarInputStream::wxTarInputStream(wxInputStream
*stream
,
581 wxMBConv
& conv
/*=wxConvLocal*/)
582 : wxArchiveInputStream(stream
, conv
)
587 void wxTarInputStream::Init()
589 m_pos
= wxInvalidOffset
;
591 m_size
= wxInvalidOffset
;
592 m_sumType
= SUM_UNKNOWN
;
593 m_tarType
= TYPE_USTAR
;
594 m_hdr
= new wxTarHeaderBlock
;
596 m_GlobalHeaderRecs
= NULL
;
597 m_lasterror
= m_parent_i_stream
->GetLastError();
600 wxTarInputStream::~wxTarInputStream()
604 delete m_GlobalHeaderRecs
;
607 wxTarEntry
*wxTarInputStream::GetNextEntry()
609 m_lasterror
= ReadHeaders();
614 wxTarEntryPtr_
entry(new wxTarEntry
);
616 entry
->SetMode(GetHeaderNumber(TAR_MODE
));
617 entry
->SetUserId(GetHeaderNumber(TAR_UID
));
618 entry
->SetGroupId(GetHeaderNumber(TAR_UID
));
619 entry
->SetSize(GetHeaderNumber(TAR_SIZE
));
621 entry
->SetOffset(m_offset
);
623 entry
->SetDateTime(GetHeaderDate(_T("mtime")));
624 entry
->SetAccessTime(GetHeaderDate(_T("atime")));
625 entry
->SetCreateTime(GetHeaderDate(_T("ctime")));
627 entry
->SetTypeFlag(*m_hdr
->Get(TAR_TYPEFLAG
));
628 bool isDir
= entry
->IsDir();
630 entry
->SetLinkName(GetHeaderString(TAR_LINKNAME
));
632 if (m_tarType
!= TYPE_OLDTAR
) {
633 entry
->SetUserName(GetHeaderString(TAR_UNAME
));
634 entry
->SetGroupName(GetHeaderString(TAR_GNAME
));
636 entry
->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR
));
637 entry
->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR
));
640 entry
->SetName(GetHeaderPath(), wxPATH_UNIX
);
645 m_HeaderRecs
->clear();
647 m_size
= GetDataSize(*entry
);
650 return entry
.release();
653 bool wxTarInputStream::OpenEntry(wxTarEntry
& entry
)
655 wxFileOffset offset
= entry
.GetOffset();
657 if (GetLastError() != wxSTREAM_READ_ERROR
658 && m_parent_i_stream
->IsSeekable()
659 && m_parent_i_stream
->SeekI(offset
) == offset
)
662 m_size
= GetDataSize(entry
);
664 m_lasterror
= wxSTREAM_NO_ERROR
;
667 m_lasterror
= wxSTREAM_READ_ERROR
;
672 bool wxTarInputStream::OpenEntry(wxArchiveEntry
& entry
)
674 wxTarEntry
*tarEntry
= wxStaticCast(&entry
, wxTarEntry
);
675 return tarEntry
? OpenEntry(*tarEntry
) : false;
678 bool wxTarInputStream::CloseEntry()
680 if (m_lasterror
== wxSTREAM_READ_ERROR
)
685 wxFileOffset size
= RoundUpSize(m_size
);
686 wxFileOffset remainder
= size
- m_pos
;
688 if (remainder
&& m_parent_i_stream
->IsSeekable()) {
690 if (m_parent_i_stream
->SeekI(remainder
, wxFromCurrent
)
696 const int BUFSIZE
= 8192;
697 wxCharBuffer
buf(BUFSIZE
);
699 while (remainder
> 0 && m_parent_i_stream
->IsOk())
700 remainder
-= m_parent_i_stream
->Read(
701 buf
.data(), wxMin(BUFSIZE
, remainder
)).LastRead();
704 m_pos
= wxInvalidOffset
;
706 m_lasterror
= m_parent_i_stream
->GetLastError();
711 wxStreamError
wxTarInputStream::ReadHeaders()
714 return wxSTREAM_READ_ERROR
;
719 m_hdr
->Read(*m_parent_i_stream
);
720 if (m_parent_i_stream
->Eof())
721 wxLogError(_("incomplete header block in tar"));
722 if (!*m_parent_i_stream
)
723 return wxSTREAM_READ_ERROR
;
724 m_offset
+= TAR_BLOCKSIZE
;
726 // an all-zero header marks the end of the tar
727 if (m_hdr
->IsAllZeros())
730 // the checksum is supposed to be the unsigned sum of the header bytes,
731 // but there have been versions of tar that used the signed sum, so
732 // accept that too, but only if used throughout.
733 wxUint32 chksum
= m_hdr
->GetOctal(TAR_CHKSUM
);
736 if (m_sumType
!= SUM_SIGNED
) {
737 ok
= chksum
== m_hdr
->Sum();
738 if (m_sumType
== SUM_UNKNOWN
)
739 m_sumType
= ok
? SUM_UNSIGNED
: SUM_SIGNED
;
741 if (m_sumType
== SUM_SIGNED
)
742 ok
= chksum
== m_hdr
->Sum(true);
744 wxLogError(_("checksum failure reading tar header block"));
745 return wxSTREAM_READ_ERROR
;
748 if (strcmp(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
) == 0)
749 m_tarType
= TYPE_USTAR
;
750 else if (strcmp(m_hdr
->Get(TAR_MAGIC
), GNU_MAGIC
) == 0 &&
751 strcmp(m_hdr
->Get(TAR_VERSION
), GNU_VERION
) == 0)
752 m_tarType
= TYPE_GNUTAR
;
754 m_tarType
= TYPE_OLDTAR
;
756 if (m_tarType
!= TYPE_USTAR
)
759 switch (*m_hdr
->Get(TAR_TYPEFLAG
)) {
760 case 'g': ReadExtendedHeader(m_GlobalHeaderRecs
); break;
761 case 'x': ReadExtendedHeader(m_HeaderRecs
); break;
762 default: done
= true;
766 return wxSTREAM_NO_ERROR
;
769 wxString
wxTarInputStream::GetExtendedHeader(const wxString
& key
) const
771 wxTarHeaderRecords::iterator it
;
773 // look at normal extended header records first
775 it
= m_HeaderRecs
->find(key
);
776 if (it
!= m_HeaderRecs
->end())
777 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
780 // if not found, look at the global header records
781 if (m_GlobalHeaderRecs
) {
782 it
= m_GlobalHeaderRecs
->find(key
);
783 if (it
!= m_GlobalHeaderRecs
->end())
784 return wxString(it
->second
.wc_str(wxConvUTF8
), GetConv());
787 return wxEmptyString
;
790 wxString
wxTarInputStream::GetHeaderPath() const
794 if ((path
= GetExtendedHeader(_T("path"))) != wxEmptyString
)
797 path
= wxString(m_hdr
->Get(TAR_NAME
), GetConv());
798 if (m_tarType
!= TYPE_USTAR
)
801 const char *prefix
= m_hdr
->Get(TAR_PREFIX
);
802 return *prefix
? wxString(prefix
, GetConv()) + _T("/") + path
: path
;
805 wxDateTime
wxTarInputStream::GetHeaderDate(const wxString
& key
) const
809 // try extended header, stored as decimal seconds since the epoch
810 if ((value
= GetExtendedHeader(key
)) != wxEmptyString
) {
812 ll
.Assign(wxAtof(value
) * 1000.0);
816 if (key
== _T("mtime"))
817 return wxLongLong(m_hdr
->GetOctal(TAR_MTIME
)) * 1000L;
822 wxTarNumber
wxTarInputStream::GetHeaderNumber(int id
) const
826 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
) {
828 const wxChar
*p
= value
;
832 n
= n
* 10 + (*p
++ - '0');
835 return m_hdr
->GetOctal(id
);
839 wxString
wxTarInputStream::GetHeaderString(int id
) const
843 if ((value
= GetExtendedHeader(m_hdr
->Name(id
))) != wxEmptyString
)
846 return wxString(m_hdr
->Get(id
), GetConv());
849 // An extended header consists of one or more records, each constructed:
850 // "%d %s=%s\n", <length>, <keyword>, <value>
851 // <length> is the byte length, <keyword> and <value> are UTF-8
853 bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords
*& recs
)
856 recs
= new wxTarHeaderRecords
;
858 // round length up to a whole number of blocks
859 size_t len
= m_hdr
->GetOctal(TAR_SIZE
);
860 size_t size
= RoundUpSize(len
);
862 // read in the whole header since it should be small
863 wxCharBuffer
buf(size
);
864 size_t lastread
= m_parent_i_stream
->Read(buf
.data(), size
).LastRead();
868 m_offset
+= lastread
;
870 size_t recPos
, recSize
;
873 for (recPos
= 0; recPos
< len
; recPos
+= recSize
) {
874 char *pRec
= buf
.data() + recPos
;
877 // read the record size (byte count in ascii decimal)
880 recSize
= recSize
* 10 + *p
++ - '0';
883 if (recPos
+ recSize
> len
)
885 if (recSize
< p
- pRec
+ (size_t)3 || *p
!= ' '
886 || pRec
[recSize
- 1] != '\012') {
891 // replace the final '\n' with a nul, to terminate value
892 pRec
[recSize
- 1] = 0;
893 // the key is here, following the space
896 // look forward for the '=', the value follows
897 while (*p
&& *p
!= '=')
903 // replace the '=' with a nul, to terminate the key
906 wxString
key(wxConvUTF8
.cMB2WC(pKey
), GetConv());
907 wxString
value(wxConvUTF8
.cMB2WC(p
), GetConv());
909 // an empty value unsets a previously given value
913 (*recs
)[key
] = value
;
916 if (!ok
|| recPos
< len
|| size
!= lastread
) {
917 wxLogWarning(_("invalid data in extended tar header"));
924 wxFileOffset
wxTarInputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
927 wxLogError(_("tar entry not open"));
928 m_lasterror
= wxSTREAM_READ_ERROR
;
931 return wxInvalidOffset
;
934 case wxFromStart
: break;
935 case wxFromCurrent
: pos
+= m_pos
; break;
936 case wxFromEnd
: pos
+= m_size
; break;
939 if (pos
< 0 || m_parent_i_stream
->SeekI(m_offset
+ pos
) == wxInvalidOffset
)
940 return wxInvalidOffset
;
946 size_t wxTarInputStream::OnSysRead(void *buffer
, size_t size
)
949 wxLogError(_("tar entry not open"));
950 m_lasterror
= wxSTREAM_READ_ERROR
;
952 if (!IsOk() || !size
)
957 else if (m_pos
+ size
> m_size
+ (size_t)0)
958 size
= m_size
- m_pos
;
960 size_t lastread
= m_parent_i_stream
->Read(buffer
, size
).LastRead();
963 if (m_pos
>= m_size
) {
964 m_lasterror
= wxSTREAM_EOF
;
965 } else if (!m_parent_i_stream
->IsOk()) {
966 // any other error will have been reported by the underlying stream
967 if (m_parent_i_stream
->Eof())
968 wxLogError(_("unexpected end of file"));
969 m_lasterror
= wxSTREAM_READ_ERROR
;
976 /////////////////////////////////////////////////////////////////////////////
979 wxTarOutputStream::wxTarOutputStream(wxOutputStream
& stream
,
980 wxTarFormat format
/*=wxTAR_PAX*/,
981 wxMBConv
& conv
/*=wxConvLocal*/)
982 : wxArchiveOutputStream(stream
, conv
)
987 wxTarOutputStream::wxTarOutputStream(wxOutputStream
*stream
,
988 wxTarFormat format
/*=wxTAR_PAX*/,
989 wxMBConv
& conv
/*=wxConvLocal*/)
990 : wxArchiveOutputStream(stream
, conv
)
995 void wxTarOutputStream::Init(wxTarFormat format
)
997 m_pos
= wxInvalidOffset
;
998 m_maxpos
= wxInvalidOffset
;
999 m_size
= wxInvalidOffset
;
1000 m_headpos
= wxInvalidOffset
;
1001 m_datapos
= wxInvalidOffset
;
1002 m_tarstart
= wxInvalidOffset
;
1004 m_pax
= format
== wxTAR_PAX
;
1005 m_BlockingFactor
= m_pax
? 10 : 20;
1008 m_hdr
= new wxTarHeaderBlock
;
1010 m_extendedHdr
= NULL
;
1012 m_lasterror
= m_parent_o_stream
->GetLastError();
1015 wxTarOutputStream::~wxTarOutputStream()
1021 delete [] m_extendedHdr
;
1024 bool wxTarOutputStream::PutNextEntry(wxTarEntry
*entry
)
1026 wxTarEntryPtr_
e(entry
);
1033 m_tarstart
= m_parent_o_stream
->TellO();
1036 if (m_tarstart
!= wxInvalidOffset
)
1037 m_headpos
= m_tarstart
+ m_tarsize
;
1039 if (WriteHeaders(*e
)) {
1042 m_size
= GetDataSize(*e
);
1043 if (m_tarstart
!= wxInvalidOffset
)
1044 m_datapos
= m_tarstart
+ m_tarsize
;
1046 // types that are not allowd any data
1047 const char nodata
[] = {
1048 wxTAR_LNKTYPE
, wxTAR_SYMTYPE
, wxTAR_CHRTYPE
, wxTAR_BLKTYPE
,
1049 wxTAR_DIRTYPE
, wxTAR_FIFOTYPE
, 0
1051 char typeflag
= e
->GetTypeFlag();
1053 // pax does now allow data for wxTAR_LNKTYPE
1054 if (!m_pax
|| typeflag
- wxTAR_LNKTYPE
!= 0)
1055 if (strchr(nodata
, typeflag
) != NULL
)
1062 bool wxTarOutputStream::PutNextEntry(const wxString
& name
,
1063 const wxDateTime
& dt
,
1066 return PutNextEntry(new wxTarEntry(name
, dt
, size
));
1069 bool wxTarOutputStream::PutNextDirEntry(const wxString
& name
,
1070 const wxDateTime
& dt
)
1072 wxTarEntry
*entry
= new wxTarEntry(name
, dt
);
1074 return PutNextEntry(entry
);
1077 bool wxTarOutputStream::PutNextEntry(wxArchiveEntry
*entry
)
1079 wxTarEntry
*tarEntry
= wxStaticCast(entry
, wxTarEntry
);
1082 return PutNextEntry(tarEntry
);
1085 bool wxTarOutputStream::CopyEntry(wxTarEntry
*entry
,
1086 wxTarInputStream
& inputStream
)
1088 if (PutNextEntry(entry
))
1090 return IsOk() && inputStream
.Eof();
1093 bool wxTarOutputStream::CopyEntry(wxArchiveEntry
*entry
,
1094 wxArchiveInputStream
& inputStream
)
1096 if (PutNextEntry(entry
))
1098 return IsOk() && inputStream
.Eof();
1101 bool wxTarOutputStream::CloseEntry()
1106 if (m_pos
< m_maxpos
) {
1107 wxASSERT(m_parent_o_stream
->IsSeekable());
1108 m_parent_o_stream
->SeekO(m_datapos
+ m_maxpos
);
1109 m_lasterror
= m_parent_o_stream
->GetLastError();
1114 wxFileOffset size
= RoundUpSize(m_pos
);
1116 memset(m_hdr
, 0, size
- m_pos
);
1117 m_parent_o_stream
->Write(m_hdr
, size
- m_pos
);
1118 m_lasterror
= m_parent_o_stream
->GetLastError();
1123 if (IsOk() && m_pos
!= m_size
)
1126 m_pos
= wxInvalidOffset
;
1127 m_maxpos
= wxInvalidOffset
;
1128 m_size
= wxInvalidOffset
;
1129 m_headpos
= wxInvalidOffset
;
1130 m_datapos
= wxInvalidOffset
;
1135 bool wxTarOutputStream::Close()
1140 memset(m_hdr
, 0, sizeof(*m_hdr
));
1141 int count
= (RoundUpSize(m_tarsize
+ 2 * TAR_BLOCKSIZE
, m_BlockingFactor
)
1142 - m_tarsize
) / TAR_BLOCKSIZE
;
1144 m_parent_o_stream
->Write(m_hdr
, TAR_BLOCKSIZE
);
1147 m_tarstart
= wxInvalidOffset
;
1148 m_lasterror
= m_parent_o_stream
->GetLastError();
1152 bool wxTarOutputStream::WriteHeaders(wxTarEntry
& entry
)
1154 memset(m_hdr
, 0, sizeof(*m_hdr
));
1156 SetHeaderPath(entry
.GetName(wxPATH_UNIX
));
1158 SetHeaderNumber(TAR_MODE
, entry
.GetMode());
1159 SetHeaderNumber(TAR_UID
, entry
.GetUserId());
1160 SetHeaderNumber(TAR_GID
, entry
.GetGroupId());
1162 if (entry
.GetSize() == wxInvalidOffset
)
1164 m_large
= SetHeaderNumber(TAR_SIZE
, entry
.GetSize());
1166 SetHeaderDate(_T("mtime"), entry
.GetDateTime());
1167 if (entry
.GetAccessTime().IsValid())
1168 SetHeaderDate(_T("atime"), entry
.GetAccessTime());
1169 if (entry
.GetCreateTime().IsValid())
1170 SetHeaderDate(_T("ctime"), entry
.GetCreateTime());
1172 *m_hdr
->Get(TAR_TYPEFLAG
) = entry
.GetTypeFlag();
1174 strcpy(m_hdr
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1175 strcpy(m_hdr
->Get(TAR_VERSION
), USTAR_VERSION
);
1177 SetHeaderString(TAR_LINKNAME
, entry
.GetLinkName());
1178 SetHeaderString(TAR_UNAME
, entry
.GetUserName());
1179 SetHeaderString(TAR_GNAME
, entry
.GetGroupName());
1181 if (~entry
.GetDevMajor())
1182 SetHeaderNumber(TAR_DEVMAJOR
, entry
.GetDevMajor());
1183 if (~entry
.GetDevMinor())
1184 SetHeaderNumber(TAR_DEVMINOR
, entry
.GetDevMinor());
1186 m_chksum
= m_hdr
->Sum();
1187 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1189 m_chksum
-= m_hdr
->SumField(TAR_SIZE
);
1191 // The main header is now fully prepared so we know what extended headers
1192 // (if any) will be needed. Output any extended headers before writing
1194 if (m_extendedHdr
&& *m_extendedHdr
) {
1196 // the extended headers are written to the tar as a file entry,
1197 // so prepare a regular header block for the pseudo-file.
1199 m_hdr2
= new wxTarHeaderBlock
;
1200 memset(m_hdr2
, 0, sizeof(*m_hdr2
));
1202 // an old tar that doesn't understand extended headers will
1203 // extract it as a file, so give these fields reasonable values
1204 // so that the user will have access to read and remove it.
1205 m_hdr2
->SetPath(PaxHeaderPath(_T("%d/PaxHeaders.%p/%f"),
1206 entry
.GetName(wxPATH_UNIX
)), GetConv());
1207 m_hdr2
->SetOctal(TAR_MODE
, 0600);
1208 strcpy(m_hdr2
->Get(TAR_UID
), m_hdr
->Get(TAR_UID
));
1209 strcpy(m_hdr2
->Get(TAR_GID
), m_hdr
->Get(TAR_GID
));
1210 size_t length
= strlen(m_extendedHdr
);
1211 m_hdr2
->SetOctal(TAR_SIZE
, length
);
1212 strcpy(m_hdr2
->Get(TAR_MTIME
), m_hdr
->Get(TAR_MTIME
));
1213 *m_hdr2
->Get(TAR_TYPEFLAG
) = 'x';
1214 strcpy(m_hdr2
->Get(TAR_MAGIC
), USTAR_MAGIC
);
1215 strcpy(m_hdr2
->Get(TAR_VERSION
), USTAR_VERSION
);
1216 strcpy(m_hdr2
->Get(TAR_UNAME
), m_hdr
->Get(TAR_UNAME
));
1217 strcpy(m_hdr2
->Get(TAR_GNAME
), m_hdr
->Get(TAR_GNAME
));
1219 m_hdr2
->SetOctal(TAR_CHKSUM
, m_hdr2
->Sum());
1221 m_hdr2
->Write(*m_parent_o_stream
);
1222 m_tarsize
+= TAR_BLOCKSIZE
;
1224 size_t rounded
= RoundUpSize(length
);
1225 memset(m_extendedHdr
+ length
, 0, rounded
- length
);
1226 m_parent_o_stream
->Write(m_extendedHdr
, rounded
);
1227 m_tarsize
+= rounded
;
1232 // if don't have extended headers just report error
1233 if (!m_badfit
.empty()) {
1235 wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
1236 m_badfit
.c_str(), entry
.GetName().c_str());
1240 m_hdr
->Write(*m_parent_o_stream
);
1241 m_tarsize
+= TAR_BLOCKSIZE
;
1242 m_lasterror
= m_parent_o_stream
->GetLastError();
1247 wxString
wxTarOutputStream::PaxHeaderPath(const wxString
& format
,
1248 const wxString
& path
)
1250 wxString d
= path
.BeforeLast(_T('/'));
1251 wxString f
= path
.AfterLast(_T('/'));
1257 ret
.reserve(format
.length() + path
.length() + 16);
1263 end
= format
.find('%', begin
);
1264 if (end
== wxString::npos
|| end
+ 1 >= format
.length())
1266 ret
<< format
.substr(begin
, end
- begin
);
1267 switch (format
[end
+ 1]) {
1268 case 'd': ret
<< d
; break;
1269 case 'f': ret
<< f
; break;
1270 case 'p': ret
<< wxGetProcessId(); break;
1271 case '%': ret
<< _T("%"); break;
1276 ret
<< format
.substr(begin
);
1281 bool wxTarOutputStream::ModifyHeader()
1283 wxFileOffset originalPos
= wxInvalidOffset
;
1284 wxFileOffset sizePos
= wxInvalidOffset
;
1286 if (!m_large
&& m_headpos
!= wxInvalidOffset
1287 && m_parent_o_stream
->IsSeekable())
1290 originalPos
= m_parent_o_stream
->TellO();
1291 if (originalPos
!= wxInvalidOffset
)
1293 m_parent_o_stream
->SeekO(m_headpos
+ m_hdr
->Offset(TAR_SIZE
));
1296 if (sizePos
== wxInvalidOffset
|| !m_hdr
->SetOctal(TAR_SIZE
, m_pos
)) {
1297 wxLogError(_("incorrect size given for tar entry"));
1298 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1302 m_chksum
+= m_hdr
->SumField(TAR_SIZE
);
1303 m_hdr
->SetOctal(TAR_CHKSUM
, m_chksum
);
1304 wxFileOffset sumPos
= m_headpos
+ m_hdr
->Offset(TAR_CHKSUM
);
1307 m_hdr
->WriteField(*m_parent_o_stream
, TAR_SIZE
) &&
1308 m_parent_o_stream
->SeekO(sumPos
) == sumPos
&&
1309 m_hdr
->WriteField(*m_parent_o_stream
, TAR_CHKSUM
) &&
1310 m_parent_o_stream
->SeekO(originalPos
) == originalPos
;
1313 void wxTarOutputStream::SetHeaderPath(const wxString
& name
)
1315 if (!m_hdr
->SetPath(name
, GetConv()) || (m_pax
&& !name
.IsAscii()))
1316 SetExtendedHeader(_T("path"), name
);
1319 bool wxTarOutputStream::SetHeaderNumber(int id
, wxTarNumber n
)
1321 if (m_hdr
->SetOctal(id
, n
)) {
1324 SetExtendedHeader(m_hdr
->Name(id
), wxLongLong(n
).ToString());
1329 void wxTarOutputStream::SetHeaderString(int id
, const wxString
& str
)
1331 strncpy(m_hdr
->Get(id
), str
.mb_str(GetConv()), m_hdr
->Len(id
));
1332 if (str
.length() > m_hdr
->Len(id
))
1333 SetExtendedHeader(m_hdr
->Name(id
), str
);
1336 void wxTarOutputStream::SetHeaderDate(const wxString
& key
,
1337 const wxDateTime
& datetime
)
1339 wxLongLong ll
= datetime
.IsValid() ? datetime
.GetValue() : wxLongLong(0);
1340 wxLongLong secs
= ll
/ 1000L;
1342 if (key
!= _T("mtime") || !m_hdr
->SetOctal(TAR_MTIME
, secs
.GetValue())
1343 || secs
<= 0 || secs
>= 0x7fffffff)
1346 if (ll
>= LONG_MIN
&& ll
<= LONG_MAX
) {
1347 str
.Printf(_T("%g"), ll
.ToLong() / 1000.0);
1349 str
= ll
.ToString();
1350 str
.insert(str
.end() - 3, '.');
1352 SetExtendedHeader(key
, str
);
1356 void wxTarOutputStream::SetExtendedHeader(const wxString
& key
,
1357 const wxString
& value
)
1360 const wxWX2WCbuf wide_key
= key
.wc_str(GetConv());
1361 const wxCharBuffer utf_key
= wxConvUTF8
.cWC2MB(wide_key
);
1363 const wxWX2WCbuf wide_value
= value
.wc_str(GetConv());
1364 const wxCharBuffer utf_value
= wxConvUTF8
.cWC2MB(wide_value
);
1366 // a small buffer to format the length field in
1368 // length of "99<space><key>=<value>\n"
1369 unsigned long length
= strlen(utf_value
) + strlen(utf_key
) + 5;
1370 sprintf(buf
, "%lu", length
);
1371 // the length includes itself
1372 size_t lenlen
= strlen(buf
);
1374 length
+= lenlen
- 2;
1375 sprintf(buf
, "%lu", length
);
1376 if (strlen(buf
) > lenlen
)
1377 sprintf(buf
, "%lu", ++length
);
1380 // reallocate m_extendedHdr if it's not big enough
1381 if (m_extendedSize
< length
) {
1382 size_t rounded
= RoundUpSize(length
);
1383 m_extendedSize
<<= 1;
1384 if (rounded
> m_extendedSize
)
1385 m_extendedSize
= rounded
;
1386 char *oldHdr
= m_extendedHdr
;
1387 m_extendedHdr
= new char[m_extendedSize
];
1389 strcpy(m_extendedHdr
, oldHdr
);
1396 // append the new record
1397 char *append
= strchr(m_extendedHdr
, 0);
1398 sprintf(append
, "%s %s=%s\012", buf
,
1399 (const char*)utf_key
, (const char*)utf_value
);
1402 // if not pax then make a list of fields to report as errors
1403 if (!m_badfit
.empty())
1404 m_badfit
+= _T(", ");
1409 void wxTarOutputStream::Sync()
1411 m_parent_o_stream
->Sync();
1414 wxFileOffset
wxTarOutputStream::OnSysSeek(wxFileOffset pos
, wxSeekMode mode
)
1417 wxLogError(_("tar entry not open"));
1418 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1420 if (!IsOk() || m_datapos
== wxInvalidOffset
)
1421 return wxInvalidOffset
;
1424 case wxFromStart
: break;
1425 case wxFromCurrent
: pos
+= m_pos
; break;
1426 case wxFromEnd
: pos
+= m_maxpos
; break;
1429 if (pos
< 0 || m_parent_o_stream
->SeekO(m_datapos
+ pos
) == wxInvalidOffset
)
1430 return wxInvalidOffset
;
1436 size_t wxTarOutputStream::OnSysWrite(const void *buffer
, size_t size
)
1439 wxLogError(_("tar entry not open"));
1440 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1442 if (!IsOk() || !size
)
1445 size_t lastwrite
= m_parent_o_stream
->Write(buffer
, size
).LastWrite();
1447 if (m_pos
> m_maxpos
)
1450 if (lastwrite
!= size
)
1451 m_lasterror
= wxSTREAM_WRITE_ERROR
;
1456 #endif // wxUSE_TARSTREAM