]> git.saurik.com Git - wxWidgets.git/blame - src/common/tarstrm.cpp
restored expanders
[wxWidgets.git] / src / common / tarstrm.cpp
CommitLineData
dd1462c3
MW
1/////////////////////////////////////////////////////////////////////////////
2// Name: tarstrm.cpp
3// Purpose: Streams for Tar files
4// Author: Mike Wetherell
5// RCS-ID: $Id$
6// Copyright: (c) 2004 Mike Wetherell
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14 #pragma hdrstop
15#endif
16
17#if wxUSE_TARSTREAM
18
19#include "wx/tarstrm.h"
20
21#ifndef WX_PRECOMP
dd1462c3
MW
22 #include "wx/intl.h"
23 #include "wx/log.h"
24 #include "wx/utils.h"
25#endif
26
27#include "wx/buffer.h"
28#include "wx/datetime.h"
29#include "wx/ptr_scpd.h"
30#include "wx/filename.h"
31
32#include <ctype.h>
33
34#ifdef __UNIX__
35#include <grp.h>
36#endif
37
38
39/////////////////////////////////////////////////////////////////////////////
40// constants
41
42enum {
43 TAR_NAME,
44 TAR_MODE,
45 TAR_UID,
46 TAR_GID,
47 TAR_SIZE,
48 TAR_MTIME,
49 TAR_CHKSUM,
50 TAR_TYPEFLAG,
51 TAR_LINKNAME,
52 TAR_MAGIC,
53 TAR_VERSION,
54 TAR_UNAME,
55 TAR_GNAME,
56 TAR_DEVMAJOR,
57 TAR_DEVMINOR,
58 TAR_PREFIX,
59 TAR_UNUSED,
60 TAR_NUMFIELDS
61};
62
63enum {
6df6e35a 64 TAR_BLOCKSIZE = 512
dd1462c3
MW
65};
66
67// checksum type
68enum {
69 SUM_UNKNOWN,
70 SUM_UNSIGNED,
71 SUM_SIGNED
72};
73
74// type of input tar
75enum {
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
79};
80
81// signatures
82static const char *USTAR_MAGIC = "ustar";
83static const char *USTAR_VERSION = "00";
84static const char *GNU_MAGIC = "ustar ";
85static const char *GNU_VERION = " ";
86
87IMPLEMENT_DYNAMIC_CLASS(wxTarEntry, wxArchiveEntry)
88IMPLEMENT_DYNAMIC_CLASS(wxTarClassFactory, wxArchiveClassFactory)
89
90
91/////////////////////////////////////////////////////////////////////////////
92// Class factory
93
94static wxTarClassFactory g_wxTarClassFactory;
95
96wxTarClassFactory::wxTarClassFactory()
97{
98 if (this == &g_wxTarClassFactory)
99 PushFront();
100}
101
102const wxChar * const *
103wxTarClassFactory::GetProtocols(wxStreamProtocolType type) const
104{
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 };
109
110 switch (type) {
489a164c
MW
111 case wxSTREAM_PROTOCOL: return protocols;
112 case wxSTREAM_MIMETYPE: return mimetypes;
113 case wxSTREAM_FILEEXT: return fileexts;
114 default: return empty;
dd1462c3
MW
115 }
116}
117
118
119/////////////////////////////////////////////////////////////////////////////
120// tar header block
121
122typedef wxFileOffset wxTarNumber;
123
124struct wxTarField { const wxChar *name; int pos; };
125
126class wxTarHeaderBlock
127{
128public:
129 wxTarHeaderBlock()
130 { memset(data, 0, sizeof(data)); }
131 wxTarHeaderBlock(const wxTarHeaderBlock& hb)
132 { memcpy(data, hb.data, sizeof(data)); }
133
134 bool Read(wxInputStream& in);
135 bool Write(wxOutputStream& out);
ca2261d3 136 inline bool WriteField(wxOutputStream& out, int id);
dd1462c3
MW
137
138 bool IsAllZeros() const;
139 wxUint32 Sum(bool SignedSum = false);
140 wxUint32 SumField(int id);
141
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; }
146
147 bool SetOctal(int id, wxTarNumber n);
148 wxTarNumber GetOctal(int id);
149 bool SetPath(const wxString& name, wxMBConv& conv);
150
151private:
152 char data[TAR_BLOCKSIZE + TAR_NUMFIELDS];
153 static const wxTarField fields[];
154 static void check();
155};
156
6df6e35a 157wxDEFINE_SCOPED_PTR_TYPE(wxTarHeaderBlock)
dd1462c3
MW
158
159// A table giving the field names and offsets in a tar header block
160const wxTarField wxTarHeaderBlock::fields[] =
161{
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 }
180};
181
182void wxTarHeaderBlock::check()
183{
1cc42ce3 184#if 0
dd1462c3
MW
185 wxCOMPILE_TIME_ASSERT(
186 WXSIZEOF(fields) == TAR_NUMFIELDS + 1,
187 Wrong_number_of_elements_in_fields_table
188 );
1cc42ce3 189#endif
dd1462c3
MW
190}
191
192bool wxTarHeaderBlock::IsAllZeros() const
193{
194 const char *p = data;
195 for (size_t i = 0; i < sizeof(data); i++)
196 if (p[i])
197 return false;
198 return true;
199}
200
201wxUint32 wxTarHeaderBlock::Sum(bool SignedSum /*=false*/)
202{
203 // the chksum field itself should be blanks during the calculation
204 memset(Get(TAR_CHKSUM), ' ', Len(TAR_CHKSUM));
205 const char *p = data;
206 wxUint32 n = 0;
207
208 if (SignedSum)
209 for (size_t i = 0; i < sizeof(data); i++)
210 n += (signed char)p[i];
211 else
212 for (size_t i = 0; i < sizeof(data); i++)
213 n += (unsigned char)p[i];
214
215 return n;
216}
217
218wxUint32 wxTarHeaderBlock::SumField(int id)
219{
220 unsigned char *p = (unsigned char*)Get(id);
221 unsigned char *q = p + Len(id);
222 wxUint32 n = 0;
223
224 while (p < q)
225 n += *p++;
226
227 return n;
228}
229
230bool wxTarHeaderBlock::Read(wxInputStream& in)
231{
232 bool ok = true;
233
234 for (int id = 0; id < TAR_NUMFIELDS && ok; id++)
235 ok = in.Read(Get(id), Len(id)).LastRead() == Len(id);
236
237 return ok;
238}
239
240bool wxTarHeaderBlock::Write(wxOutputStream& out)
241{
242 bool ok = true;
243
244 for (int id = 0; id < TAR_NUMFIELDS && ok; id++)
245 ok = WriteField(out, id);
246
247 return ok;
248}
249
ca2261d3 250inline bool wxTarHeaderBlock::WriteField(wxOutputStream& out, int id)
dd1462c3
MW
251{
252 return out.Write(Get(id), Len(id)).LastWrite() == Len(id);
253}
254
255wxTarNumber wxTarHeaderBlock::GetOctal(int id)
256{
257 wxTarNumber n = 0;
258 const char *p = Get(id);
259 while (*p == ' ')
260 p++;
261 while (*p >= '0' && *p < '8')
262 n = (n << 3) | (*p++ - '0');
263 return n;
264}
265
266bool wxTarHeaderBlock::SetOctal(int id, wxTarNumber n)
267{
268 // set an octal field, return true if the number fits
269 char *field = Get(id);
270 char *p = field + Len(id);
271 *--p = 0;
272 while (p > field) {
ea923162 273 *--p = char('0' + (n & 7));
dd1462c3
MW
274 n >>= 3;
275 }
276 return n == 0;
277}
278
279bool wxTarHeaderBlock::SetPath(const wxString& name, wxMBConv& conv)
280{
281 bool badconv = false;
282
283#if wxUSE_UNICODE
284 wxCharBuffer nameBuf = name.mb_str(conv);
285
286 // if the conversion fails make an approximation
287 if (!nameBuf) {
288 badconv = true;
289 size_t len = name.length();
290 wxCharBuffer approx(len);
291 for (size_t i = 0; i < len; i++)
c9f78968
VS
292 {
293 wxChar c = name[i];
294 approx.data()[i] = c & ~0x7F ? '_' : c;
295 }
dd1462c3
MW
296 nameBuf = approx;
297 }
298
299 const char *mbName = nameBuf;
300#else
301 const char *mbName = name.c_str();
302 (void)conv;
303#endif
304
305 bool fits;
306 bool notGoingToFit = false;
307 size_t len = strlen(mbName);
308 size_t maxname = Len(TAR_NAME);
309 size_t maxprefix = Len(TAR_PREFIX);
310 size_t i = 0;
311 size_t nexti = 0;
312
313 for (;;) {
314 fits = i < maxprefix && len - i <= maxname;
315
316 if (!fits) {
317 const char *p = strchr(mbName + i, '/');
318 if (p)
319 nexti = p - mbName + 1;
320 if (!p || nexti - 1 > maxprefix)
321 notGoingToFit = true;
322 }
323
324 if (fits || notGoingToFit) {
325 strncpy(Get(TAR_NAME), mbName + i, maxname);
326 if (i > 0)
327 strncpy(Get(TAR_PREFIX), mbName, i - 1);
328 break;
329 }
330
331 i = nexti;
332 }
333
334 return fits && !badconv;
335}
336
337
338/////////////////////////////////////////////////////////////////////////////
339// Some helpers
340
341static wxFileOffset RoundUpSize(wxFileOffset size, int factor = 1)
342{
343 wxFileOffset chunk = TAR_BLOCKSIZE * factor;
344 return ((size + chunk - 1) / chunk) * chunk;
345}
346
347static wxString GroupName()
348{
349#ifdef __UNIX__
350 group *gr;
351 if ((gr = getgrgid(getgid())) != NULL)
352 return wxString(gr->gr_name, wxConvLibc);
353#endif
354 return _("unknown");
355}
356
357static inline int UserId()
358{
359#ifdef __UNIX__
360 return getuid();
361#else
362 return 0;
363#endif
364}
365
366static inline int GroupId()
367{
368#ifdef __UNIX__
369 return getgid();
370#else
371 return 0;
372#endif
373}
374
375// ignore the size field for entry types 3, 4, 5 and 6
376//
377static inline wxFileOffset GetDataSize(const wxTarEntry& entry)
378{
379 switch (entry.GetTypeFlag()) {
380 case wxTAR_CHRTYPE:
381 case wxTAR_BLKTYPE:
382 case wxTAR_DIRTYPE:
383 case wxTAR_FIFOTYPE:
384 return 0;
385 default:
386 return entry.GetSize();
61d8dec7 387 }
dd1462c3
MW
388}
389
390
391/////////////////////////////////////////////////////////////////////////////
392// Tar Entry
393// Holds all the meta-data for a file in the tar
394
395wxTarEntry::wxTarEntry(const wxString& name /*=wxEmptyString*/,
396 const wxDateTime& dt /*=wxDateTime::Now()*/,
397 wxFileOffset size /*=0*/)
398 : m_Mode(0644),
399 m_IsModeSet(false),
400 m_UserId(UserId()),
401 m_GroupId(GroupId()),
402 m_Size(size),
403 m_Offset(wxInvalidOffset),
404 m_ModifyTime(dt),
405 m_TypeFlag(wxTAR_REGTYPE),
406 m_UserName(wxGetUserId()),
407 m_GroupName(GroupName()),
408 m_DevMajor(~0),
409 m_DevMinor(~0)
410{
411 if (!name.empty())
412 SetName(name);
413}
414
415wxTarEntry::~wxTarEntry()
416{
417}
418
419wxTarEntry::wxTarEntry(const wxTarEntry& e)
0a704d39
VZ
420 : wxArchiveEntry(),
421 m_Name(e.m_Name),
dd1462c3
MW
422 m_Mode(e.m_Mode),
423 m_IsModeSet(e.m_IsModeSet),
424 m_UserId(e.m_UserId),
425 m_GroupId(e.m_GroupId),
426 m_Size(e.m_Size),
427 m_Offset(e.m_Offset),
428 m_ModifyTime(e.m_ModifyTime),
429 m_AccessTime(e.m_AccessTime),
430 m_CreateTime(e.m_CreateTime),
431 m_TypeFlag(e.m_TypeFlag),
432 m_LinkName(e.m_LinkName),
433 m_UserName(e.m_UserName),
434 m_GroupName(e.m_GroupName),
435 m_DevMajor(e.m_DevMajor),
436 m_DevMinor(e.m_DevMinor)
437{
438}
439
440wxTarEntry& wxTarEntry::operator=(const wxTarEntry& e)
441{
442 if (&e != this) {
443 m_Name = e.m_Name;
444 m_Mode = e.m_Mode;
445 m_IsModeSet = e.m_IsModeSet;
446 m_UserId = e.m_UserId;
447 m_GroupId = e.m_GroupId;
448 m_Size = e.m_Size;
449 m_Offset = e.m_Offset;
450 m_ModifyTime = e.m_ModifyTime;
451 m_AccessTime = e.m_AccessTime;
452 m_CreateTime = e.m_CreateTime;
453 m_TypeFlag = e.m_TypeFlag;
454 m_LinkName = e.m_LinkName;
455 m_UserName = e.m_UserName;
456 m_GroupName = e.m_GroupName;
457 m_DevMajor = e.m_DevMajor;
458 m_DevMinor = e.m_DevMinor;
459 }
460 return *this;
461}
462
463wxString wxTarEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
464{
465 bool isDir = IsDir() && !m_Name.empty();
466
467 // optimisations for common (and easy) cases
468 switch (wxFileName::GetFormat(format)) {
469 case wxPATH_DOS:
470 {
471 wxString name(isDir ? m_Name + _T("\\") : m_Name);
472 for (size_t i = 0; i < name.length(); i++)
473 if (name[i] == _T('/'))
474 name[i] = _T('\\');
475 return name;
476 }
477
478 case wxPATH_UNIX:
479 return isDir ? m_Name + _T("/") : m_Name;
480
481 default:
482 ;
483 }
484
485 wxFileName fn;
486
487 if (isDir)
488 fn.AssignDir(m_Name, wxPATH_UNIX);
489 else
490 fn.Assign(m_Name, wxPATH_UNIX);
491
492 return fn.GetFullPath(format);
493}
494
495void wxTarEntry::SetName(const wxString& name, wxPathFormat format)
496{
497 bool isDir;
498 m_Name = GetInternalName(name, format, &isDir);
499 SetIsDir(isDir);
500}
501
502// Static - Internally tars and zips use forward slashes for the path
503// separator, absolute paths aren't allowed, and directory names have a
504// trailing slash. This function converts a path into this internal format,
505// but without a trailing slash for a directory.
506//
507wxString wxTarEntry::GetInternalName(const wxString& name,
508 wxPathFormat format /*=wxPATH_NATIVE*/,
509 bool *pIsDir /*=NULL*/)
510{
511 wxString internal;
512
513 if (wxFileName::GetFormat(format) != wxPATH_UNIX)
514 internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX);
515 else
516 internal = name;
517
518 bool isDir = !internal.empty() && internal.Last() == '/';
519 if (pIsDir)
520 *pIsDir = isDir;
521 if (isDir)
522 internal.erase(internal.length() - 1);
523
524 while (!internal.empty() && *internal.begin() == '/')
525 internal.erase(0, 1);
526 while (!internal.empty() && internal.compare(0, 2, _T("./")) == 0)
527 internal.erase(0, 2);
528 if (internal == _T(".") || internal == _T(".."))
529 internal = wxEmptyString;
530
531 return internal;
532}
533
534bool wxTarEntry::IsDir() const
535{
5f77e766 536 return m_TypeFlag == wxTAR_DIRTYPE;
dd1462c3
MW
537}
538
539void wxTarEntry::SetIsDir(bool isDir)
540{
541 if (isDir)
542 m_TypeFlag = wxTAR_DIRTYPE;
5f77e766 543 else if (m_TypeFlag == wxTAR_DIRTYPE)
dd1462c3
MW
544 m_TypeFlag = wxTAR_REGTYPE;
545}
546
547void wxTarEntry::SetIsReadOnly(bool isReadOnly)
548{
549 if (isReadOnly)
550 m_Mode &= ~0222;
551 else
552 m_Mode |= 0200;
553}
554
555int wxTarEntry::GetMode() const
556{
557 if (m_IsModeSet || !IsDir())
558 return m_Mode;
559 else
560 return m_Mode | 0111;
561
562}
563
564void wxTarEntry::SetMode(int mode)
565{
566 m_Mode = mode & 07777;
567 m_IsModeSet = true;
568}
569
570
571/////////////////////////////////////////////////////////////////////////////
572// Input stream
573
574wxDECLARE_SCOPED_PTR(wxTarEntry, wxTarEntryPtr_)
575wxDEFINE_SCOPED_PTR (wxTarEntry, wxTarEntryPtr_)
576
dd1462c3
MW
577wxTarInputStream::wxTarInputStream(wxInputStream& stream,
578 wxMBConv& conv /*=wxConvLocal*/)
579 : wxArchiveInputStream(stream, conv)
580{
581 Init();
582}
583
584wxTarInputStream::wxTarInputStream(wxInputStream *stream,
585 wxMBConv& conv /*=wxConvLocal*/)
586 : wxArchiveInputStream(stream, conv)
587{
588 Init();
589}
590
591void wxTarInputStream::Init()
592{
593 m_pos = wxInvalidOffset;
594 m_offset = 0;
595 m_size = wxInvalidOffset;
596 m_sumType = SUM_UNKNOWN;
597 m_tarType = TYPE_USTAR;
598 m_hdr = new wxTarHeaderBlock;
599 m_HeaderRecs = NULL;
600 m_GlobalHeaderRecs = NULL;
601 m_lasterror = m_parent_i_stream->GetLastError();
602}
603
604wxTarInputStream::~wxTarInputStream()
605{
606 delete m_hdr;
607 delete m_HeaderRecs;
608 delete m_GlobalHeaderRecs;
609}
610
611wxTarEntry *wxTarInputStream::GetNextEntry()
612{
613 m_lasterror = ReadHeaders();
614
615 if (!IsOk())
616 return NULL;
617
618 wxTarEntryPtr_ entry(new wxTarEntry);
619
620 entry->SetMode(GetHeaderNumber(TAR_MODE));
621 entry->SetUserId(GetHeaderNumber(TAR_UID));
622 entry->SetGroupId(GetHeaderNumber(TAR_UID));
623 entry->SetSize(GetHeaderNumber(TAR_SIZE));
624
625 entry->SetOffset(m_offset);
626
627 entry->SetDateTime(GetHeaderDate(_T("mtime")));
628 entry->SetAccessTime(GetHeaderDate(_T("atime")));
629 entry->SetCreateTime(GetHeaderDate(_T("ctime")));
630
631 entry->SetTypeFlag(*m_hdr->Get(TAR_TYPEFLAG));
632 bool isDir = entry->IsDir();
633
634 entry->SetLinkName(GetHeaderString(TAR_LINKNAME));
635
636 if (m_tarType != TYPE_OLDTAR) {
637 entry->SetUserName(GetHeaderString(TAR_UNAME));
638 entry->SetGroupName(GetHeaderString(TAR_GNAME));
639
640 entry->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR));
641 entry->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR));
642 }
643
644 entry->SetName(GetHeaderPath(), wxPATH_UNIX);
645 if (isDir)
646 entry->SetIsDir();
647
648 if (m_HeaderRecs)
649 m_HeaderRecs->clear();
650
651 m_size = GetDataSize(*entry);
652 m_pos = 0;
653
654 return entry.release();
655}
656
657bool wxTarInputStream::OpenEntry(wxTarEntry& entry)
658{
659 wxFileOffset offset = entry.GetOffset();
660
661 if (GetLastError() != wxSTREAM_READ_ERROR
662 && m_parent_i_stream->IsSeekable()
663 && m_parent_i_stream->SeekI(offset) == offset)
664 {
665 m_offset = offset;
666 m_size = GetDataSize(entry);
667 m_pos = 0;
668 m_lasterror = wxSTREAM_NO_ERROR;
669 return true;
670 } else {
671 m_lasterror = wxSTREAM_READ_ERROR;
672 return false;
673 }
674}
675
676bool wxTarInputStream::OpenEntry(wxArchiveEntry& entry)
677{
678 wxTarEntry *tarEntry = wxStaticCast(&entry, wxTarEntry);
679 return tarEntry ? OpenEntry(*tarEntry) : false;
680}
681
682bool wxTarInputStream::CloseEntry()
683{
684 if (m_lasterror == wxSTREAM_READ_ERROR)
685 return false;
686 if (!IsOpened())
687 return true;
688
689 wxFileOffset size = RoundUpSize(m_size);
690 wxFileOffset remainder = size - m_pos;
691
692 if (remainder && m_parent_i_stream->IsSeekable()) {
693 wxLogNull nolog;
694 if (m_parent_i_stream->SeekI(remainder, wxFromCurrent)
695 != wxInvalidOffset)
696 remainder = 0;
697 }
698
699 if (remainder) {
700 const int BUFSIZE = 8192;
701 wxCharBuffer buf(BUFSIZE);
702
703 while (remainder > 0 && m_parent_i_stream->IsOk())
704 remainder -= m_parent_i_stream->Read(
705 buf.data(), wxMin(BUFSIZE, remainder)).LastRead();
706 }
707
708 m_pos = wxInvalidOffset;
709 m_offset += size;
710 m_lasterror = m_parent_i_stream->GetLastError();
711
712 return IsOk();
713}
714
715wxStreamError wxTarInputStream::ReadHeaders()
716{
717 if (!CloseEntry())
718 return wxSTREAM_READ_ERROR;
719
720 bool done = false;
721
722 while (!done) {
723 m_hdr->Read(*m_parent_i_stream);
724 if (m_parent_i_stream->Eof())
725 wxLogError(_("incomplete header block in tar"));
726 if (!*m_parent_i_stream)
727 return wxSTREAM_READ_ERROR;
728 m_offset += TAR_BLOCKSIZE;
729
730 // an all-zero header marks the end of the tar
731 if (m_hdr->IsAllZeros())
732 return wxSTREAM_EOF;
733
734 // the checksum is supposed to be the unsigned sum of the header bytes,
735 // but there have been versions of tar that used the signed sum, so
736 // accept that too, but only if used throughout.
737 wxUint32 chksum = m_hdr->GetOctal(TAR_CHKSUM);
738 bool ok = false;
739
740 if (m_sumType != SUM_SIGNED) {
741 ok = chksum == m_hdr->Sum();
742 if (m_sumType == SUM_UNKNOWN)
743 m_sumType = ok ? SUM_UNSIGNED : SUM_SIGNED;
744 }
745 if (m_sumType == SUM_SIGNED)
746 ok = chksum == m_hdr->Sum(true);
747 if (!ok) {
748 wxLogError(_("checksum failure reading tar header block"));
749 return wxSTREAM_READ_ERROR;
750 }
751
752 if (strcmp(m_hdr->Get(TAR_MAGIC), USTAR_MAGIC) == 0)
753 m_tarType = TYPE_USTAR;
754 else if (strcmp(m_hdr->Get(TAR_MAGIC), GNU_MAGIC) == 0 &&
755 strcmp(m_hdr->Get(TAR_VERSION), GNU_VERION) == 0)
756 m_tarType = TYPE_GNUTAR;
757 else
758 m_tarType = TYPE_OLDTAR;
759
760 if (m_tarType != TYPE_USTAR)
761 break;
762
763 switch (*m_hdr->Get(TAR_TYPEFLAG)) {
764 case 'g': ReadExtendedHeader(m_GlobalHeaderRecs); break;
765 case 'x': ReadExtendedHeader(m_HeaderRecs); break;
766 default: done = true;
767 }
768 }
769
770 return wxSTREAM_NO_ERROR;
771}
772
773wxString wxTarInputStream::GetExtendedHeader(const wxString& key) const
774{
775 wxTarHeaderRecords::iterator it;
776
777 // look at normal extended header records first
778 if (m_HeaderRecs) {
779 it = m_HeaderRecs->find(key);
780 if (it != m_HeaderRecs->end())
781 return wxString(it->second.wc_str(wxConvUTF8), GetConv());
782 }
783
784 // if not found, look at the global header records
785 if (m_GlobalHeaderRecs) {
786 it = m_GlobalHeaderRecs->find(key);
787 if (it != m_GlobalHeaderRecs->end())
788 return wxString(it->second.wc_str(wxConvUTF8), GetConv());
789 }
790
791 return wxEmptyString;
792}
793
794wxString wxTarInputStream::GetHeaderPath() const
795{
796 wxString path;
797
798 if ((path = GetExtendedHeader(_T("path"))) != wxEmptyString)
799 return path;
800
801 path = wxString(m_hdr->Get(TAR_NAME), GetConv());
802 if (m_tarType != TYPE_USTAR)
803 return path;
804
805 const char *prefix = m_hdr->Get(TAR_PREFIX);
806 return *prefix ? wxString(prefix, GetConv()) + _T("/") + path : path;
807}
808
809wxDateTime wxTarInputStream::GetHeaderDate(const wxString& key) const
810{
811 wxString value;
812
813 // try extended header, stored as decimal seconds since the epoch
814 if ((value = GetExtendedHeader(key)) != wxEmptyString) {
815 wxLongLong ll;
816 ll.Assign(wxAtof(value) * 1000.0);
817 return ll;
818 }
819
820 if (key == _T("mtime"))
821 return wxLongLong(m_hdr->GetOctal(TAR_MTIME)) * 1000L;
822
823 return wxDateTime();
824}
825
826wxTarNumber wxTarInputStream::GetHeaderNumber(int id) const
827{
828 wxString value;
829
e3a10e0c 830 if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString) {
dd1462c3 831 wxTarNumber n = 0;
86501081
VS
832 wxString::const_iterator p = value.begin();
833 while (*p == ' ' && p != value.end())
dd1462c3
MW
834 p++;
835 while (isdigit(*p))
836 n = n * 10 + (*p++ - '0');
837 return n;
838 } else {
839 return m_hdr->GetOctal(id);
840 }
841}
842
843wxString wxTarInputStream::GetHeaderString(int id) const
844{
845 wxString value;
846
e3a10e0c 847 if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString)
dd1462c3
MW
848 return value;
849
850 return wxString(m_hdr->Get(id), GetConv());
851}
852
853// An extended header consists of one or more records, each constructed:
854// "%d %s=%s\n", <length>, <keyword>, <value>
855// <length> is the byte length, <keyword> and <value> are UTF-8
856
857bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords*& recs)
858{
859 if (!recs)
860 recs = new wxTarHeaderRecords;
861
862 // round length up to a whole number of blocks
863 size_t len = m_hdr->GetOctal(TAR_SIZE);
864 size_t size = RoundUpSize(len);
865
866 // read in the whole header since it should be small
867 wxCharBuffer buf(size);
868 size_t lastread = m_parent_i_stream->Read(buf.data(), size).LastRead();
869 if (lastread < len)
870 len = lastread;
871 buf.data()[len] = 0;
872 m_offset += lastread;
873
874 size_t recPos, recSize;
875 bool ok = true;
876
877 for (recPos = 0; recPos < len; recPos += recSize) {
878 char *pRec = buf.data() + recPos;
879 char *p = pRec;
880
881 // read the record size (byte count in ascii decimal)
882 recSize = 0;
883 while (isdigit(*p))
884 recSize = recSize * 10 + *p++ - '0';
885
886 // validity checks
887 if (recPos + recSize > len)
888 break;
889 if (recSize < p - pRec + (size_t)3 || *p != ' '
890 || pRec[recSize - 1] != '\012') {
891 ok = false;
892 continue;
893 }
894
895 // replace the final '\n' with a nul, to terminate value
896 pRec[recSize - 1] = 0;
897 // the key is here, following the space
898 char *pKey = ++p;
899
900 // look forward for the '=', the value follows
901 while (*p && *p != '=')
902 p++;
903 if (!*p) {
904 ok = false;
905 continue;
906 }
907 // replace the '=' with a nul, to terminate the key
908 *p++ = 0;
909
910 wxString key(wxConvUTF8.cMB2WC(pKey), GetConv());
911 wxString value(wxConvUTF8.cMB2WC(p), GetConv());
912
913 // an empty value unsets a previously given value
914 if (value.empty())
915 recs->erase(key);
916 else
917 (*recs)[key] = value;
918 }
919
920 if (!ok || recPos < len || size != lastread) {
921 wxLogWarning(_("invalid data in extended tar header"));
922 return false;
923 }
924
925 return true;
926}
927
928wxFileOffset wxTarInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
929{
930 if (!IsOpened()) {
931 wxLogError(_("tar entry not open"));
932 m_lasterror = wxSTREAM_READ_ERROR;
933 }
934 if (!IsOk())
935 return wxInvalidOffset;
936
937 switch (mode) {
938 case wxFromStart: break;
939 case wxFromCurrent: pos += m_pos; break;
940 case wxFromEnd: pos += m_size; break;
941 }
942
943 if (pos < 0 || m_parent_i_stream->SeekI(m_offset + pos) == wxInvalidOffset)
944 return wxInvalidOffset;
945
946 m_pos = pos;
947 return m_pos;
948}
949
950size_t wxTarInputStream::OnSysRead(void *buffer, size_t size)
951{
952 if (!IsOpened()) {
953 wxLogError(_("tar entry not open"));
954 m_lasterror = wxSTREAM_READ_ERROR;
955 }
956 if (!IsOk() || !size)
957 return 0;
958
959 if (m_pos >= m_size)
960 size = 0;
961 else if (m_pos + size > m_size + (size_t)0)
962 size = m_size - m_pos;
963
964 size_t lastread = m_parent_i_stream->Read(buffer, size).LastRead();
965 m_pos += lastread;
966
967 if (m_pos >= m_size) {
968 m_lasterror = wxSTREAM_EOF;
969 } else if (!m_parent_i_stream->IsOk()) {
970 // any other error will have been reported by the underlying stream
971 if (m_parent_i_stream->Eof())
972 wxLogError(_("unexpected end of file"));
973 m_lasterror = wxSTREAM_READ_ERROR;
974 }
975
976 return lastread;
977}
978
979
980/////////////////////////////////////////////////////////////////////////////
981// Output stream
982
983wxTarOutputStream::wxTarOutputStream(wxOutputStream& stream,
984 wxTarFormat format /*=wxTAR_PAX*/,
985 wxMBConv& conv /*=wxConvLocal*/)
986 : wxArchiveOutputStream(stream, conv)
987{
988 Init(format);
989}
990
991wxTarOutputStream::wxTarOutputStream(wxOutputStream *stream,
992 wxTarFormat format /*=wxTAR_PAX*/,
993 wxMBConv& conv /*=wxConvLocal*/)
994 : wxArchiveOutputStream(stream, conv)
995{
996 Init(format);
997}
998
999void wxTarOutputStream::Init(wxTarFormat format)
1000{
1001 m_pos = wxInvalidOffset;
1002 m_maxpos = wxInvalidOffset;
1003 m_size = wxInvalidOffset;
1004 m_headpos = wxInvalidOffset;
1005 m_datapos = wxInvalidOffset;
1006 m_tarstart = wxInvalidOffset;
1007 m_tarsize = 0;
1008 m_pax = format == wxTAR_PAX;
1009 m_BlockingFactor = m_pax ? 10 : 20;
1010 m_chksum = 0;
1011 m_large = false;
1012 m_hdr = new wxTarHeaderBlock;
1013 m_hdr2 = NULL;
1014 m_extendedHdr = NULL;
1015 m_extendedSize = 0;
1016 m_lasterror = m_parent_o_stream->GetLastError();
1017}
1018
1019wxTarOutputStream::~wxTarOutputStream()
1020{
1021 if (m_tarsize)
1022 Close();
1023 delete m_hdr;
1024 delete m_hdr2;
1025 delete [] m_extendedHdr;
1026}
1027
1028bool wxTarOutputStream::PutNextEntry(wxTarEntry *entry)
1029{
1030 wxTarEntryPtr_ e(entry);
1031
1032 if (!CloseEntry())
1033 return false;
1034
1035 if (!m_tarsize) {
1036 wxLogNull nolog;
1037 m_tarstart = m_parent_o_stream->TellO();
1038 }
1039
1040 if (m_tarstart != wxInvalidOffset)
1041 m_headpos = m_tarstart + m_tarsize;
1042
1043 if (WriteHeaders(*e)) {
1044 m_pos = 0;
1045 m_maxpos = 0;
1046 m_size = GetDataSize(*e);
1047 if (m_tarstart != wxInvalidOffset)
1048 m_datapos = m_tarstart + m_tarsize;
1049
1050 // types that are not allowd any data
1051 const char nodata[] = {
1052 wxTAR_LNKTYPE, wxTAR_SYMTYPE, wxTAR_CHRTYPE, wxTAR_BLKTYPE,
1053 wxTAR_DIRTYPE, wxTAR_FIFOTYPE, 0
1054 };
ea923162 1055 int typeflag = e->GetTypeFlag();
dd1462c3
MW
1056
1057 // pax does now allow data for wxTAR_LNKTYPE
ea923162 1058 if (!m_pax || typeflag != wxTAR_LNKTYPE)
dd1462c3
MW
1059 if (strchr(nodata, typeflag) != NULL)
1060 CloseEntry();
1061 }
1062
1063 return IsOk();
1064}
1065
1066bool wxTarOutputStream::PutNextEntry(const wxString& name,
1067 const wxDateTime& dt,
1068 wxFileOffset size)
1069{
1070 return PutNextEntry(new wxTarEntry(name, dt, size));
1071}
1072
1073bool wxTarOutputStream::PutNextDirEntry(const wxString& name,
1074 const wxDateTime& dt)
1075{
1076 wxTarEntry *entry = new wxTarEntry(name, dt);
1077 entry->SetIsDir();
1078 return PutNextEntry(entry);
1079}
1080
1081bool wxTarOutputStream::PutNextEntry(wxArchiveEntry *entry)
1082{
1083 wxTarEntry *tarEntry = wxStaticCast(entry, wxTarEntry);
1084 if (!tarEntry)
1085 delete entry;
1086 return PutNextEntry(tarEntry);
1087}
1088
1089bool wxTarOutputStream::CopyEntry(wxTarEntry *entry,
1090 wxTarInputStream& inputStream)
1091{
1092 if (PutNextEntry(entry))
1093 Write(inputStream);
1094 return IsOk() && inputStream.Eof();
1095}
1096
1097bool wxTarOutputStream::CopyEntry(wxArchiveEntry *entry,
1098 wxArchiveInputStream& inputStream)
1099{
1100 if (PutNextEntry(entry))
1101 Write(inputStream);
1102 return IsOk() && inputStream.Eof();
1103}
1104
1105bool wxTarOutputStream::CloseEntry()
1106{
1107 if (!IsOpened())
1108 return true;
1109
1110 if (m_pos < m_maxpos) {
1111 wxASSERT(m_parent_o_stream->IsSeekable());
1112 m_parent_o_stream->SeekO(m_datapos + m_maxpos);
1113 m_lasterror = m_parent_o_stream->GetLastError();
1114 m_pos = m_maxpos;
1115 }
1116
1117 if (IsOk()) {
1118 wxFileOffset size = RoundUpSize(m_pos);
1119 if (size > m_pos) {
1120 memset(m_hdr, 0, size - m_pos);
1121 m_parent_o_stream->Write(m_hdr, size - m_pos);
1122 m_lasterror = m_parent_o_stream->GetLastError();
1123 }
1124 m_tarsize += size;
1125 }
1126
1127 if (IsOk() && m_pos != m_size)
1128 ModifyHeader();
1129
1130 m_pos = wxInvalidOffset;
1131 m_maxpos = wxInvalidOffset;
1132 m_size = wxInvalidOffset;
1133 m_headpos = wxInvalidOffset;
1134 m_datapos = wxInvalidOffset;
1135
1136 return IsOk();
1137}
1138
1139bool wxTarOutputStream::Close()
1140{
1141 if (!CloseEntry())
1142 return false;
1143
1144 memset(m_hdr, 0, sizeof(*m_hdr));
1145 int count = (RoundUpSize(m_tarsize + 2 * TAR_BLOCKSIZE, m_BlockingFactor)
1146 - m_tarsize) / TAR_BLOCKSIZE;
1147 while (count--)
1148 m_parent_o_stream->Write(m_hdr, TAR_BLOCKSIZE);
1149
1150 m_tarsize = 0;
1151 m_tarstart = wxInvalidOffset;
1152 m_lasterror = m_parent_o_stream->GetLastError();
1153 return IsOk();
1154}
1155
1156bool wxTarOutputStream::WriteHeaders(wxTarEntry& entry)
1157{
1158 memset(m_hdr, 0, sizeof(*m_hdr));
1159
1160 SetHeaderPath(entry.GetName(wxPATH_UNIX));
1161
1162 SetHeaderNumber(TAR_MODE, entry.GetMode());
1163 SetHeaderNumber(TAR_UID, entry.GetUserId());
1164 SetHeaderNumber(TAR_GID, entry.GetGroupId());
1165
1166 if (entry.GetSize() == wxInvalidOffset)
1167 entry.SetSize(0);
7bb82a74 1168 m_large = !SetHeaderNumber(TAR_SIZE, entry.GetSize());
dd1462c3
MW
1169
1170 SetHeaderDate(_T("mtime"), entry.GetDateTime());
1171 if (entry.GetAccessTime().IsValid())
1172 SetHeaderDate(_T("atime"), entry.GetAccessTime());
1173 if (entry.GetCreateTime().IsValid())
1174 SetHeaderDate(_T("ctime"), entry.GetCreateTime());
1175
ea923162 1176 *m_hdr->Get(TAR_TYPEFLAG) = char(entry.GetTypeFlag());
dd1462c3
MW
1177
1178 strcpy(m_hdr->Get(TAR_MAGIC), USTAR_MAGIC);
1179 strcpy(m_hdr->Get(TAR_VERSION), USTAR_VERSION);
1180
1181 SetHeaderString(TAR_LINKNAME, entry.GetLinkName());
1182 SetHeaderString(TAR_UNAME, entry.GetUserName());
1183 SetHeaderString(TAR_GNAME, entry.GetGroupName());
1184
1185 if (~entry.GetDevMajor())
1186 SetHeaderNumber(TAR_DEVMAJOR, entry.GetDevMajor());
1187 if (~entry.GetDevMinor())
1188 SetHeaderNumber(TAR_DEVMINOR, entry.GetDevMinor());
1189
1190 m_chksum = m_hdr->Sum();
1191 m_hdr->SetOctal(TAR_CHKSUM, m_chksum);
1192 if (!m_large)
1193 m_chksum -= m_hdr->SumField(TAR_SIZE);
1194
1195 // The main header is now fully prepared so we know what extended headers
1196 // (if any) will be needed. Output any extended headers before writing
1197 // the main header.
1198 if (m_extendedHdr && *m_extendedHdr) {
1199 wxASSERT(m_pax);
1200 // the extended headers are written to the tar as a file entry,
1201 // so prepare a regular header block for the pseudo-file.
1202 if (!m_hdr2)
1203 m_hdr2 = new wxTarHeaderBlock;
1204 memset(m_hdr2, 0, sizeof(*m_hdr2));
1205
1206 // an old tar that doesn't understand extended headers will
1207 // extract it as a file, so give these fields reasonable values
1208 // so that the user will have access to read and remove it.
1209 m_hdr2->SetPath(PaxHeaderPath(_T("%d/PaxHeaders.%p/%f"),
1210 entry.GetName(wxPATH_UNIX)), GetConv());
1211 m_hdr2->SetOctal(TAR_MODE, 0600);
1212 strcpy(m_hdr2->Get(TAR_UID), m_hdr->Get(TAR_UID));
1213 strcpy(m_hdr2->Get(TAR_GID), m_hdr->Get(TAR_GID));
1214 size_t length = strlen(m_extendedHdr);
1215 m_hdr2->SetOctal(TAR_SIZE, length);
1216 strcpy(m_hdr2->Get(TAR_MTIME), m_hdr->Get(TAR_MTIME));
1217 *m_hdr2->Get(TAR_TYPEFLAG) = 'x';
1218 strcpy(m_hdr2->Get(TAR_MAGIC), USTAR_MAGIC);
1219 strcpy(m_hdr2->Get(TAR_VERSION), USTAR_VERSION);
1220 strcpy(m_hdr2->Get(TAR_UNAME), m_hdr->Get(TAR_UNAME));
1221 strcpy(m_hdr2->Get(TAR_GNAME), m_hdr->Get(TAR_GNAME));
1222
1223 m_hdr2->SetOctal(TAR_CHKSUM, m_hdr2->Sum());
1224
1225 m_hdr2->Write(*m_parent_o_stream);
1226 m_tarsize += TAR_BLOCKSIZE;
1227
1228 size_t rounded = RoundUpSize(length);
1229 memset(m_extendedHdr + length, 0, rounded - length);
1230 m_parent_o_stream->Write(m_extendedHdr, rounded);
1231 m_tarsize += rounded;
1232
1233 *m_extendedHdr = 0;
1234 }
1235
1236 // if don't have extended headers just report error
1237 if (!m_badfit.empty()) {
1238 wxASSERT(!m_pax);
1239 wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
1240 m_badfit.c_str(), entry.GetName().c_str());
1241 m_badfit.clear();
1242 }
1243
1244 m_hdr->Write(*m_parent_o_stream);
1245 m_tarsize += TAR_BLOCKSIZE;
1246 m_lasterror = m_parent_o_stream->GetLastError();
1247
1248 return IsOk();
1249}
1250
1251wxString wxTarOutputStream::PaxHeaderPath(const wxString& format,
1252 const wxString& path)
1253{
1254 wxString d = path.BeforeLast(_T('/'));
1255 wxString f = path.AfterLast(_T('/'));
1256 wxString ret;
1257
1258 if (d.empty())
1259 d = _T(".");
1260
1261 ret.reserve(format.length() + path.length() + 16);
1262
1263 size_t begin = 0;
1264 size_t end;
1265
1266 for (;;) {
1267 end = format.find('%', begin);
1268 if (end == wxString::npos || end + 1 >= format.length())
1269 break;
1270 ret << format.substr(begin, end - begin);
c9f78968 1271 switch ( format[end + 1].GetValue() ) {
dd1462c3
MW
1272 case 'd': ret << d; break;
1273 case 'f': ret << f; break;
1274 case 'p': ret << wxGetProcessId(); break;
1275 case '%': ret << _T("%"); break;
1276 }
1277 begin = end + 2;
1278 }
1279
1280 ret << format.substr(begin);
1281
1282 return ret;
1283}
1284
1285bool wxTarOutputStream::ModifyHeader()
1286{
1287 wxFileOffset originalPos = wxInvalidOffset;
1288 wxFileOffset sizePos = wxInvalidOffset;
1289
1290 if (!m_large && m_headpos != wxInvalidOffset
1291 && m_parent_o_stream->IsSeekable())
1292 {
1293 wxLogNull nolog;
1294 originalPos = m_parent_o_stream->TellO();
1295 if (originalPos != wxInvalidOffset)
1296 sizePos =
e3a10e0c 1297 m_parent_o_stream->SeekO(m_headpos + m_hdr->Offset(TAR_SIZE));
dd1462c3
MW
1298 }
1299
1300 if (sizePos == wxInvalidOffset || !m_hdr->SetOctal(TAR_SIZE, m_pos)) {
1301 wxLogError(_("incorrect size given for tar entry"));
1302 m_lasterror = wxSTREAM_WRITE_ERROR;
1303 return false;
1304 }
1305
1306 m_chksum += m_hdr->SumField(TAR_SIZE);
1307 m_hdr->SetOctal(TAR_CHKSUM, m_chksum);
e3a10e0c 1308 wxFileOffset sumPos = m_headpos + m_hdr->Offset(TAR_CHKSUM);
dd1462c3
MW
1309
1310 return
1311 m_hdr->WriteField(*m_parent_o_stream, TAR_SIZE) &&
1312 m_parent_o_stream->SeekO(sumPos) == sumPos &&
1313 m_hdr->WriteField(*m_parent_o_stream, TAR_CHKSUM) &&
1314 m_parent_o_stream->SeekO(originalPos) == originalPos;
1315}
1316
1317void wxTarOutputStream::SetHeaderPath(const wxString& name)
1318{
1319 if (!m_hdr->SetPath(name, GetConv()) || (m_pax && !name.IsAscii()))
1320 SetExtendedHeader(_T("path"), name);
1321}
1322
1323bool wxTarOutputStream::SetHeaderNumber(int id, wxTarNumber n)
1324{
1325 if (m_hdr->SetOctal(id, n)) {
1326 return true;
1327 } else {
e3a10e0c 1328 SetExtendedHeader(m_hdr->Name(id), wxLongLong(n).ToString());
dd1462c3
MW
1329 return false;
1330 }
1331}
1332
1333void wxTarOutputStream::SetHeaderString(int id, const wxString& str)
1334{
e3a10e0c
MW
1335 strncpy(m_hdr->Get(id), str.mb_str(GetConv()), m_hdr->Len(id));
1336 if (str.length() > m_hdr->Len(id))
1337 SetExtendedHeader(m_hdr->Name(id), str);
dd1462c3
MW
1338}
1339
1340void wxTarOutputStream::SetHeaderDate(const wxString& key,
1341 const wxDateTime& datetime)
1342{
1343 wxLongLong ll = datetime.IsValid() ? datetime.GetValue() : wxLongLong(0);
1344 wxLongLong secs = ll / 1000L;
1345
ea923162
MW
1346 if (key != _T("mtime")
1347 || !m_hdr->SetOctal(TAR_MTIME, wxTarNumber(secs.GetValue()))
dd1462c3
MW
1348 || secs <= 0 || secs >= 0x7fffffff)
1349 {
1350 wxString str;
1351 if (ll >= LONG_MIN && ll <= LONG_MAX) {
1352 str.Printf(_T("%g"), ll.ToLong() / 1000.0);
1353 } else {
1354 str = ll.ToString();
1355 str.insert(str.end() - 3, '.');
1356 }
1357 SetExtendedHeader(key, str);
1358 }
1359}
1360
1361void wxTarOutputStream::SetExtendedHeader(const wxString& key,
1362 const wxString& value)
1363{
1364 if (m_pax) {
163d9efc
VS
1365#if wxUSE_UNICODE
1366 const wxCharBuffer utf_key = key.utf8_str();
1367 const wxCharBuffer utf_value = value.utf8_str();
1368#else
dd1462c3
MW
1369 const wxWX2WCbuf wide_key = key.wc_str(GetConv());
1370 const wxCharBuffer utf_key = wxConvUTF8.cWC2MB(wide_key);
1371
1372 const wxWX2WCbuf wide_value = value.wc_str(GetConv());
1373 const wxCharBuffer utf_value = wxConvUTF8.cWC2MB(wide_value);
163d9efc 1374#endif // wxUSE_UNICODE/!wxUSE_UNICODE
dd1462c3
MW
1375
1376 // a small buffer to format the length field in
1377 char buf[32];
1378 // length of "99<space><key>=<value>\n"
1379 unsigned long length = strlen(utf_value) + strlen(utf_key) + 5;
1380 sprintf(buf, "%lu", length);
1381 // the length includes itself
1382 size_t lenlen = strlen(buf);
1383 if (lenlen != 2) {
1384 length += lenlen - 2;
1385 sprintf(buf, "%lu", length);
1386 if (strlen(buf) > lenlen)
1387 sprintf(buf, "%lu", ++length);
1388 }
1389
1390 // reallocate m_extendedHdr if it's not big enough
1391 if (m_extendedSize < length) {
1392 size_t rounded = RoundUpSize(length);
1393 m_extendedSize <<= 1;
1394 if (rounded > m_extendedSize)
1395 m_extendedSize = rounded;
1396 char *oldHdr = m_extendedHdr;
1397 m_extendedHdr = new char[m_extendedSize];
1398 if (oldHdr) {
1399 strcpy(m_extendedHdr, oldHdr);
1400 delete oldHdr;
1401 } else {
1402 *m_extendedHdr = 0;
1403 }
1404 }
1405
1406 // append the new record
1407 char *append = strchr(m_extendedHdr, 0);
1408 sprintf(append, "%s %s=%s\012", buf,
1409 (const char*)utf_key, (const char*)utf_value);
1410 }
1411 else {
1412 // if not pax then make a list of fields to report as errors
1413 if (!m_badfit.empty())
1414 m_badfit += _T(", ");
1415 m_badfit += key;
1416 }
1417}
1418
1419void wxTarOutputStream::Sync()
1420{
1421 m_parent_o_stream->Sync();
1422}
1423
1424wxFileOffset wxTarOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
1425{
1426 if (!IsOpened()) {
1427 wxLogError(_("tar entry not open"));
1428 m_lasterror = wxSTREAM_WRITE_ERROR;
1429 }
1430 if (!IsOk() || m_datapos == wxInvalidOffset)
1431 return wxInvalidOffset;
1432
1433 switch (mode) {
1434 case wxFromStart: break;
1435 case wxFromCurrent: pos += m_pos; break;
1436 case wxFromEnd: pos += m_maxpos; break;
1437 }
1438
1439 if (pos < 0 || m_parent_o_stream->SeekO(m_datapos + pos) == wxInvalidOffset)
1440 return wxInvalidOffset;
1441
1442 m_pos = pos;
1443 return m_pos;
1444}
1445
1446size_t wxTarOutputStream::OnSysWrite(const void *buffer, size_t size)
1447{
1448 if (!IsOpened()) {
1449 wxLogError(_("tar entry not open"));
1450 m_lasterror = wxSTREAM_WRITE_ERROR;
1451 }
1452 if (!IsOk() || !size)
1453 return 0;
1454
1455 size_t lastwrite = m_parent_o_stream->Write(buffer, size).LastWrite();
1456 m_pos += lastwrite;
1457 if (m_pos > m_maxpos)
1458 m_maxpos = m_pos;
1459
1460 if (lastwrite != size)
1461 m_lasterror = wxSTREAM_WRITE_ERROR;
1462
1463 return lastwrite;
1464}
1465
1466#endif // wxUSE_TARSTREAM