1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 * $Revision: 7109 $
6 * $Id: mytar.cpp 7109 2011-04-15 11:53:16Z mortenmacfly $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/contrib/devpak_plugin/mytar.cpp $
8 */
9
10 #include "mytar.h"
11 #include <io.h>
12 #include <globals.h>
13 #include <wx/intl.h>
14 #include <wx/arrimpl.cpp>
15 WX_DEFINE_OBJARRAY(ReplacersArray);
16
TAR(const wxString & filename)17 TAR::TAR(const wxString& filename)
18 : m_pFile(0),
19 m_SkipBytes(0),
20 m_Size(0)
21 {
22 if (!filename.IsEmpty())
23 Open(filename);
24 }
25
~TAR()26 TAR::~TAR()
27 {
28 Close();
29 }
30
Open(const wxString & filename)31 bool TAR::Open(const wxString& filename)
32 {
33 if (filename.IsEmpty())
34 return false;
35 Close();
36
37 m_pFile = fopen(filename.mb_str(), "rb");
38 if (!m_pFile)
39 return false;
40
41 fseek(m_pFile, 0, SEEK_END);
42 m_Size = ftell(m_pFile);
43 fseek(m_pFile, 0, SEEK_SET);
44
45 return true;
46 }
47
Close()48 void TAR::Close()
49 {
50 if (m_pFile)
51 fclose(m_pFile);
52 m_pFile = 0;
53 Reset();
54 m_Size = 0;
55 }
56
Reset()57 void TAR::Reset()
58 {
59 if (m_pFile)
60 fseek(m_pFile, 0, SEEK_SET);
61 m_SkipBytes = 0;
62 }
63
OctToInt(const char * oct)64 int TAR::OctToInt(const char* oct)
65 {
66 int i = 0;
67 if (sscanf(oct, "%o", &i) != 1)
68 i = 0;
69 // return 1;
70 return i;
71 }
72
OffsetRecords(size_t bytes)73 size_t TAR::OffsetRecords(size_t bytes)
74 {
75 size_t i = bytes / sizeof(TAR::Header);
76 if (bytes % sizeof(TAR::Header) > 0)
77 ++i;
78 return i;
79 }
80
Next(TAR::Record * rec)81 bool TAR::Next(TAR::Record* rec)
82 {
83 if (!rec)
84 return false;
85 rec->name.Clear();
86 rec->size = 0;
87 rec->pos = 0;
88 if (m_SkipBytes > 0)
89 fseek(m_pFile, OffsetRecords(m_SkipBytes) * sizeof(TAR::Header), SEEK_CUR);
90 TAR::Header buffer;
91 memset(&buffer, 0, sizeof(TAR::Header));
92
93 // reached end of file?
94 size_t pos = ftell(m_pFile);
95 if (pos + sizeof(buffer) > m_Size)
96 return false; // yes
97 if (fread(&buffer, sizeof(buffer), 1, m_pFile) != 1)
98 return false;
99
100 rec->pos = pos;
101 rec->name = cbC2U(buffer.name);
102 rec->size = OctToInt(buffer.size);
103
104 #if 1
105 // many DevPaks, end with a single null record...
106 if (buffer.name[0] == 0)
107 return false;
108 #else // 0
109 // 2 consecutive nulls means EOT
110 static bool previousWasNull = false;
111 if (rec->name.IsEmpty())
112 {
113 rec->name.Clear();
114 if (previousWasNull)
115 return false; // EOT
116 else
117 {
118 previousWasNull = true;
119 m_SkipBytes = rec->size;
120 return true;
121 }
122 }
123 else
124 previousWasNull = false; // reset flag
125 #endif // 0
126
127 switch (buffer.typeflag)
128 {
129 case 0:
130 case _T('0'): rec->ft = ftNormal; break;
131 case _T('1'): rec->ft = ftLink; break;
132 case _T('2'): rec->ft = ftSymbolicLink; break;
133 case _T('3'): rec->ft = ftCharacter; break;
134 case _T('4'): rec->ft = ftBlock; break;
135 case _T('5'): rec->ft = ftDirectory; break;
136 case _T('6'): rec->ft = ftFifo; break;
137 case _T('7'): rec->ft = ftContiguous; break;
138 case _T('D'): rec->ft = ftDumpDir; break;
139 case _T('M'): rec->ft = ftMultiVolume; break;
140 case _T('V'): rec->ft = ftVolumeHeader; break;
141 // case _T('L'): rec.ft = ftLongName; break;
142 // case _T('K'): rec.ft = ftLongLink; break;
143 default: break;
144 }
145
146 switch (rec->ft)
147 {
148 case ftLink:
149 case ftSymbolicLink:
150 case ftDirectory:
151 case ftFifo:
152 case ftVolumeHeader:
153 m_SkipBytes = 0;
154 break;
155 default:
156 m_SkipBytes = rec->size;
157 break;
158 }
159 return true;
160 }
161
ExtractAll(const wxString & dirname,wxString & status,wxArrayString * files)162 bool TAR::ExtractAll(const wxString& dirname, wxString& status, wxArrayString* files)
163 {
164 Reset();
165 status.Clear();
166 if (files)
167 files->Clear();
168 TAR::Record r;
169 while (Next(&r))
170 {
171 wxString convertedFile;
172 if (!ExtractFile(&r, dirname, status, &convertedFile))
173 {
174 status << _("Failed extracting") << _T(" \"") << r.name << _T("\"\n");
175 return false;
176 }
177 if (files && !convertedFile.IsEmpty())
178 files->Add(convertedFile);
179 }
180 return true;
181 }
182
ClearReplacers()183 void TAR::ClearReplacers()
184 {
185 m_Replacers.Clear();
186 }
187
AddReplacer(const wxString & from,const wxString & to)188 void TAR::AddReplacer(const wxString& from, const wxString& to)
189 {
190 Replacers r;
191 r.from = from;
192 if (r.from.Last() != _T('/'))
193 r.from << _T('/');
194 r.to = to;
195 r.to.Replace(_T("<app>"), _T(""));
196
197 // avoid duplicates
198 for (unsigned int i = 0; i < m_Replacers.GetCount(); ++i)
199 {
200 if (m_Replacers[i].from == r.from)
201 return;
202 }
203
204 m_Replacers.Add(r);
205 }
206
ReplaceThings(wxString & path)207 void TAR::ReplaceThings(wxString& path)
208 {
209 while (path.Replace(_T("\\"), _T("/")))
210 ;
211 for (unsigned int i = 0; i < m_Replacers.GetCount(); ++i)
212 path.Replace(m_Replacers[i].from, m_Replacers[i].to);
213 while (path.Replace(_T("\\"), _T("/")))
214 ;
215 while (path.Replace(_T("//"), _T("/")))
216 ;
217 }
218
ExtractFile(Record * rec,const wxString & dirname,wxString & status,wxString * convertedFile)219 bool TAR::ExtractFile(Record* rec, const wxString& dirname, wxString& status, wxString* convertedFile)
220 {
221 if (!rec)
222 return false;
223
224 if (convertedFile)
225 convertedFile->Clear();
226 wxString path;
227 if (rec->name.IsEmpty())
228 return true;
229 if (!dirname.IsEmpty())
230 {
231 path << dirname << _T("/");
232 }
233 path << rec->name;
234 ReplaceThings(path);
235
236 switch (rec->ft)
237 {
238 case ftNormal:
239 {
240 CreateDirRecursively(path);
241 status << _("Unpacking ") << path << _T('\n');
242 if (convertedFile)
243 *convertedFile = path;
244
245 FILE* out = fopen(path.mb_str(), "wb");
246 if (!out)
247 {
248 status << wxString(_("Can't open file ")) << path << _T("\n");
249 return false;
250 }
251 if (rec->size > 0)
252 {
253 size_t oldpos = ftell(m_pFile);
254 char* buffer = new char[rec->size];
255 memset(buffer, 0, rec->size);
256 if (fread(buffer, rec->size, 1, m_pFile) != 1)
257 {
258 delete[] buffer;
259 fclose(out);
260 fseek(m_pFile, oldpos, SEEK_SET);
261 status << _("Failure reading file ") << path << _T("\n");
262 return false;
263 }
264 fwrite(buffer, rec->size, 1, out);
265 delete[] buffer;
266 fseek(m_pFile, oldpos, SEEK_SET);
267 }
268 fclose(out);
269 break;
270 }
271
272 default: break;
273 }
274
275 return true;
276 }
277
FindFile(const wxString & filename)278 TAR::Record* TAR::FindFile(const wxString& filename)
279 {
280 if (filename.IsEmpty())
281 return 0;
282 Reset();
283 static TAR::Record r;
284 while (Next(&r))
285 {
286 if (r.name.CmpNoCase(filename) == 0 ||
287 r.name.Matches(filename)) // support wildcards
288 {
289 return &r;
290 }
291 }
292 return 0;
293 }
294