scriptura/scabbard.cpp

342 lines
10 KiB
C++

// 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 <https://www.gnu.org/licenses/>.
// scabbard.cpp - manage interface to Sword
// it's funny, right?
#include <iostream>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#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); */