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