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