/* par.* - parity file handling Copyright (C) 2003 Matthew Mueller This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef NGET__PAR_H__ #define NGET__PAR_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "myregex.h" #include "auto_map.h" #include "cache.h" #include "par2/par2cmdline.h" typedef multimap t_nocase_map; bool par2file_get_sethash(const string &filename, string &sethash); bool parfile_get_sethash(const string &filename, string &sethash); bool parfile_ok(const string &filename, uint32_t &vol_number); int parfile_check(const string &filename, const string &path, const t_nocase_map &nocase_map); // Sort incomplete files to the end of the list, so we try them last. typedef pair t_server_file_key; inline t_server_file_key server_file_key(const c_nntp_file::ptr &f) { float completeness = (f->req >= 1) ? f->have/(float)f->req : 1.0; return t_server_file_key(-completeness, f->badate()); } typedef multimap t_server_file_list; #define server_file_list_value(f) t_server_file_list::value_type(server_file_key(f), f) class ParSetInfo { protected: t_server_file_list serverpars; t_server_file_list serverpxxs; public: void addserverpar(const c_nntp_file::ptr &f) { serverpars.insert(server_file_list_value(f)); } void addserverpxx(const c_nntp_file::ptr &f) { serverpxxs.insert(server_file_list_value(f)); } int get_initial_pars(c_nntp_files_u &fc, const string &path, const string &temppath) { int count=0; for (t_server_file_list::const_iterator spi=serverpars.begin(); spi!=serverpars.end(); ++spi){ fc.addfile(spi->second, path, temppath, false); count++; } if (serverpars.empty() && !serverpxxs.empty()){ t_server_file_list::iterator smallest = serverpxxs.begin(), cur = smallest; for (++cur; cur!=serverpxxs.end(); ++cur) if (cur->second->bytes() < smallest->second->bytes()) smallest = cur; fc.addfile(smallest->second, path, temppath, false); count++; serverpxxs.erase(smallest); } serverpars.clear(); return count; } void release_unclaimed_pxxs(vector &unclaimedfiles){ for (t_server_file_list::const_iterator spi=serverpxxs.begin(); spi!=serverpxxs.end(); ++spi){ unclaimedfiles.push_back(spi->second); } serverpxxs.clear(); } }; typedef auto_multimap t_subjmatches_map; typedef map > t_basenames_map; typedef map > t_basefilenames_map; class LocalParFiles { public: t_subjmatches_map subjmatches; // maps sethash -> subject matches for known basenames of that set t_basenames_map basenames; // maps sethash -> known basenames for that set t_basefilenames_map basefilenames; // maps sethash -> all known filenames for that set set badbasenames; // set of all basenames for which corrupt/non-par files were found void addsubjmatch_par1(const string &key, const string &basename){ if (basenames[key].find(basename)!=basenames[key].end()) return;//only insert one subject match for each basename basenames[key].insert(basename); string matchstr; if (isalnum(basename[0])) matchstr+=regex_match_word_beginning_safe(); regex_escape_string(basename, matchstr); matchstr+="\\.p(ar|[0-9]{2})"; matchstr+=regex_match_word_end(); subjmatches.insert_value(key, new c_regex_r(matchstr.c_str(), REG_EXTENDED|REG_ICASE)); } void addsubjmatch_par2(const string &key, const string &basename){ if (basenames[key].find(basename)!=basenames[key].end()) return;//only insert one subject match for each basename basenames[key].insert(basename); string matchstr; if (isalnum(basename[0])) matchstr+=regex_match_word_beginning_safe(); regex_escape_string(basename, matchstr); matchstr+="(\\.vol([0-9]+)\\+([0-9]+))?\\.par2"; matchstr+=regex_match_word_end(); subjmatches.insert_value(key, new c_regex_r(matchstr.c_str(), REG_EXTENDED|REG_ICASE)); } void addfrompath_par1(const string &path, t_nocase_map *nocase_map=NULL); void addfrompath_par2(const string &path, t_nocase_map *nocase_map=NULL); void check_badbasenames(void); void clear(void){ basefilenames.clear(); basenames.clear(); subjmatches.clear(); badbasenames.clear(); } }; class ParXInfoBase { protected: t_server_file_list serverpars; t_server_file_list serverpxxs; typedef map t_parset_map; set finished_parsets; // which sethashes we have finished (either tested ok, or exhausted all hope) int finished_okcount; // how many of the finished_parsets were ok t_parset_map parsets; LocalParFiles localpars; const string path; const string temppath; ParSetInfo *parset(const string &basename) { return &parsets[basename]; // will insert a new ParSetInfo into parsets if needed. } public: ParXInfoBase(const string &p,const string &t):finished_okcount(0),path(p),temppath(t){//well, saving and using only the first temppath encountered isn't exactly perfect, but I doubt many people really download multiple things to the same path but with different temp paths. And I'm lazy. } int get_initial_pars(c_nntp_files_u &fc); int getNumFinished(void) const { return finished_parsets.size(); } int getNumFinishedOk(void) const { return finished_okcount; } }; class Par1Info : public ParXInfoBase { protected: int get_pxxs(int num, set &havevols, const string &key, c_nntp_files_u &fc); public: Par1Info(const string &p,const string &t):ParXInfoBase(p,t){ localpars.addfrompath_par1(path); } bool maybe_add_parfile(const c_nntp_file::ptr &f, bool want_incomplete); int maybe_get_pxxs(c_nntp_files_u &fc); }; class Par2Info : public ParXInfoBase { protected: int get_extradata(int num, c_nntp_files_u &fc, const Par2Repairer *par2); int get_recoverypackets(int num, set &havepackets, const string &key, c_nntp_files_u &fc, const Par2Repairer *par2); t_server_file_list serverextradata; public: Par2Info(const string &p,const string &t):ParXInfoBase(p,t){ localpars.addfrompath_par2(path); } bool maybe_add_parfile(const c_nntp_file::ptr &f, bool want_incomplete); int maybe_get_pxxs(c_nntp_files_u &fc); }; class ParInfo { protected: const string path; Par1Info par1info; Par2Info par2info; public: ParInfo(const string &p,const string &t):path(p), par1info(p,t), par2info(p,t) { } bool maybe_add_parfile(const c_nntp_file::ptr &f, bool want_incomplete) { return par1info.maybe_add_parfile(f, want_incomplete) || par2info.maybe_add_parfile(f, want_incomplete); } void get_initial_pars(c_nntp_files_u &fc) { par1info.get_initial_pars(fc); par2info.get_initial_pars(fc); } void maybe_get_pxxs(c_nntp_files_u &fc) { int total_added=0; total_added += par1info.maybe_get_pxxs(fc); total_added += par2info.maybe_get_pxxs(fc); if (!total_added) { PMSG("autopar in %s done (%i/%i parsets ok)", path.c_str(), par1info.getNumFinishedOk()+par2info.getNumFinishedOk(), par1info.getNumFinished()+par2info.getNumFinished()); } } }; class ParHandler { protected: typedef auto_map t_parinfo_map; t_parinfo_map parinfos; t_parinfo_map::mapped_type parinfo(const string &path, const string &temppath); t_parinfo_map::mapped_type parinfo(const string &path); public: bool maybe_add_parfile(const c_nntp_file::ptr &f, const string &path, const string &temppath, bool want_incomplete); void get_initial_pars(c_nntp_files_u &fc); void maybe_get_pxxs(const string &path, c_nntp_files_u &fc); }; #endif