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