From 55d58a16e2511741cc625e203205dec86144faf3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 18 Feb 2022 20:35:38 -0500 Subject: Reorganized repository layout --- src/lib/mods.cc | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 src/lib/mods.cc (limited to 'src/lib/mods.cc') diff --git a/src/lib/mods.cc b/src/lib/mods.cc new file mode 100644 index 0000000..ab54e48 --- /dev/null +++ b/src/lib/mods.cc @@ -0,0 +1,233 @@ +#include "libbible.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class myStatusReporter : public sword::StatusReporter { + public: + myStatusReporter(libbible::Status *status); + ~myStatusReporter(); + void preStatus(long totalBytes, long completedBytes, const char *message); + void update(unsigned long totalBytes, unsigned long completedBytes); + protected: + libbible::Status *status; + string message; +}; + +myStatusReporter::myStatusReporter(libbible::Status *s) { + status = s; +} + +myStatusReporter::~myStatusReporter() {}; + +//virtual void libbible::Status::update(unsigned long totalBytes, unsigned long completedBytes, string message) {} + +void myStatusReporter::preStatus(long totalBytes, long completedBytes, const char *msg) { + message = string(msg); + status->update((unsigned long) totalBytes, (unsigned long) completedBytes, message); + //printf("Got a status update: %ld / %ld, \"%s\"\n", completedBytes, totalBytes, message.c_str()); +} + +void myStatusReporter::update(unsigned long totalBytes, unsigned long completedBytes) { + status->update(totalBytes, completedBytes, message); + //printf("Got a status update: %ld / %ld, \"%s\"\n", completedBytes, totalBytes, message.c_str()); +} + +string basedir = (getenv("HOME")) + string("/.sword/"); +sword::InstallMgr *installMgr = new sword::InstallMgr((basedir + std::string("InstallMgr")).c_str(), nullptr); +map>> installSources; +map languageNames; // maps abbreviation to full name + +void libbible::setStatusReporter(libbible::Status& status) { + myStatusReporter *msr = new myStatusReporter(&status); + free(installMgr); + installMgr = new sword::InstallMgr((basedir + std::string("InstallMgr")).c_str(), msr); + installMgr->setUserDisclaimerConfirmed(true); +} + +map> libbible::downloadModsAvailable() { + installSources.clear(); + languageNames.clear(); + mkdir((basedir + std::string("mods.d/")).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + mkdir((basedir + std::string("modules/")).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + installMgr->setUserDisclaimerConfirmed(true); + string confpath = basedir + string("InstallMgr/InstallMgr.conf"); + if(! sword::FileMgr::existsFile(confpath.c_str())) { + // Lifted directly from xiphos + sword::FileMgr::createParent(confpath.c_str()); + sword::SWConfig config(confpath.c_str()); + sword::InstallSource is("FTP"); + is.caption = "CrossWire"; + is.source = "ftp.crosswire.org"; + is.directory = "/pub/sword/raw"; + config["General"]["PassiveFTP"] = "true"; + config["Sources"]["FTPSource"] = is.getConfEnt(); + config.save(); + installMgr->refreshRemoteSourceConfiguration(); + } + installMgr->readInstallConf(); + map> modsAvailable; + map> languagesToFull; + //printf("Getting langs...\n"); + for(auto src : installMgr->sources) { + if(src.second->getMgr()->Modules.empty()) { + //printf("Refreshing remote source: %s\n", src.second->getConfEnt().c_str()); + installMgr->refreshRemoteSource(src.second); + } + for(auto mod : src.second->getMgr()->Modules) { + auto *curMod = mod.second; + string type(curMod->getType()); + if(type == "Biblical Texts") { + string language(curMod->getLanguage()); + string fullLang; + if(curMod->getConfigEntry("LCSH")) { + // Split on periods, last field, strip + fullLang = string(curMod->getConfigEntry("LCSH")); + // If ends with ., remove + if(fullLang.ends_with('.')) fullLang = fullLang.substr(0, fullLang.size()-1); + if(fullLang.find('.') != string::npos) fullLang = fullLang.substr(fullLang.find_last_of('.')+1); + while(fullLang.starts_with(' ')) fullLang = fullLang.substr(1); + while(fullLang.ends_with(' ')) fullLang = fullLang.substr(0, fullLang.size()-1); + } + vector newLangs; + languagesToFull.emplace(language, newLangs); + languagesToFull[language].push_back(fullLang); + vector newMods; + vector> newSources; + // emplace only adds if key is unique + modsAvailable.emplace(language, newMods); + installSources.emplace(language, newSources); + modsAvailable[language].push_back(string(curMod->getName())); + pair p(string(curMod->getName()), src.second); + installSources[language].push_back(p); + } + } + } + // Now use majority voting to move languagesToFull -> languageNames + for(const auto& [abbrev, fulls] : languagesToFull) { + std::map majVote; + for(auto full : fulls) { + majVote.try_emplace(full, 0); + majVote[full]++; + } + string selected = fulls[0]; + for(auto full : fulls) { + if(majVote[full] > majVote[selected] or (majVote[full] == majVote[selected] and !full.empty() and full.size() < selected.size())) { + selected = full; + } + } + if(selected.empty()) languageNames[abbrev] = abbrev; + else languageNames[abbrev] = selected; + } + return modsAvailable; +} + +std::map libbible::getLanguageNames() { + if(languageNames.empty()) { + downloadModsAvailable(); + } + return languageNames; +} + +void libbible::terminateDownload() { + installMgr->terminate(); +} + +bool libbible::installModFromInternet(string language, string name) { + // Searching through map>> installSources; + if(installSources.empty()) { + downloadModsAvailable(); + } + for (pair p : installSources[language]) { + if(p.first == name) { + sword::SWMgr mgr(basedir.c_str()); + if(installMgr->installModule(&mgr, 0, name.c_str(), p.second) == 0) { + printf("Installed from %s\n", p.second->getConfEnt().c_str()); + return true; + } + return false; + } + } + return false; +} + +#define READ_SIZE 8192 +#define delim '/' + +bool libbible::installModFromZip(string filename) { + // So... turns out it's a mite unsupported to install from a .zip + // Here's the deal. We do a syscall to unzip. We fancy like that. + // TODO: Use the ZipCompress module from SWORD instead. + /*string command = "unzip -o " + filename + " -d " + basedir + "&> /dev/null"; + if(system(command.c_str())) { + //Uh oh... + printf("Something bad happened when unpacking %s\n. Is unzip installed?", filename.c_str()); + }*/ + unzFile zipfile = unzOpen(filename.c_str()); + if(zipfile == NULL) { + return false; + } + unz_global_info global_info; + if(unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK) { + unzClose(zipfile); + return false; + } + char read_buffer[READ_SIZE]; + ulong i; + for(i = 0; i < global_info.number_entry; i++) { + unz_file_info file_info; + if(unzGetCurrentFileInfo(zipfile, &file_info, read_buffer, READ_SIZE, NULL, 0, NULL, 0) != UNZ_OK) { + unzClose(zipfile); + return false; + } + string fname = basedir + string(read_buffer); + size_t pos = fname.find_last_of(delim); + if(pos != string::npos) { + string path = fname.substr(0, pos); + filesystem::create_directories(path); + } + if(unzOpenCurrentFile(zipfile) != UNZ_OK) { + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + return false; + } + FILE *out = fopen(fname.c_str(), "wb"); + if(out == NULL) { + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + return false; + } + int bytesRead; + do { + bytesRead = unzReadCurrentFile(zipfile, read_buffer, READ_SIZE); + if(bytesRead < 0) { + printf("error %d\n", bytesRead); + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + return false; + } + if(bytesRead > 0) { + fwrite(read_buffer, bytesRead, 1, out); + } + } while(bytesRead > 0); + fclose(out); + unzCloseCurrentFile(zipfile); + unzGoToNextFile(zipfile); + } + unzClose(zipfile); + return true; +} + +void libbible::uninstallMod(string modname) { + sword::SWMgr mgr(basedir.c_str()); + sword::ModMap::iterator it = mgr.Modules.find(modname.c_str()); + if(it != mgr.Modules.end()) { + installMgr->removeModule(&mgr, it->second->getName()); + } +} -- cgit v1.2.3