summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/paq.cpp161
1 files changed, 145 insertions, 16 deletions
diff --git a/contrib/paq.cpp b/contrib/paq.cpp
index cd4fb0d..433e2bb 100644
--- a/contrib/paq.cpp
+++ b/contrib/paq.cpp
@@ -6,9 +6,16 @@
#include <exception>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
+#include <boost/iostreams/copy.hpp>
+#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>
-#include <boost/iostreams/copy.hpp>
+
+#ifdef _WIN32
+ #define DIR_SEPARATOR "\\"
+#else
+ #define DIR_SEPARTOR "/"
+#endif
using namespace std;
@@ -22,17 +29,109 @@ public:
virtual const char* what() const { return cause.c_str(); }
};
-class paq_builder
+struct index_entry
+{
+ index_entry(const string& file_name, bool is_dir) : file_name(file_name), is_dir(is_dir), file_size(0) { }
+
+ unsigned int file_size;
+ string file_name;
+ bool is_dir;
+};
+
+class paq_extractor
{
private:
- struct index_entry
+ list<index_entry> index;
+ vector<char> files;
+
+ void process(const vector<char>& v)
+ {
+ unsigned int i = 4, depth = 1;
+ while (depth)
+ {
+ unsigned int fname_size = static_cast<unsigned char>(v[i++]);
+ if (fname_size)
+ {
+ if (i + fname_size + 6 > v.size()) throw exception("Unexpected end of PAQ file");
+ index.push_back(index_entry(string(&v[i], &v[i + fname_size]), v[i + fname_size + 4]));
+ if (index.back().is_dir)
+ ++depth;
+ else
+ index.back().file_size = *reinterpret_cast<const unsigned int*>(&v[i + fname_size]);
+ i += fname_size + 5;
+ }
+ else
+ {
+ index.push_back(index_entry(string(""), 0));
+ --depth;
+ }
+ }
+
+ files.assign(v.begin() + i, v.end());
+ for(list<index_entry>::iterator i_it = index.begin(); i_it != index.end(); i_it++)
+ i += i_it->file_size;
+ if (i < v.size()) throw exception("Unexpected end of PAQ file");
+ }
+
+ void extract_r(string path, list<index_entry>::iterator& i_it, vector<char>::iterator& f_it)
+ {
+ boost::filesystem::create_directory(path);
+ for(i_it; i_it->file_name.size() != 0; i_it++)
+ if (i_it->is_dir)
+ extract_r(path + DIR_SEPARATOR + i_it++->file_name, i_it, f_it);
+ else
+ {
+ const string file_path = path + DIR_SEPARATOR + i_it->file_name;
+ cout << "Extracting " << file_path << endl;
+ ofstream out(file_path.c_str(), ios::binary);
+ if (!out) throw my_exception(string("Can't open ") + file_path);
+ if (i_it->file_size > 4)
+ {
+ boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
+ in.push(boost::iostreams::gzip_decompressor());
+ in.push(boost::iostreams::array_source(&(*f_it) + 4, i_it->file_size - 4));
+ boost::iostreams::copy(in, out);
+ out.close();
+ if (*reinterpret_cast<unsigned int*>(&(*f_it)) != boost::filesystem::file_size(file_path)) throw my_exception(string("Uncompressed file size of ") + file_path + " inconsistent with PAQ entry for this file");
+ }
+ f_it += i_it->file_size;
+ }
+ }
+
+ void print_r(string path, list<index_entry>::iterator& i_it)
+ {
+ for(i_it; i_it->file_name.size() != 0; i_it++)
+ if (i_it->is_dir)
+ print_r(path + DIR_SEPARATOR + i_it++->file_name, i_it);
+ else
+ cout << (path + DIR_SEPARATOR + i_it->file_name).c_str() + 1 << endl;
+ }
+
+public:
+ paq_extractor(const char* paq_path)
{
- index_entry(const string& file_name, bool is_dir) : file_name(file_name), is_dir(is_dir) { }
+ ifstream paq(paq_path, ios::binary);
+ if (!paq) throw my_exception(string("Can't open ") + paq_path);
+
+ unsigned int file_size = boost::filesystem::file_size(paq_path);
+ if (file_size < 5) throw my_exception(string(paq_path) + " doesn't seem to be a valid PAQ archive");
+ vector<char> v(file_size);
+ paq.read(&v[0], file_size);
+ paq.close();
+
+ if (*reinterpret_cast<unsigned int*>(&v[0]) != 0x5141504E) throw exception("No valid signature found in PAQ file");
+
+ process(v);
+ }
+
+ void extract_to(const char* path) { extract_r(string(path), index.begin(), files.begin()); }
- string file_name;
- bool is_dir;
- };
+ void print_index() { print_r(string(), index.begin()); }
+};
+class paq_builder
+{
+private:
list<index_entry> index;
list<vector<char> > files;
@@ -43,9 +142,10 @@ private:
{
index.push_back(index_entry(itr->filename(), boost::filesystem::is_directory(itr->status())));
if (index.back().is_dir)
- process(path + "/" + itr->filename());
+ process(path + DIR_SEPARATOR + itr->filename());
else
{
+ cout << "Adding " << path << DIR_SEPARATOR << itr->filename() << endl;
ifstream in(itr->string().c_str(), ios::binary);
if (!in) throw my_exception(string("Can't open ") + itr->string());
vector<char> v(4);
@@ -59,7 +159,7 @@ private:
}
index.push_back(index_entry(string(""), 0));
}
-
+
public:
paq_builder(const char* path)
{
@@ -94,21 +194,50 @@ public:
}
};
-int main(int argc, char** argv)
+void process_command_line(int argc, char** argv)
{
- if (argc < 3)
+ if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "/?"))
+ {
+ cout << "PAQ archiving utility, version 1.0 (" << __DATE__ << ")" << endl;
+ cout << "Copyright (C) 2009 Jes, http://www.bessab.com" << endl << endl;
+ cout << "Usage: " << argv[0] << " [action] [paq file] {in/out directory}" << endl << endl;
+ cout << " The following actions are supported:" << endl << endl;
+ cout << " -l list files in a PAQ archive" << endl;
+ cout << " -x extract PAQ archive to the specified directory" << endl;
+ cout << " -c create PAQ archive of an entire directory (including its sub-directories)" << endl;
+ return;
+ }
+
+ if (!strcmp(argv[1], "-c") && argc > 3)
+ {
+ paq_builder(argv[3]).write_to_file(argv[2]);
+ return;
+ }
+
+ if (!strcmp(argv[1], "-x") && argc > 3)
{
- cout << "Usage: " << argv[0] << " <in-directory> <out-paq>" << endl;
- return 0;
+ paq_extractor(argv[2]).extract_to(argv[3]);
+ return;
}
+ if (!strcmp(argv[1], "-l") && argc > 2)
+ {
+ paq_extractor(argv[2]).print_index();
+ return;
+ }
+
+ throw exception("Syntax error in the command line, use --help option for any help");
+}
+
+int main(int argc, char** argv)
+{
try
{
- paq_builder(argv[1]).write_to_file(argv[2]);
+ process_command_line(argc, argv);
}
- catch (const exception& e)
+ catch (exception& e)
{
- cerr << e.what() << endl;
+ cerr << e.what();
return -1;
}