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