More specific error message
[rarfs.git] / src / rararchive.cc
blob51a37cb0f553d3f1f895285398f228452fb8362f
1 /***************************************************************************
2 * Copyright (C) 2006-2008 Kent Gustavsson <nedo80@gmail.com>
3 ****************************************************************************/
4 /*
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 // Class: RARArchive
24 // Created by: Kent Gustavsson <nedo80@gmail.com>
25 // Created on: Sun Mar 5 00:40:27 2006
28 #include "rararchive.h"
29 #include <fstream>
30 #include <sstream>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <cstdlib>
36 #include <time.h>
38 RARArchive::RARArchive()
40 default_date = 0;
44 RARArchive::~RARArchive()
46 std::vector <std::ifstream*>::iterator iter;
48 for( iter = streams.begin() ; iter != streams.end() ; iter++)
50 (*iter)->close();
54 int
55 RARArchive::Init(std::string filename)
57 struct stat st;
58 stat(filename.c_str(), &st);
59 default_date = st.st_mtime;
61 // Hate this part
62 this->filename = filename;
64 volsuffix = ".rar";
65 voldigits = 0;
66 std::string numprefix;
67 std::size_t offset;
69 offset = volsuffix.size();
70 if ( offset <= filename.size() &&
71 filename.substr(filename.size()-offset) == volsuffix )
73 /* Ends in ".rar"; check for "part<N>.rar" */
74 numprefix = "part";
76 else
78 /* Try ".<N>" */
79 volsuffix = "";
80 numprefix = ".";
83 while(true)
85 offset = voldigits+volsuffix.size();
86 if ( offset >= filename.size() ||
87 !isdigit(filename[filename.size()-offset-1]))
89 break;
92 voldigits++;
93 if ( voldigits > 5 )
95 std::cerr << "Too many digits in volume number" <<
96 std::endl;
97 return false;
101 offset = numprefix.size() + voldigits + volsuffix.size();
102 if ( offset <= filename.size() && filename.substr(
103 filename.size() - offset, numprefix.size()) == numprefix )
105 offset = volsuffix.size()+voldigits;
106 volprefix = filename.substr(0, filename.size()-offset);
107 firstfile = atoi( filename.substr(
108 filename.size()-offset, voldigits).c_str() );
110 else
111 voldigits = 0;
113 return true;
116 std::string
117 RARArchive::GetFileName(int n)
119 if ( n == 0 )
120 return filename;
122 std::stringstream f;
124 if ( voldigits == 0 )
126 /* n=0 => .rar (see above), n=1 => .r00, n=2 => .r01, etc */
127 if( n-1 < 10 )
128 f << filename.substr(0,filename.size()-2) << 0 << n-1;
129 else
130 f << filename.substr(0,filename.size()-2) << n-1;
132 else
134 char prev;
136 f << volprefix;
138 prev = f.fill ('0');
139 f.width (voldigits);
140 f << n + firstfile;
141 f.fill(prev);
142 f.width(0);
144 f << volsuffix;
147 return f.str();
150 bool
151 RARArchive::HasFile(std::string f)
153 if( fileblocks.find(f) == fileblocks.end() )
154 return false;
155 return true;
159 bool
160 RARArchive::HasFolder(std::string f)
162 if( folderblocks.find(f) == folderblocks.end() )
163 return false;
164 return true;
167 unsigned long long int
168 RARArchive::GetFileSize(std::string file)
170 unsigned long long int size = 0;
171 if( fileblocks.find(file) == fileblocks.end() )
172 return 0;
174 std::vector <FileBlock *>::iterator i;
175 for(i = fileblocks[file].begin() ; i != fileblocks[file].end() ; i++ )
177 size += (*i)->GetDataSize();
180 return size;
183 void
184 RARArchive::Parse(bool showcompressed)
186 int n = 0;
187 for(;;)
189 std::ifstream *file = new std::ifstream(GetFileName(n++).c_str());
190 if(!file->good())
191 return;
192 streams.push_back(file);
194 for(;;)
196 if(!file->good())
197 break;
199 char buf[4];
200 file->read(buf,3);
201 //for some rar files there are seeks past file end, then directly to file end
202 //which makes file->good() return true, but there is nothing to read
203 //so with file->gcount() we check if we actually read something
204 //otherwise we use old bufer and crash somewhere
205 if(file->gcount()==0)
206 break;
208 file->seekg (-3, std::ios::cur);
210 switch( buf[2] )
212 case 0x00:
213 break;
214 case 0x72:
215 blocks.push_back( new MarkerBlock(*file) );
216 break;
217 case 0x73:
218 blocks.push_back( new ArchiveBlock(*file) );
219 break;
220 case 0x74:
221 FileBlock *f;
222 f = new FileBlock(*file);
224 if ( showcompressed || !f->isCompressed() )
226 if ( f->isFolder() )
227 folderblocks[f->GetFileName()].push_back(f);
228 else
230 fileblocks[f->GetFileName()].push_back(f);
232 for ( int pos = 0 ; ( pos = f->GetFileName().find("\\", pos) ) != std::string::npos ; pos++)
234 if( folderblocks.find(f->GetFileName().substr(0,pos)) == folderblocks.end())
235 folderblocks[f->GetFileName().substr(0,pos)].push_back(NULL);
240 blocks.push_back( f );
242 break;
243 default:
244 blocks.push_back( new RARBlock(*file) );
247 if ( buf[2] == 0 || buf[2] == 0x7B || buf[2] == 0x78 )
250 break;
253 file->clear();
254 file->seekg(0);
259 unsigned int
260 RARArchive::Read(const char *path, char *buf, size_t size, off_t offset)
262 unsigned int pos = 0;
263 if ( fileblocks.find(path) == fileblocks.end() )
264 // return -ENOENT;
265 return -1;
267 std::vector <FileBlock *>::iterator i;
268 for(i = fileblocks[path].begin() ; i != fileblocks[path].end() ; i++ )
270 if ( (*i)->GetDataSize() > offset )
272 unsigned int len = (*i)->GetData(buf + pos, offset, size);
273 pos += len;
274 size -= len;
275 offset = 0;
277 else
279 offset -= (*i)->GetDataSize();
282 return pos;
285 std::vector < std::string >
286 RARArchive::GetFolders()
288 std::vector <std::string> retdata;
290 std::map<std::string, std::vector <FileBlock *> >::iterator iter;
292 for( iter = folderblocks.begin() ; iter != folderblocks.end() ; iter++)
293 retdata.push_back(iter->first);
296 return retdata;
299 std::vector < std::string >
300 RARArchive::GetFiles()
302 std::vector <std::string> retdata;
303 std::map<std::string, std::vector <FileBlock * > >::iterator iter;
305 for( iter = fileblocks.begin() ; iter != fileblocks.end() ; iter++)
307 retdata.push_back(iter->first);
310 return retdata;
313 void
314 RARArchive::PrintFiles()
318 std::map<std::string, std::vector <FileBlock * > >::iterator iter;
320 for( iter = fileblocks.begin() ; iter != fileblocks.end() ; iter++)
322 unsigned long long int s = 0;
323 std::vector <FileBlock *>::iterator i;
324 for(i = iter->second.begin() ; i != iter->second.end() ; i++ )
326 s += (*i)->GetDataSize();
329 std::cout << iter->first << " blocks: " << iter->second.size() << " size: " << s << std::endl;
333 void
334 RARArchive::GetDate(std::string file, struct timespec* tp)
336 if( fileblocks.find(file) == fileblocks.end() )
338 tp->tv_sec = default_date;
339 tp->tv_nsec = 0;
340 return;
343 std::vector <FileBlock *>::iterator i;
344 i = fileblocks[file].begin();
345 (*i)->GetFileDate(tp);
348 void
349 RARArchive::PrintFolders()
351 std::map<std::string, std::vector <FileBlock * > >::iterator iter;
353 for( iter = folderblocks.begin() ; iter != folderblocks.end() ; iter++)
355 unsigned long long int s = 0;
356 std::vector <FileBlock *>::iterator i;
357 for(i = iter->second.begin() ; i != iter->second.end() ; i++ )
358 s += (*i)->GetDataSize();
360 std::cout << iter->first << " blocks: " << iter->second.size() << " size: " << s << std::endl;