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