342 lines
10 KiB
C++
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); */
|
|
|