aboutsummaryrefslogtreecommitdiff
path: root/libbible.cc
diff options
context:
space:
mode:
authorYour Name <you@example.com>2020-11-05 15:50:11 -0500
committerYour Name <you@example.com>2020-11-05 15:50:11 -0500
commit2fe897e2cf750339a7e466aeafe64f45fb650f10 (patch)
treed27bbda7c1ef5368a90704843deea014e6c810de /libbible.cc
downloadlibbible-2fe897e2cf750339a7e466aeafe64f45fb650f10.tar.gz
libbible-2fe897e2cf750339a7e466aeafe64f45fb650f10.tar.bz2
libbible-2fe897e2cf750339a7e466aeafe64f45fb650f10.zip
Initial commit
Diffstat (limited to 'libbible.cc')
-rw-r--r--libbible.cc218
1 files changed, 218 insertions, 0 deletions
diff --git a/libbible.cc b/libbible.cc
new file mode 100644
index 0000000..7b08174
--- /dev/null
+++ b/libbible.cc
@@ -0,0 +1,218 @@
+#include "libbible.h"
+#include <sword/versekey.h>
+#include <sword/markupfiltmgr.h>
+#include <sword/swmodule.h>
+#include <sword/swmgr.h>
+#include <algorithm>
+
+using namespace sword;
+using namespace std;
+
+SWMgr library(new MarkupFilterMgr(FMT_XHTML));
+
+vector<string> getBooks(SWModule *target) {
+ vector<string> books;
+ VerseKey *key = (VerseKey *) target->getKey();
+ for(char t = 1; t <= key->getTestamentMax(); t++) {
+ key->setTestament(t);
+ for(char b = 1; b <= key->getBookMax(); b++) {
+ key->setBook(b);
+ // Bug (whose fault??) in JPS; they CLAIM to have two testaments,
+ // but they only have one, which causes repeats.
+ if(std::find(books.begin(), books.end(), key->getBookName()) == books.end()) {
+ books.push_back(key->getBookName());
+ }
+ }
+ }
+ return books;
+}
+
+map<string, vector<string>> libbible::getModules() {
+ library.load();
+ map<string, vector<string>> mods;
+ ModMap::iterator it;
+ for (it = library.getModules().begin(); it != library.getModules().end(); it++) {
+ string modName = (*it).second->getName();
+ SWModule *target = library.getModule(modName.c_str());
+ mods[modName] = getBooks(target);
+ }
+ return mods;
+}
+
+vector<libbible::passage> libbible::getPassages(string modName, string book) {
+ vector<libbible::passage> passages;
+ SWModule *target = library.getModule(modName.c_str());
+ target->setKey((book + " " + "1").c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+ int maxChapter = key->getChapterMax();
+ for(int chapter = 1; chapter <= maxChapter; chapter++) {
+ string ref = book + ' ' + to_string(chapter);
+ target->setKey(ref.c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+ libbible::passage pass;
+ pass.modName = modName;
+ pass.reference = ref;
+ pass.book = string(key->getBookName());
+ pass.bookShort = string(key->getBookAbbrev());
+ pass.chapterStart = chapter;
+ pass.chapterEnd = chapter;
+ pass.verseStart = 1;
+ pass.verseEnd = key->getVerseMax();
+ passages.push_back(pass);
+ }
+ return passages;
+}
+
+libbible::text getEmptyText(VerseKey *key) {
+ libbible::text t;
+ t.chapter = key->getChapter();
+ t.verse = key->getVerse();
+ t.book = key->getBook();
+ t.bookShort = key->getBookAbbrev();
+ return t;
+}
+
+void inferMissing(libbible::passage *pass, SWModule *target) {
+ //printf("Hey, I'm inferring missing parts!\n");
+ if(! pass->reference.empty()) {
+ // Let's use the target to help us
+ target->setKey(pass->reference.c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+ pass->book = string(key->getBookName());
+ pass->bookShort = string(key->getBookAbbrev());
+ pass->chapterStart = key->getChapter();
+ pass->verseStart = key->getVerse();
+ //printf("Results so far: book: %s; chapterStart: %d; verseStart: %d\n", pass->book.c_str(), pass->chapterStart, pass->verseStart);
+ // And now we just need chapterEnd and verseEnd. Yippee.
+ string ref = string(pass->reference);
+ ref.erase(remove(ref.begin(), ref.end(), ' '), ref.end());
+ if(ref.find('-') == string::npos) {
+ // There's no range!
+ if(ref.find(':') == string::npos) {
+ // It's a full chapter reference
+ pass->chapterEnd = pass->chapterStart;
+ pass->verseEnd = key->getVerseMax();
+ } else {
+ // It's a single verse reference
+ pass->chapterEnd = pass->chapterStart;
+ pass->verseEnd = pass->verseStart;
+ //printf("Hey, it's a single verse reference!\n");
+ }
+ } else {
+ if(ref.find(':') == string::npos) {
+ // It's a multi-full-chapter reference
+ pass->chapterEnd = stoi(ref.substr(ref.find_last_of('-')+1));
+ pass->verseEnd = key->getVerseMax();
+ } else {
+ // It falls in categories c:v-v or c:v-c:v (or, technically, c-c:v)
+ string rangeEnd = ref.substr(ref.find_last_of('-')+1);
+ if(rangeEnd.find(':') == string::npos) {
+ // It's c:v-v
+ pass->verseEnd = stoi(rangeEnd);
+ pass->chapterEnd = pass->chapterStart;
+ } else {
+ // It's c:v-c:v (or c-c:v, but code is the same)
+ pass->chapterEnd = stoi(rangeEnd.substr(0, rangeEnd.find(':')));
+ pass->verseEnd = stoi(rangeEnd.substr(rangeEnd.find(':')+1));
+ }
+ }
+ }
+ }
+ if(pass->book.empty()) {
+ pass->book = pass->bookShort;
+ }
+}
+
+vector<libbible::text> libbible::getText(libbible::passage pass) {
+ SWModule *target = library.getModule(pass.modName.c_str());
+ inferMissing(&pass, target);
+ target->setKey((pass.book
+ + " " + to_string(pass.chapterStart)
+ + ":" + to_string(pass.verseStart)).c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+ vector<libbible::text> texts;
+
+ bool endOfParagraph = false;
+
+ for(; key->getChapter() < pass.chapterEnd ||
+ (key->getChapter() == pass.chapterEnd && key->getVerse() <= pass.verseEnd);
+ (*key)++) {
+ texts.push_back(getEmptyText(key));
+
+ string text = string(target->renderText());
+
+ // Handle ¶ symbol if at beginning of line
+ if(text.find("¶") == 0) {
+ text.replace(0, 1, "\n");
+ endOfParagraph = true;
+ }
+
+ while(text[0] == ' ' || text[0] == '\t') {
+ text.erase(0, 1);
+ }
+
+ // Find and replace everything in "subs" map
+ const map<string, string> subs = {{"¶", "\n\t"}};
+ for(auto const &repl : subs) {
+ string::size_type location;
+ while((location = text.find(repl.first)) != string::npos) {
+ text.replace(location, repl.first.size(), repl.second);
+ }
+ }
+
+ if(key->getVerse() == 1 || endOfParagraph) {
+ if(find(texts.back().modifiers.begin(), texts.back().modifiers.end(), "paragraph") == texts.back().modifiers.end()) {
+ texts.back().modifiers.push_back("paragraph");
+ }
+ endOfParagraph = false;
+ }
+
+ // Variable to accumulate unterminated spans
+ std::vector<string> spans;
+ bool spansChanged = false;
+ // Iterate over text
+ for(auto i = text.begin(); i != text.end(); i++) {
+ if(*i != '<') {
+ if(spansChanged) {
+ spansChanged = false;
+ if(!texts.back().text.empty()) {
+ texts.push_back(getEmptyText(key));
+ }
+ for(string modifier : spans) {
+ if(find(texts.back().modifiers.begin(), texts.back().modifiers.end(), modifier) == texts.back().modifiers.end()) {
+ texts.back().modifiers.push_back(modifier);
+ }
+ }
+ }
+ texts.back().text += *i;
+ }
+ else {
+ string span;
+ for(; i != text.end(); i++) {
+ span.push_back(*i);
+ if(*i == '>') {
+ // The end of the span will be "</span>".
+ if(span == "</span>") {
+ if(! spans.empty()) {
+ spans.pop_back();
+ spansChanged = true;
+ }
+ } else {
+ // The span will be formatted "<span class=\"NAME\">"
+ // We want just the NAME
+ size_t start = span.find_first_of('"')+1;
+ size_t end = span.find_last_of('"');
+ if(start > 0) {
+ spans.push_back(span.substr(start, end-start));
+ spansChanged = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ endOfParagraph = (text[text.length()-1] == '\n');
+ }
+ return texts;
+}