diff options
-rw-r--r-- | contrib/paq.cpp | 161 |
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; } |