]> git.saurik.com Git - wxWidgets.git/blame - src/common/tarstrm.cpp
Compilation fix.
[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);
136 inline bool WriteField(wxOutputStream& out, int id);
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
250bool wxTarHeaderBlock::WriteField(wxOutputStream& out, int id)
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++)
292 approx.data()[i] = name[i] & ~0x7F ? '_' : name[i];
293 nameBuf = approx;
294 }
295
296 const char *mbName = nameBuf;
297#else
298 const char *mbName = name.c_str();
299 (void)conv;
300#endif
301
302 bool fits;
303 bool notGoingToFit = false;
304 size_t len = strlen(mbName);
305 size_t maxname = Len(TAR_NAME);
306 size_t maxprefix = Len(TAR_PREFIX);
307 size_t i = 0;
308 size_t nexti = 0;
309
310 for (;;) {
311 fits = i < maxprefix && len - i <= maxname;
312
313 if (!fits) {
314 const char *p = strchr(mbName + i, '/');
315 if (p)
316 nexti = p - mbName + 1;
317 if (!p || nexti - 1 > maxprefix)
318 notGoingToFit = true;
319 }
320
321 if (fits || notGoingToFit) {
322 strncpy(Get(TAR_NAME), mbName + i, maxname);
323 if (i > 0)
324 strncpy(Get(TAR_PREFIX), mbName, i - 1);
325 break;
326 }
327
328 i = nexti;
329 }
330
331 return fits && !badconv;
332}
333
334
335/////////////////////////////////////////////////////////////////////////////
336// Some helpers
337
338static wxFileOffset RoundUpSize(wxFileOffset size, int factor = 1)
339{
340 wxFileOffset chunk = TAR_BLOCKSIZE * factor;
341 return ((size + chunk - 1) / chunk) * chunk;
342}
343
344static wxString GroupName()
345{
346#ifdef __UNIX__
347 group *gr;
348 if ((gr = getgrgid(getgid())) != NULL)
349 return wxString(gr->gr_name, wxConvLibc);
350#endif
351 return _("unknown");
352}
353
354static inline int UserId()
355{
356#ifdef __UNIX__
357 return getuid();
358#else
359 return 0;
360#endif
361}
362
363static inline int GroupId()
364{
365#ifdef __UNIX__
366 return getgid();
367#else
368 return 0;
369#endif
370}
371
372// ignore the size field for entry types 3, 4, 5 and 6
373//
374static inline wxFileOffset GetDataSize(const wxTarEntry& entry)
375{
376 switch (entry.GetTypeFlag()) {
377 case wxTAR_CHRTYPE:
378 case wxTAR_BLKTYPE:
379 case wxTAR_DIRTYPE:
380 case wxTAR_FIFOTYPE:
381 return 0;
382 default:
383 return entry.GetSize();
384 };
385}
386
387
388/////////////////////////////////////////////////////////////////////////////
389// Tar Entry
390// Holds all the meta-data for a file in the tar
391
392wxTarEntry::wxTarEntry(const wxString& name /*=wxEmptyString*/,
393 const wxDateTime& dt /*=wxDateTime::Now()*/,
394 wxFileOffset size /*=0*/)
395 : m_Mode(0644),
396 m_IsModeSet(false),
397 m_UserId(UserId()),
398 m_GroupId(GroupId()),
399 m_Size(size),
400 m_Offset(wxInvalidOffset),
401 m_ModifyTime(dt),
402 m_TypeFlag(wxTAR_REGTYPE),
403 m_UserName(wxGetUserId()),
404 m_GroupName(GroupName()),
405 m_DevMajor(~0),
406 m_DevMinor(~0)
407{
408 if (!name.empty())
409 SetName(name);
410}
411
412wxTarEntry::~wxTarEntry()
413{
414}
415
416wxTarEntry::wxTarEntry(const wxTarEntry& e)
417 : m_Name(e.m_Name),
418 m_Mode(e.m_Mode),
419 m_IsModeSet(e.m_IsModeSet),
420 m_UserId(e.m_UserId),
421 m_GroupId(e.m_GroupId),
422 m_Size(e.m_Size),
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)
433{
434}
435
436wxTarEntry& wxTarEntry::operator=(const wxTarEntry& e)
437{
438 if (&e != this) {
439 m_Name = e.m_Name;
440 m_Mode = e.m_Mode;
441 m_IsModeSet = e.m_IsModeSet;
442 m_UserId = e.m_UserId;
443 m_GroupId = e.m_GroupId;
444 m_Size = e.m_Size;
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;
455 }
456 return *this;
457}
458
459wxString wxTarEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
460{
461 bool isDir = IsDir() && !m_Name.empty();
462
463 // optimisations for common (and easy) cases
464 switch (wxFileName::GetFormat(format)) {
465 case wxPATH_DOS:
466 {
467 wxString name(isDir ? m_Name + _T("\\") : m_Name);
468 for (size_t i = 0; i < name.length(); i++)
469 if (name[i] == _T('/'))
470 name[i] = _T('\\');
471 return name;
472 }
473
474 case wxPATH_UNIX:
475 return isDir ? m_Name + _T("/") : m_Name;
476
477 default:
478 ;
479 }
480
481 wxFileName fn;
482
483 if (isDir)
484 fn.AssignDir(m_Name, wxPATH_UNIX);
485 else
486 fn.Assign(m_Name, wxPATH_UNIX);
487
488 return fn.GetFullPath(format);
489}
490
491void wxTarEntry::SetName(const wxString& name, wxPathFormat format)
492{
493 bool isDir;
494 m_Name = GetInternalName(name, format, &isDir);
495 SetIsDir(isDir);
496}
497
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.
502//
503wxString wxTarEntry::GetInternalName(const wxString& name,
504 wxPathFormat format /*=wxPATH_NATIVE*/,
505 bool *pIsDir /*=NULL*/)
506{
507 wxString internal;
508
509 if (wxFileName::GetFormat(format) != wxPATH_UNIX)
510 internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX);
511 else
512 internal = name;
513
514 bool isDir = !internal.empty() && internal.Last() == '/';
515 if (pIsDir)
516 *pIsDir = isDir;
517 if (isDir)
518 internal.erase(internal.length() - 1);
519
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;
526
527 return internal;
528}
529
530bool wxTarEntry::IsDir() const
531{
5f77e766 532 return m_TypeFlag == wxTAR_DIRTYPE;
dd1462c3
MW
533}
534
535void wxTarEntry::SetIsDir(bool isDir)
536{
537 if (isDir)
538 m_TypeFlag = wxTAR_DIRTYPE;
5f77e766 539 else if (m_TypeFlag == wxTAR_DIRTYPE)
dd1462c3
MW
540 m_TypeFlag = wxTAR_REGTYPE;
541}
542
543void wxTarEntry::SetIsReadOnly(bool isReadOnly)
544{
545 if (isReadOnly)
546 m_Mode &= ~0222;
547 else
548 m_Mode |= 0200;
549}
550
551int wxTarEntry::GetMode() const
552{
553 if (m_IsModeSet || !IsDir())
554 return m_Mode;
555 else
556 return m_Mode | 0111;
557
558}
559
560void wxTarEntry::SetMode(int mode)
561{
562 m_Mode = mode & 07777;
563 m_IsModeSet = true;
564}
565
566
567/////////////////////////////////////////////////////////////////////////////
568// Input stream
569
570wxDECLARE_SCOPED_PTR(wxTarEntry, wxTarEntryPtr_)
571wxDEFINE_SCOPED_PTR (wxTarEntry, wxTarEntryPtr_)
572
dd1462c3
MW
573wxTarInputStream::wxTarInputStream(wxInputStream& stream,
574 wxMBConv& conv /*=wxConvLocal*/)
575 : wxArchiveInputStream(stream, conv)
576{
577 Init();
578}
579
580wxTarInputStream::wxTarInputStream(wxInputStream *stream,
581 wxMBConv& conv /*=wxConvLocal*/)
582 : wxArchiveInputStream(stream, conv)
583{
584 Init();
585}
586
587void wxTarInputStream::Init()
588{
589 m_pos = wxInvalidOffset;
590 m_offset = 0;
591 m_size = wxInvalidOffset;
592 m_sumType = SUM_UNKNOWN;
593 m_tarType = TYPE_USTAR;
594 m_hdr = new wxTarHeaderBlock;
595 m_HeaderRecs = NULL;
596 m_GlobalHeaderRecs = NULL;
597 m_lasterror = m_parent_i_stream->GetLastError();
598}
599
600wxTarInputStream::~wxTarInputStream()
601{
602 delete m_hdr;
603 delete m_HeaderRecs;
604 delete m_GlobalHeaderRecs;
605}
606
607wxTarEntry *wxTarInputStream::GetNextEntry()
608{
609 m_lasterror = ReadHeaders();
610
611 if (!IsOk())
612 return NULL;
613
614 wxTarEntryPtr_ entry(new wxTarEntry);
615
616 entry->SetMode(GetHeaderNumber(TAR_MODE));
617 entry->SetUserId(GetHeaderNumber(TAR_UID));
618 entry->SetGroupId(GetHeaderNumber(TAR_UID));
619 entry->SetSize(GetHeaderNumber(TAR_SIZE));
620
621 entry->SetOffset(m_offset);
622
623 entry->SetDateTime(GetHeaderDate(_T("mtime")));
624 entry->SetAccessTime(GetHeaderDate(_T("atime")));
625 entry->SetCreateTime(GetHeaderDate(_T("ctime")));
626
627 entry->SetTypeFlag(*m_hdr->Get(TAR_TYPEFLAG));
628 bool isDir = entry->IsDir();
629
630 entry->SetLinkName(GetHeaderString(TAR_LINKNAME));
631
632 if (m_tarType != TYPE_OLDTAR) {
633 entry->SetUserName(GetHeaderString(TAR_UNAME));
634 entry->SetGroupName(GetHeaderString(TAR_GNAME));
635
636 entry->SetDevMajor(GetHeaderNumber(TAR_DEVMAJOR));
637 entry->SetDevMinor(GetHeaderNumber(TAR_DEVMINOR));
638 }
639
640 entry->SetName(GetHeaderPath(), wxPATH_UNIX);
641 if (isDir)
642 entry->SetIsDir();
643
644 if (m_HeaderRecs)
645 m_HeaderRecs->clear();
646
647 m_size = GetDataSize(*entry);
648 m_pos = 0;
649
650 return entry.release();
651}
652
653bool wxTarInputStream::OpenEntry(wxTarEntry& entry)
654{
655 wxFileOffset offset = entry.GetOffset();
656
657 if (GetLastError() != wxSTREAM_READ_ERROR
658 && m_parent_i_stream->IsSeekable()
659 && m_parent_i_stream->SeekI(offset) == offset)
660 {
661 m_offset = offset;
662 m_size = GetDataSize(entry);
663 m_pos = 0;
664 m_lasterror = wxSTREAM_NO_ERROR;
665 return true;
666 } else {
667 m_lasterror = wxSTREAM_READ_ERROR;
668 return false;
669 }
670}
671
672bool wxTarInputStream::OpenEntry(wxArchiveEntry& entry)
673{
674 wxTarEntry *tarEntry = wxStaticCast(&entry, wxTarEntry);
675 return tarEntry ? OpenEntry(*tarEntry) : false;
676}
677
678bool wxTarInputStream::CloseEntry()
679{
680 if (m_lasterror == wxSTREAM_READ_ERROR)
681 return false;
682 if (!IsOpened())
683 return true;
684
685 wxFileOffset size = RoundUpSize(m_size);
686 wxFileOffset remainder = size - m_pos;
687
688 if (remainder && m_parent_i_stream->IsSeekable()) {
689 wxLogNull nolog;
690 if (m_parent_i_stream->SeekI(remainder, wxFromCurrent)
691 != wxInvalidOffset)
692 remainder = 0;
693 }
694
695 if (remainder) {
696 const int BUFSIZE = 8192;
697 wxCharBuffer buf(BUFSIZE);
698
699 while (remainder > 0 && m_parent_i_stream->IsOk())
700 remainder -= m_parent_i_stream->Read(
701 buf.data(), wxMin(BUFSIZE, remainder)).LastRead();
702 }
703
704 m_pos = wxInvalidOffset;
705 m_offset += size;
706 m_lasterror = m_parent_i_stream->GetLastError();
707
708 return IsOk();
709}
710
711wxStreamError wxTarInputStream::ReadHeaders()
712{
713 if (!CloseEntry())
714 return wxSTREAM_READ_ERROR;
715
716 bool done = false;
717
718 while (!done) {
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;
725
726 // an all-zero header marks the end of the tar
727 if (m_hdr->IsAllZeros())
728 return wxSTREAM_EOF;
729
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);
734 bool ok = false;
735
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;
740 }
741 if (m_sumType == SUM_SIGNED)
742 ok = chksum == m_hdr->Sum(true);
743 if (!ok) {
744 wxLogError(_("checksum failure reading tar header block"));
745 return wxSTREAM_READ_ERROR;
746 }
747
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;
753 else
754 m_tarType = TYPE_OLDTAR;
755
756 if (m_tarType != TYPE_USTAR)
757 break;
758
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;
763 }
764 }
765
766 return wxSTREAM_NO_ERROR;
767}
768
769wxString wxTarInputStream::GetExtendedHeader(const wxString& key) const
770{
771 wxTarHeaderRecords::iterator it;
772
773 // look at normal extended header records first
774 if (m_HeaderRecs) {
775 it = m_HeaderRecs->find(key);
776 if (it != m_HeaderRecs->end())
777 return wxString(it->second.wc_str(wxConvUTF8), GetConv());
778 }
779
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());
785 }
786
787 return wxEmptyString;
788}
789
790wxString wxTarInputStream::GetHeaderPath() const
791{
792 wxString path;
793
794 if ((path = GetExtendedHeader(_T("path"))) != wxEmptyString)
795 return path;
796
797 path = wxString(m_hdr->Get(TAR_NAME), GetConv());
798 if (m_tarType != TYPE_USTAR)
799 return path;
800
801 const char *prefix = m_hdr->Get(TAR_PREFIX);
802 return *prefix ? wxString(prefix, GetConv()) + _T("/") + path : path;
803}
804
805wxDateTime wxTarInputStream::GetHeaderDate(const wxString& key) const
806{
807 wxString value;
808
809 // try extended header, stored as decimal seconds since the epoch
810 if ((value = GetExtendedHeader(key)) != wxEmptyString) {
811 wxLongLong ll;
812 ll.Assign(wxAtof(value) * 1000.0);
813 return ll;
814 }
815
816 if (key == _T("mtime"))
817 return wxLongLong(m_hdr->GetOctal(TAR_MTIME)) * 1000L;
818
819 return wxDateTime();
820}
821
822wxTarNumber wxTarInputStream::GetHeaderNumber(int id) const
823{
824 wxString value;
825
826 if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString) {
827 wxTarNumber n = 0;
828 const wxChar *p = value;
829 while (*p == ' ')
830 p++;
831 while (isdigit(*p))
832 n = n * 10 + (*p++ - '0');
833 return n;
834 } else {
835 return m_hdr->GetOctal(id);
836 }
837}
838
839wxString wxTarInputStream::GetHeaderString(int id) const
840{
841 wxString value;
842
843 if ((value = GetExtendedHeader(m_hdr->Name(id))) != wxEmptyString)
844 return value;
845
846 return wxString(m_hdr->Get(id), GetConv());
847}
848
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
852
853bool wxTarInputStream::ReadExtendedHeader(wxTarHeaderRecords*& recs)
854{
855 if (!recs)
856 recs = new wxTarHeaderRecords;
857
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);
861
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();
865 if (lastread < len)
866 len = lastread;
867 buf.data()[len] = 0;
868 m_offset += lastread;
869
870 size_t recPos, recSize;
871 bool ok = true;
872
873 for (recPos = 0; recPos < len; recPos += recSize) {
874 char *pRec = buf.data() + recPos;
875 char *p = pRec;
876
877 // read the record size (byte count in ascii decimal)
878 recSize = 0;
879 while (isdigit(*p))
880 recSize = recSize * 10 + *p++ - '0';
881
882 // validity checks
883 if (recPos + recSize > len)
884 break;
885 if (recSize < p - pRec + (size_t)3 || *p != ' '
886 || pRec[recSize - 1] != '\012') {
887 ok = false;
888 continue;
889 }
890
891 // replace the final '\n' with a nul, to terminate value
892 pRec[recSize - 1] = 0;
893 // the key is here, following the space
894 char *pKey = ++p;
895
896 // look forward for the '=', the value follows
897 while (*p && *p != '=')
898 p++;
899 if (!*p) {
900 ok = false;
901 continue;
902 }
903 // replace the '=' with a nul, to terminate the key
904 *p++ = 0;
905
906 wxString key(wxConvUTF8.cMB2WC(pKey), GetConv());
907 wxString value(wxConvUTF8.cMB2WC(p), GetConv());
908
909 // an empty value unsets a previously given value
910 if (value.empty())
911 recs->erase(key);
912 else
913 (*recs)[key] = value;
914 }
915
916 if (!ok || recPos < len || size != lastread) {
917 wxLogWarning(_("invalid data in extended tar header"));
918 return false;
919 }
920
921 return true;
922}
923
924wxFileOffset wxTarInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
925{
926 if (!IsOpened()) {
927 wxLogError(_("tar entry not open"));
928 m_lasterror = wxSTREAM_READ_ERROR;
929 }
930 if (!IsOk())
931 return wxInvalidOffset;
932
933 switch (mode) {
934 case wxFromStart: break;
935 case wxFromCurrent: pos += m_pos; break;
936 case wxFromEnd: pos += m_size; break;
937 }
938
939 if (pos < 0 || m_parent_i_stream->SeekI(m_offset + pos) == wxInvalidOffset)
940 return wxInvalidOffset;
941
942 m_pos = pos;
943 return m_pos;
944}
945
946size_t wxTarInputStream::OnSysRead(void *buffer, size_t size)
947{
948 if (!IsOpened()) {
949 wxLogError(_("tar entry not open"));
950 m_lasterror = wxSTREAM_READ_ERROR;
951 }
952 if (!IsOk() || !size)
953 return 0;
954
955 if (m_pos >= m_size)
956 size = 0;
957 else if (m_pos + size > m_size + (size_t)0)
958 size = m_size - m_pos;
959
960 size_t lastread = m_parent_i_stream->Read(buffer, size).LastRead();
961 m_pos += lastread;
962
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;
970 }
971
972 return lastread;
973}
974
975
976/////////////////////////////////////////////////////////////////////////////
977// Output stream
978
979wxTarOutputStream::wxTarOutputStream(wxOutputStream& stream,
980 wxTarFormat format /*=wxTAR_PAX*/,
981 wxMBConv& conv /*=wxConvLocal*/)
982 : wxArchiveOutputStream(stream, conv)
983{
984 Init(format);
985}
986
987wxTarOutputStream::wxTarOutputStream(wxOutputStream *stream,
988 wxTarFormat format /*=wxTAR_PAX*/,
989 wxMBConv& conv /*=wxConvLocal*/)
990 : wxArchiveOutputStream(stream, conv)
991{
992 Init(format);
993}
994
995void wxTarOutputStream::Init(wxTarFormat format)
996{
997 m_pos = wxInvalidOffset;
998 m_maxpos = wxInvalidOffset;
999 m_size = wxInvalidOffset;
1000 m_headpos = wxInvalidOffset;
1001 m_datapos = wxInvalidOffset;
1002 m_tarstart = wxInvalidOffset;
1003 m_tarsize = 0;
1004 m_pax = format == wxTAR_PAX;
1005 m_BlockingFactor = m_pax ? 10 : 20;
1006 m_chksum = 0;
1007 m_large = false;
1008 m_hdr = new wxTarHeaderBlock;
1009 m_hdr2 = NULL;
1010 m_extendedHdr = NULL;
1011 m_extendedSize = 0;
1012 m_lasterror = m_parent_o_stream->GetLastError();
1013}
1014
1015wxTarOutputStream::~wxTarOutputStream()
1016{
1017 if (m_tarsize)
1018 Close();
1019 delete m_hdr;
1020 delete m_hdr2;
1021 delete [] m_extendedHdr;
1022}
1023
1024bool wxTarOutputStream::PutNextEntry(wxTarEntry *entry)
1025{
1026 wxTarEntryPtr_ e(entry);
1027
1028 if (!CloseEntry())
1029 return false;
1030
1031 if (!m_tarsize) {
1032 wxLogNull nolog;
1033 m_tarstart = m_parent_o_stream->TellO();
1034 }
1035
1036 if (m_tarstart != wxInvalidOffset)
1037 m_headpos = m_tarstart + m_tarsize;
1038
1039 if (WriteHeaders(*e)) {
1040 m_pos = 0;
1041 m_maxpos = 0;
1042 m_size = GetDataSize(*e);
1043 if (m_tarstart != wxInvalidOffset)
1044 m_datapos = m_tarstart + m_tarsize;
1045
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
1050 };
ea923162 1051 int typeflag = e->GetTypeFlag();
dd1462c3
MW
1052
1053 // pax does now allow data for wxTAR_LNKTYPE
ea923162 1054 if (!m_pax || typeflag != wxTAR_LNKTYPE)
dd1462c3
MW
1055 if (strchr(nodata, typeflag) != NULL)
1056 CloseEntry();
1057 }
1058
1059 return IsOk();
1060}
1061
1062bool wxTarOutputStream::PutNextEntry(const wxString& name,
1063 const wxDateTime& dt,
1064 wxFileOffset size)
1065{
1066 return PutNextEntry(new wxTarEntry(name, dt, size));
1067}
1068
1069bool wxTarOutputStream::PutNextDirEntry(const wxString& name,
1070 const wxDateTime& dt)
1071{
1072 wxTarEntry *entry = new wxTarEntry(name, dt);
1073 entry->SetIsDir();
1074 return PutNextEntry(entry);
1075}
1076
1077bool wxTarOutputStream::PutNextEntry(wxArchiveEntry *entry)
1078{
1079 wxTarEntry *tarEntry = wxStaticCast(entry, wxTarEntry);
1080 if (!tarEntry)
1081 delete entry;
1082 return PutNextEntry(tarEntry);
1083}
1084
1085bool wxTarOutputStream::CopyEntry(wxTarEntry *entry,
1086 wxTarInputStream& inputStream)
1087{
1088 if (PutNextEntry(entry))
1089 Write(inputStream);
1090 return IsOk() && inputStream.Eof();
1091}
1092
1093bool wxTarOutputStream::CopyEntry(wxArchiveEntry *entry,
1094 wxArchiveInputStream& inputStream)
1095{
1096 if (PutNextEntry(entry))
1097 Write(inputStream);
1098 return IsOk() && inputStream.Eof();
1099}
1100
1101bool wxTarOutputStream::CloseEntry()
1102{
1103 if (!IsOpened())
1104 return true;
1105
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();
1110 m_pos = m_maxpos;
1111 }
1112
1113 if (IsOk()) {
1114 wxFileOffset size = RoundUpSize(m_pos);
1115 if (size > 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();
1119 }
1120 m_tarsize += size;
1121 }
1122
1123 if (IsOk() && m_pos != m_size)
1124 ModifyHeader();
1125
1126 m_pos = wxInvalidOffset;
1127 m_maxpos = wxInvalidOffset;
1128 m_size = wxInvalidOffset;
1129 m_headpos = wxInvalidOffset;
1130 m_datapos = wxInvalidOffset;
1131
1132 return IsOk();
1133}
1134
1135bool wxTarOutputStream::Close()
1136{
1137 if (!CloseEntry())
1138 return false;
1139
1140 memset(m_hdr, 0, sizeof(*m_hdr));
1141 int count = (RoundUpSize(m_tarsize + 2 * TAR_BLOCKSIZE, m_BlockingFactor)
1142 - m_tarsize) / TAR_BLOCKSIZE;
1143 while (count--)
1144 m_parent_o_stream->Write(m_hdr, TAR_BLOCKSIZE);
1145
1146 m_tarsize = 0;
1147 m_tarstart = wxInvalidOffset;
1148 m_lasterror = m_parent_o_stream->GetLastError();
1149 return IsOk();
1150}
1151
1152bool wxTarOutputStream::WriteHeaders(wxTarEntry& entry)
1153{
1154 memset(m_hdr, 0, sizeof(*m_hdr));
1155
1156 SetHeaderPath(entry.GetName(wxPATH_UNIX));
1157
1158 SetHeaderNumber(TAR_MODE, entry.GetMode());
1159 SetHeaderNumber(TAR_UID, entry.GetUserId());
1160 SetHeaderNumber(TAR_GID, entry.GetGroupId());
1161
1162 if (entry.GetSize() == wxInvalidOffset)
1163 entry.SetSize(0);
1164 m_large = SetHeaderNumber(TAR_SIZE, entry.GetSize());
1165
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());
1171
ea923162 1172 *m_hdr->Get(TAR_TYPEFLAG) = char(entry.GetTypeFlag());
dd1462c3
MW
1173
1174 strcpy(m_hdr->Get(TAR_MAGIC), USTAR_MAGIC);
1175 strcpy(m_hdr->Get(TAR_VERSION), USTAR_VERSION);
1176
1177 SetHeaderString(TAR_LINKNAME, entry.GetLinkName());
1178 SetHeaderString(TAR_UNAME, entry.GetUserName());
1179 SetHeaderString(TAR_GNAME, entry.GetGroupName());
1180
1181 if (~entry.GetDevMajor())
1182 SetHeaderNumber(TAR_DEVMAJOR, entry.GetDevMajor());
1183 if (~entry.GetDevMinor())
1184 SetHeaderNumber(TAR_DEVMINOR, entry.GetDevMinor());
1185
1186 m_chksum = m_hdr->Sum();
1187 m_hdr->SetOctal(TAR_CHKSUM, m_chksum);
1188 if (!m_large)
1189 m_chksum -= m_hdr->SumField(TAR_SIZE);
1190
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
1193 // the main header.
1194 if (m_extendedHdr && *m_extendedHdr) {
1195 wxASSERT(m_pax);
1196 // the extended headers are written to the tar as a file entry,
1197 // so prepare a regular header block for the pseudo-file.
1198 if (!m_hdr2)
1199 m_hdr2 = new wxTarHeaderBlock;
1200 memset(m_hdr2, 0, sizeof(*m_hdr2));
1201
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));
1218
1219 m_hdr2->SetOctal(TAR_CHKSUM, m_hdr2->Sum());
1220
1221 m_hdr2->Write(*m_parent_o_stream);
1222 m_tarsize += TAR_BLOCKSIZE;
1223
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;
1228
1229 *m_extendedHdr = 0;
1230 }
1231
1232 // if don't have extended headers just report error
1233 if (!m_badfit.empty()) {
1234 wxASSERT(!m_pax);
1235 wxLogWarning(_("%s did not fit the tar header for entry '%s'"),
1236 m_badfit.c_str(), entry.GetName().c_str());
1237 m_badfit.clear();
1238 }
1239
1240 m_hdr->Write(*m_parent_o_stream);
1241 m_tarsize += TAR_BLOCKSIZE;
1242 m_lasterror = m_parent_o_stream->GetLastError();
1243
1244 return IsOk();
1245}
1246
1247wxString wxTarOutputStream::PaxHeaderPath(const wxString& format,
1248 const wxString& path)
1249{
1250 wxString d = path.BeforeLast(_T('/'));
1251 wxString f = path.AfterLast(_T('/'));
1252 wxString ret;
1253
1254 if (d.empty())
1255 d = _T(".");
1256
1257 ret.reserve(format.length() + path.length() + 16);
1258
1259 size_t begin = 0;
1260 size_t end;
1261
1262 for (;;) {
1263 end = format.find('%', begin);
1264 if (end == wxString::npos || end + 1 >= format.length())
1265 break;
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;
1272 }
1273 begin = end + 2;
1274 }
1275
1276 ret << format.substr(begin);
1277
1278 return ret;
1279}
1280
1281bool wxTarOutputStream::ModifyHeader()
1282{
1283 wxFileOffset originalPos = wxInvalidOffset;
1284 wxFileOffset sizePos = wxInvalidOffset;
1285
1286 if (!m_large && m_headpos != wxInvalidOffset
1287 && m_parent_o_stream->IsSeekable())
1288 {
1289 wxLogNull nolog;
1290 originalPos = m_parent_o_stream->TellO();
1291 if (originalPos != wxInvalidOffset)
1292 sizePos =
1293 m_parent_o_stream->SeekO(m_headpos + m_hdr->Offset(TAR_SIZE));
1294 }
1295
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;
1299 return false;
1300 }
1301
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);
1305
1306 return
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;
1311}
1312
1313void wxTarOutputStream::SetHeaderPath(const wxString& name)
1314{
1315 if (!m_hdr->SetPath(name, GetConv()) || (m_pax && !name.IsAscii()))
1316 SetExtendedHeader(_T("path"), name);
1317}
1318
1319bool wxTarOutputStream::SetHeaderNumber(int id, wxTarNumber n)
1320{
1321 if (m_hdr->SetOctal(id, n)) {
1322 return true;
1323 } else {
1324 SetExtendedHeader(m_hdr->Name(id), wxLongLong(n).ToString());
1325 return false;
1326 }
1327}
1328
1329void wxTarOutputStream::SetHeaderString(int id, const wxString& str)
1330{
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);
1334}
1335
1336void wxTarOutputStream::SetHeaderDate(const wxString& key,
1337 const wxDateTime& datetime)
1338{
1339 wxLongLong ll = datetime.IsValid() ? datetime.GetValue() : wxLongLong(0);
1340 wxLongLong secs = ll / 1000L;
1341
ea923162
MW
1342 if (key != _T("mtime")
1343 || !m_hdr->SetOctal(TAR_MTIME, wxTarNumber(secs.GetValue()))
dd1462c3
MW
1344 || secs <= 0 || secs >= 0x7fffffff)
1345 {
1346 wxString str;
1347 if (ll >= LONG_MIN && ll <= LONG_MAX) {
1348 str.Printf(_T("%g"), ll.ToLong() / 1000.0);
1349 } else {
1350 str = ll.ToString();
1351 str.insert(str.end() - 3, '.');
1352 }
1353 SetExtendedHeader(key, str);
1354 }
1355}
1356
1357void wxTarOutputStream::SetExtendedHeader(const wxString& key,
1358 const wxString& value)
1359{
1360 if (m_pax) {
1361 const wxWX2WCbuf wide_key = key.wc_str(GetConv());
1362 const wxCharBuffer utf_key = wxConvUTF8.cWC2MB(wide_key);
1363
1364 const wxWX2WCbuf wide_value = value.wc_str(GetConv());
1365 const wxCharBuffer utf_value = wxConvUTF8.cWC2MB(wide_value);
1366
1367 // a small buffer to format the length field in
1368 char buf[32];
1369 // length of "99<space><key>=<value>\n"
1370 unsigned long length = strlen(utf_value) + strlen(utf_key) + 5;
1371 sprintf(buf, "%lu", length);
1372 // the length includes itself
1373 size_t lenlen = strlen(buf);
1374 if (lenlen != 2) {
1375 length += lenlen - 2;
1376 sprintf(buf, "%lu", length);
1377 if (strlen(buf) > lenlen)
1378 sprintf(buf, "%lu", ++length);
1379 }
1380
1381 // reallocate m_extendedHdr if it's not big enough
1382 if (m_extendedSize < length) {
1383 size_t rounded = RoundUpSize(length);
1384 m_extendedSize <<= 1;
1385 if (rounded > m_extendedSize)
1386 m_extendedSize = rounded;
1387 char *oldHdr = m_extendedHdr;
1388 m_extendedHdr = new char[m_extendedSize];
1389 if (oldHdr) {
1390 strcpy(m_extendedHdr, oldHdr);
1391 delete oldHdr;
1392 } else {
1393 *m_extendedHdr = 0;
1394 }
1395 }
1396
1397 // append the new record
1398 char *append = strchr(m_extendedHdr, 0);
1399 sprintf(append, "%s %s=%s\012", buf,
1400 (const char*)utf_key, (const char*)utf_value);
1401 }
1402 else {
1403 // if not pax then make a list of fields to report as errors
1404 if (!m_badfit.empty())
1405 m_badfit += _T(", ");
1406 m_badfit += key;
1407 }
1408}
1409
1410void wxTarOutputStream::Sync()
1411{
1412 m_parent_o_stream->Sync();
1413}
1414
1415wxFileOffset wxTarOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
1416{
1417 if (!IsOpened()) {
1418 wxLogError(_("tar entry not open"));
1419 m_lasterror = wxSTREAM_WRITE_ERROR;
1420 }
1421 if (!IsOk() || m_datapos == wxInvalidOffset)
1422 return wxInvalidOffset;
1423
1424 switch (mode) {
1425 case wxFromStart: break;
1426 case wxFromCurrent: pos += m_pos; break;
1427 case wxFromEnd: pos += m_maxpos; break;
1428 }
1429
1430 if (pos < 0 || m_parent_o_stream->SeekO(m_datapos + pos) == wxInvalidOffset)
1431 return wxInvalidOffset;
1432
1433 m_pos = pos;
1434 return m_pos;
1435}
1436
1437size_t wxTarOutputStream::OnSysWrite(const void *buffer, size_t size)
1438{
1439 if (!IsOpened()) {
1440 wxLogError(_("tar entry not open"));
1441 m_lasterror = wxSTREAM_WRITE_ERROR;
1442 }
1443 if (!IsOk() || !size)
1444 return 0;
1445
1446 size_t lastwrite = m_parent_o_stream->Write(buffer, size).LastWrite();
1447 m_pos += lastwrite;
1448 if (m_pos > m_maxpos)
1449 m_maxpos = m_pos;
1450
1451 if (lastwrite != size)
1452 m_lasterror = wxSTREAM_WRITE_ERROR;
1453
1454 return lastwrite;
1455}
1456
1457#endif // wxUSE_TARSTREAM