diff options
Diffstat (limited to 'src/lib/libbible.cc')
-rw-r--r-- | src/lib/libbible.cc | 262 |
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; +} |