#include "libbible.h" #include #include #include #include #include using namespace sword; using namespace std; SWMgr library(new MarkupFilterMgr(FMT_XHTML)); vector getBooks(SWModule *target) { vector 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> libbible::getModules() { library.load(); map> 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::getPassages(string modName, string book) { vector 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::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 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 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 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 "". if(span == "") { if(! spans.empty()) { spans.pop_back(); spansChanged = true; } } else { // The span will be formatted "" // 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; }