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