#include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define DIR_SEPARATOR "\\" #else #define DIR_SEPARATOR "/" #endif using namespace std; class my_exception : public exception { private: string cause; public: my_exception(const string& cause) : cause(cause) { } ~my_exception() throw () { } virtual const char* what() const throw() { return cause.c_str(); } }; 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: list index; vector files; void process(const vector& v) { unsigned int i = 4, depth = 1; while (depth) { unsigned int fname_size = static_cast(v[i++]); if (fname_size) { if (i + fname_size + 6 > v.size()) throw my_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(&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::iterator i_it = index.begin(); i_it != index.end(); i_it++) i += i_it->file_size; if (i < v.size()) throw my_exception("Unexpected end of PAQ file"); } void extract_r(string path, list::iterator& i_it, vector::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 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(&(*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::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) { 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 v(file_size); paq.read(&v[0], file_size); paq.close(); if (*reinterpret_cast(&v[0]) != 0x5141504E) throw my_exception("No valid signature found in PAQ file"); process(v); } void extract_to(const char* path) { list::iterator ib = index.begin(); vector::iterator fb = files.begin(); extract_r(string(path), ib, fb); } void print_index() { list::iterator b = index.begin(); print_r(string(), b); } }; class paq_builder { private: list index; list > files; void process(string path) { boost::filesystem::directory_iterator end; for (boost::filesystem::directory_iterator itr(path.c_str()); itr != end ; ++itr) { index.push_back(index_entry(itr->filename(), boost::filesystem::is_directory(itr->status()))); if (index.back().is_dir) 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 v(4); *reinterpret_cast(&v[0]) = boost::filesystem::file_size(itr->path()); boost::iostreams::filtering_streambuf out; out.push(boost::iostreams::gzip_compressor()); out.push(boost::iostreams::back_inserter(v)); boost::iostreams::copy(in, out); files.push_back(v); } } index.push_back(index_entry(string(""), 0)); } public: paq_builder(const char* path) { if (!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path)) throw my_exception(string(path) + " doesn't exist or isn't a directory!"); process(string(path)); } void write_to_file(const char* path) { ofstream paq(path, ios::binary); if (!paq) throw my_exception(string("Can't open ") + path); paq.write("NPAQ", 4); list >::iterator f_it = files.begin(); for (list::iterator i_it = index.begin(); i_it != index.end(); i_it++) { paq << static_cast(i_it->file_name.size()); if (i_it->file_name.size()) { paq.write(i_it->file_name.c_str(), i_it->file_name.size()); unsigned int file_size = i_it->is_dir ? 0 : f_it++->size(); paq.write(reinterpret_cast(&file_size), 4); paq << static_cast(file_size ? 0 : 1); } } for(f_it = files.begin(); f_it != files.end(); f_it++) paq.write(&(*f_it)[0], f_it->size()); } }; void process_command_line(int argc, char** argv) { 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) { paq_extractor(argv[2]).extract_to(argv[3]); return; } if (!strcmp(argv[1], "-l") && argc > 2) { paq_extractor(argv[2]).print_index(); return; } throw my_exception("Syntax error in the command line, use --help option for any help"); } int main(int argc, char** argv) { try { process_command_line(argc, argv); } catch (exception& e) { cerr << e.what() << endl; return -1; } return 0; }