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