// Copyright 2021, Paul Mosier // // This file is part of Scriptura, a ncurses-based Bible study // software for the libsword backend. // // Scriptura 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, under version 2 of the License. // // Scriptura 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 Scriptura. If not, see . // scabbard.cpp - manage interface to Sword // it's funny, right? #include #include #include #include #include "scabbard.h" scabbard::scabbard() { // initialize the thing! swrd = *(new sword::SWMgr()); /* store what markup information the user has requested be shown * SWBuf.c_str() returns the ascii code 48 for '0', so we have to do some * math to get the proper boolean setting */ markred = parseConf(config["markup"]["redletters"]); markstrongs = parseConf(config["markup"]["strongs"]); markfoot = parseConf(config["markup"]["footnotes"]); // construct the menu list of modules constructModlist(); } void scabbard::constructModlist() { int tmpnum = 5; modtype tmpmods[5]; strcpy(tmpmods[0].label, sword::SWMgr::MODTYPE_GENBOOKS); tmpmods[0].keytype = 1; strcpy(tmpmods[1].label, sword::SWMgr::MODTYPE_BIBLES); tmpmods[1].keytype = 0; strcpy(tmpmods[2].label, sword::SWMgr::MODTYPE_LEXDICTS); tmpmods[2].keytype = 2; strcpy(tmpmods[3].label, sword::SWMgr::MODTYPE_COMMENTARIES); tmpmods[3].keytype = 0; strcpy(tmpmods[4].label, sword::SWMgr::MODTYPE_DAILYDEVOS); tmpmods[3].keytype = 0; // need some throwaway ints int index[tmpnum]; for (int i = 0; i < tmpnum; i++) index[i] = 0; // initialize other parts of modtype struct for (int i = 0; i < tmpnum; i++) { tmpmods[i].modlen = 1; tmpmods[i].modlist = (sword::SWModule**) malloc(sizeof(sword::SWModule)); if (! tmpmods[i].modlist) wrapup(1, "Error allocating memory for scabbard\n"); } // get list of installed tmpmods and store for (iter = swrd.Modules.begin(); iter != swrd.Modules.end(); iter++) { sword::SWBuf modName = iter->first; sword::SWModule *mod = iter->second; for (int i = 0; i < tmpnum; i++) { // determine which classification of module this is if (! strcmp(mod->getType(), tmpmods[i].label)) { // increase array size if needed if (tmpmods[i].modlen == index[i]) { void* extarray = realloc(tmpmods[i].modlist, sizeof(sword::SWModule) * (tmpmods[i].modlen+1)); if (extarray != NULL) { tmpmods[i].modlist = (sword::SWModule**) extarray; } else { wrapup(1, "Error allocating memory for scabbard\n"); } tmpmods[i].modlen++; } // catalog the module (tmpmods[i].modlist)[index[i]] = mod; index[i]++; } } } /* if a module type has no modules in it, we don't want to look at it again * - do this by copying our temp array to the final array if elements * exist */ nummods = 0; for (int i = 0; i < tmpnum; i++) { if (index[i] > 0) { modules[nummods] = tmpmods[i]; nummods++; } } } const char* scabbard::parseVerses(sword::SWModule *text, sword::ListKey lk) { // set output filters for text based on what we want to have if (markred) { text->addRenderFilter(new sword::OSISRedLetterWords()); text->addRenderFilter(new sword::GBFRedLetterWords()); } if (markstrongs) text->addRenderFilter(new sword::OSISStrongs()); if (markfoot) { text->addRenderFilter(new sword::OSISFootnotes()); text->addRenderFilter(new sword::GBFFootnotes()); } // retrieve sword::SWBuf excerpt = sword::SWBuf("", 0); for (lk = sword::SW_POSITION(POS_TOP); !lk.popError(); lk++) { text->setKey(lk); // add verse heading excerpt.append(lk.getShortText()); sword::VerseKey vk = (sword::VerseKey) lk.getElement(); if (vk.getVerse() < 10) excerpt.append(" "); excerpt.append(" -- "); /* if no filters selected, use stripText, otherwise we have as the * default content whatever filters are written into the module */ if (markred + markstrongs + markfoot == 0) { excerpt.append(text->stripText()); } else { excerpt.append(text->renderText()); } excerpt.append("\n"); } excerpt.append("\0"); // excerpt will free itself on exit, make a copy to send back const char* retval = strdup(excerpt.c_str()); return retval; } const char* scabbard::directSearch(sword::SWModule *text, modkey mod) { text->setKey(mod.searchkey); text->renderText(); // give the best fit to the searchkey sword::SWBuf buf = sword::SWBuf("", 0); buf.append(text->getKeyText()); buf.append("\n"); buf.append(text->getRawEntry()); buf.append("\0"); // buf will free itself on exit, make a copy const char* retval = strdup(buf.c_str()); return retval; } starray scabbard::getModClassifications() { starray retval; retval = stinit(retval); for (int i = 0; i < nummods; i++) retval = stappend(retval, modules[i].label); return retval; } starray scabbard::getModDescriptions(int type) { starray retval; retval = stinit(retval); for (int i = 0; i < modules[type].modlen; i++) retval = stappend(retval, (modules[type].modlist)[i]->getDescription()); return retval; } const char* scabbard::getModName(int modt, int mod) { return (modules[modt].modlist)[mod]->getName(); } const char* scabbard::getModDescription(int modt, int mod) { return (modules[modt].modlist)[mod]->getDescription(); } const char* scabbard::getModDescription(const char* modname) { sword::SWModule *text = swrd.getModule(modname); return text->getDescription(); } int scabbard::modExists(const char* modname) { sword::SWModule *text = swrd.getModule(modname); return (text == 0 ? 0 : 1); } starray scabbard::getModules() { starray retval; retval = stinit(retval); for (iter = swrd.Modules.begin(); iter != swrd.Modules.end(); iter++) { sword::SWModule *mod = iter->second; retval = stappend(retval, mod->getName()); } return retval; } int scabbard::getKeyType(int modt) { return modules[modt].keytype; } const char* scabbard::getSpan(modkey mod) { // get module and material specified sword::SWModule *text = swrd.getModule(mod.modname); // with the absence of certain config settings, getModule may return 0 if (text == 0) return ""; if (mod.keytype == 0) { sword::VerseKey *key = (sword::VerseKey*) text->createKey(); sword::ListKey lk = key->parseVerseList(mod.searchkey, mod.searchkey, true); delete(key); return parseVerses(text, lk); } else return directSearch(text, mod); } const char* scabbard::search(modkey mod) { sword::SWModule *text = swrd.getModule(mod.modname); // with the absence of certain config settings, getModule may return 0 if (text == 0) return ""; if (mod.keytype == 0) { sword::VerseKey *key = (sword::VerseKey*) text->createKey(); sword::SWKey *clone = (key->parseVerseList(mod.scope, mod.scope, true)).clone(); sword::ListKey lk = text->search(mod.searchkey, mod.searchtype, 0, clone); const char* retval = parseVerses(text, lk); delete(key); delete(clone); return retval; } return directSearch(text, mod); } void scabbard::installKJVA() { sword::SWBuf swordhome = getenv("HOME"); swordhome += "/.sword"; // ensure some directories exist -- apparently installmgr doesn't create them if ((mkdir(swordhome + "/mods.d", 00755) != 0) && (errno != EEXIST)) { printf("Error creating directory to perform the install:\n"); printf("~/.sword/mods.d\n"); printf("Please create manually or check directory permissions.\n"); exit(0); } sword::SWBuf finaldir = swordhome.c_str(); finaldir += "/modules"; mkdir(finaldir.c_str(), 00755); finaldir += "/texts"; mkdir(finaldir.c_str(), 00755); finaldir += "/ztext"; mkdir(finaldir.c_str(), 00755); finaldir += "/kjva"; if ((mkdir(finaldir.c_str(), 00755) != 0) && (errno != EEXIST)) { printf("Error creating directory to perform the install:\n"); printf("~/.sword/modules/texts/ztext/kjva\n"); printf("Please create manually or check directory permissions.\n"); exit(0); } printf("Directories created for new module.\n"); // installmgr -init sword::InstallMgr* im = new sword::InstallMgr(swordhome + "/InstallMgr"); im->setUserDisclaimerConfirmed(true); // sync config (installmgr -sc) sword::InstallSource is("FTP"); is.caption = "CrossWire"; is.source = "ftp.crosswire.org"; is.directory = "/pub/sword/raw"; sword::SWConfig installconf(swordhome + "/InstallMgr/InstallMgr.conf"); if (! installconf["General"]["PassiveFTP"]) installconf["General"]["PassiveFTP"] = "true"; if (! installconf["General"]["TimeoutMillis"]) installconf["General"]["TimeoutMillis"] = "10000"; if (! installconf["General"]["UnverifiedPeerAllowed"]) installconf["General"]["UnverifiedPeerAllowed"] = false; if (! installconf["General"]["FTPSource"]) installconf["Sources"]["FTPSource"] = is.getConfEnt(); installconf.save(); im->refreshRemoteSourceConfiguration(); /* refresh remote source (installmgr -r) * apparently we can't reuse our prior declared InstallSource here */ sword::InstallSourceMap::iterator source = im->sources.find("CrossWire"); sword::InstallSource *is2 = source->second; if (source == im->sources.end()) { fprintf(stderr, "Couldn't find remote source [%s]\n", "CrossWire"); } if (int refresh = im->refreshRemoteSource(is2)) { printf("Refresh call failed, error: %d\n", refresh); printf("Most likely this is an InstalMgr configuration issue.\n"); printf("Please ensure your SWORD environment is properly configured,\n"); printf(" or download & install the module manually before trying again.\n"); exit(0); } // install module (installmgr -ri CrossWire KJVA) sword::SWMgr* rmgr = is2->getMgr(); sword::SWModule* modname = rmgr->getModule("KJVA"); if (im->installModule(new sword::SWMgr(), 0, modname->getName(), is2)) { printf("Error installing module.\n"); printf("Most likely this is an environment configuration issue.\n"); printf("Please ensure your SWORD environment is properly configured,\n"); printf(" or download & install the module manually before trying again.\n"); exit(0); } // update internal module list & refresh SWMgr w/installed module info swrd = *(new sword::SWMgr()); constructModlist(); } /* really old code in case we need to pull output from diatheke FILE *modlist = popen("diatheke -b system -k modulelist", "r"); char buf[1024]; fread(buf, sizeof(char), 1024, modlist); pclose(modlist); */