aboutsummaryrefslogtreecommitdiff
path: root/src/lib/libbible.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libbible.cc')
-rw-r--r--src/lib/libbible.cc262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/lib/libbible.cc b/src/lib/libbible.cc
new file mode 100644
index 0000000..c9acb7d
--- /dev/null
+++ b/src/lib/libbible.cc
@@ -0,0 +1,262 @@
+#include "libbible.h"
+#include <sword/versekey.h>
+#include <sword/markupfiltmgr.h>
+#include <sword/swmodule.h>
+#include <sword/swmgr.h>
+#include <sword/osisfootnotes.h>
+#include <algorithm>
+
+using namespace sword;
+using namespace std;
+
+SWMgr library(new MarkupFilterMgr(FMT_XHTML));
+OSISFootnotes filter;
+
+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()) {
+ continue;
+ }
+ // Another issue (maybe bug?) Some translations are NT only,
+ // but still report OT books/chapters.
+ if(string(target->renderText()).empty()) {
+ continue;
+ }
+ 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());
+ if(target == nullptr) {
+ // Module doesn't exist
+ return passages;
+ }
+ 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.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->getBookName();
+ t.bookShort = key->getBookAbbrev();
+ return t;
+}
+
+libbible::passage libbible::getPassage(string modName, string reference) {
+ libbible::passage pass;
+ pass.modName = modName;
+ SWModule *target = library.getModule(pass.modName.c_str());
+ if(target == nullptr || reference.empty()) {
+ // Bad input
+ return pass;
+ }
+ vector<string> validBooks = getBooks(target);
+ //printf("Hey, I'm inferring missing parts!\n");
+ // Let's use the target to help us
+ target->setKey(reference.c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+ pass.book = string(key->getBookName());
+ // Hold on a moment, is this book even legal?
+ if(find(validBooks.begin(), validBooks.end(), pass.book) == validBooks.end()) {
+ key->setBookName(validBooks[0].c_str());
+ 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(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));
+ key->setChapter(pass.chapterEnd);
+ 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));
+ }
+ }
+ }
+ return pass;
+}
+
+vector<libbible::text> libbible::getText(libbible::passage pass) {
+ vector<libbible::text> texts;
+ SWModule *target = library.getModule(pass.modName.c_str());
+ filter.setOptionValue("Off");
+ target->addOptionFilter(&filter);
+ if(target == nullptr) {
+ // Module doesn't exist
+ return texts;
+ }
+ if(pass.book.empty()) {
+ pass.book = pass.bookShort;
+ }
+ target->setKey((pass.book
+ + " " + to_string(pass.chapterStart)
+ + ":" + to_string(pass.verseStart)).c_str());
+ VerseKey *key = (VerseKey *) target->getKey();
+
+ bool endOfParagraph = false;
+
+ string book = string(key->getBookName());
+
+ for(; string(key->getBookName()) == book &&
+ (key->getChapter() < pass.chapterEnd
+ || (key->getChapter() == pass.chapterEnd && key->getVerse() <= pass.verseEnd));
+ (*key)++) {
+
+ string text = string(target->renderText());
+ //printf("Working with: %s\n", text.c_str());
+
+ texts.push_back(getEmptyText(key));
+
+ 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<std::pair<std::string, std::string>> spans;
+ bool spansChanged = false;
+ bool hasAddedText = 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(auto& [tag, modifier] : spans) {
+ if(find(texts.back().modifiers.begin(), texts.back().modifiers.end(), modifier) == texts.back().modifiers.end()) {
+ texts.back().modifiers.push_back(modifier);
+ }
+ }
+ }
+ if(*i == '\n') {
+ continue; // We add newlines with <br />
+ }
+ if(! hasAddedText && (*i == ' ' || *i == '\t')) {
+ continue;
+ }
+ if(*i == "¶"[0] && i+1 != text.end() && *(i+1) == "¶"[1]) {
+ i++;
+ if(hasAddedText) {
+ texts.back().text += '\n';
+ } else {
+ // Append \n to text in previous texts (if applicable)
+ if(texts.size() > 1) {
+ texts[texts.size()-2].text += '\n';
+ }
+ texts.back().modifiers.push_back("paragraph");
+ continue;
+ }
+ }
+ texts.back().text += *i;
+ hasAddedText = true;
+ }
+ else {
+ string span;
+ for(; i != text.end(); i++) {
+ span.push_back(*i);
+ if(*i == '>') {
+ // The end of the span will be "</tag>".
+ if(span[1] == '/') {
+ string tag = span.substr(2, span.size()-3);
+ for(auto rit = spans.rbegin(); rit != spans.rend(); rit++) {
+ if(rit->first == tag) {
+ spans.erase(rit.base()-1);
+ spansChanged = true;
+ break;
+ }
+ }
+ } else if(span.find("class=\"") != string::npos) {
+ // The span will be formatted "<tag class=\"NAME\">"
+ // We want just the NAME
+ string tag = span.substr(1, span.find(" ")-1);
+ size_t start = span.find("class=\"")+7;
+ size_t end = span.find("\"", start);
+ spans.push_back(std::pair<string, string>(tag, span.substr(start, end-start)));
+ spansChanged = true;
+ } else if(span.find("preverse") != string::npos) {
+ string tag = span.substr(1, span.find(" ")-1);
+ spans.push_back(std::pair<string, string>(tag, "preverse"));
+ } else if(span == "<br />" || span == "<br/>") {
+ texts.back().text += '\n';
+ }
+ break;
+ }
+ }
+ }
+ }
+ endOfParagraph = (text[text.length()-1] == '\n');
+ }
+ return texts;
+}