/****************************************************************************** * VerseKey.cpp - code for class 'VerseKey'- a standard Biblical verse key */ #include #include #include #include #include #include #ifndef __GNUC__ #include #else #include #endif #include #include #include #include #include #include SWORD_NAMESPACE_START static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0}; SWClass VerseKey::classdef(classes); /****************************************************************************** * Initialize static members of VerseKey */ #include // Initialize static members of canonical books structure struct sbook *VerseKey::builtin_books[2] = {0,0}; const char VerseKey::builtin_BMAX[2] = {39, 27}; long *VerseKey::offsets[2][2] = {{VerseKey::otbks, VerseKey::otcps}, {VerseKey::ntbks, VerseKey::ntcps}}; int VerseKey::instance = 0; VerseKey::LocaleCache VerseKey::localeCache; /****************************************************************************** * VerseKey::init - initializes instance of VerseKey */ void VerseKey::init() { myclass = &classdef; if (!instance) initstatics(); instance++; autonorm = 1; // default auto normalization to true headings = 0; // default display headings option is false upperBound = 0; lowerBound = 0; boundSet = false; testament = 0; book = 0; chapter = 0; verse = 0; locale = 0; setLocale(LocaleMgr::systemLocaleMgr.getDefaultLocaleName()); } /****************************************************************************** * VerseKey Constructor - initializes instance of VerseKey * * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'. See * VerseKey::parse for more detailed information) */ VerseKey::VerseKey(const SWKey *ikey) : SWKey(*ikey) { init(); if (ikey) parse(); } /****************************************************************************** * VerseKey Constructor - initializes instance of VerseKey * * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'. See * VerseKey::parse for more detailed information) */ VerseKey::VerseKey(const char *ikey) : SWKey(ikey) { init(); if (ikey) parse(); } VerseKey::VerseKey(VerseKey const &k) : SWKey(k) { init(); autonorm = k.autonorm; headings = k.headings; testament = k.Testament(); book = k.Book(); chapter = k.Chapter(); verse = k.Verse(); if (k.isBoundSet()) { LowerBound(k.LowerBound()); UpperBound(k.UpperBound()); } } VerseKey::VerseKey(const char *min, const char *max) : SWKey() { init(); LowerBound(min); UpperBound(max); setPosition(TOP); } SWKey *VerseKey::clone() const { return new VerseKey(*this); } /****************************************************************************** * VerseKey Destructor - cleans up instance of VerseKey * * ENT: ikey - text key */ VerseKey::~VerseKey() { if (upperBound) delete upperBound; if (lowerBound) delete lowerBound; if (locale) delete [] locale; --instance; } void VerseKey::setLocale(const char *name) { char *BMAX; struct sbook **books; bool useCache = false; if (localeCache.name) useCache = (!strcmp(localeCache.name, name)); if (!useCache) { // if we're setting params for a new locale stdstr(&(localeCache.name), name); localeCache.abbrevsCnt = 0; } SWLocale *locale = (useCache) ? localeCache.locale : LocaleMgr::systemLocaleMgr.getLocale(name); localeCache.locale = locale; if (locale) { locale->getBooks(&BMAX, &books); setBooks(BMAX, books); setBookAbbrevs(locale->getBookAbbrevs(), localeCache.abbrevsCnt); localeCache.abbrevsCnt = abbrevsCnt; } else { setBooks(builtin_BMAX, builtin_books); setBookAbbrevs(builtin_abbrevs, localeCache.abbrevsCnt); localeCache.abbrevsCnt = abbrevsCnt; } stdstr(&(this->locale), localeCache.name); if (lowerBound) LowerBound().setLocale(name); if (upperBound) UpperBound().setLocale(name); } void VerseKey::setBooks(const char *iBMAX, struct sbook **ibooks) { BMAX = iBMAX; books = ibooks; } void VerseKey::setBookAbbrevs(const struct abbrev *bookAbbrevs, unsigned int size) { abbrevs = bookAbbrevs; if (!size) { for (abbrevsCnt = 0; *abbrevs[abbrevsCnt].ab; abbrevsCnt++) { /* if (strcmp(abbrevs[abbrevsCnt-1].ab, abbrevs[abbrevsCnt].ab) > 0) { fprintf(stderr, "ERROR: book abbreviation (canon.h or locale) misordered at entry: %s\n", abbrevs[abbrevsCnt].ab); exit(-1); } */ } for (int t = 0; t < 2; t++) { for (int i = 0; i < BMAX[t]; i++) { int bn = getBookAbbrev(books[t][i].name); if ((bn-1)%39 != i) { SWLog::systemlog->LogError("Book: %s does not have a matching toupper abbrevs entry! book number returned was: %d", books[t][i].name, bn); } } } } else abbrevsCnt = size; } /****************************************************************************** * VerseKey::initstatics - initializes statics. Performed only when first * instance on VerseKey (or descendent) is created. */ void VerseKey::initstatics() { int l1, l2, chaptmp = 0; builtin_books[0] = otbooks; builtin_books[1] = ntbooks; for (l1 = 0; l1 < 2; l1++) { for (l2 = 0; l2 < builtin_BMAX[l1]; l2++) { builtin_books[l1][l2].versemax = &vm[chaptmp]; chaptmp += builtin_books[l1][l2].chapmax; } } } /****************************************************************************** * VerseKey::parse - parses keytext into testament|book|chapter|verse * * RET: error status */ char VerseKey::parse() { testament = 2; book = BMAX[1]; chapter = 1; verse = 1; int booklen = 0; int error = 0; if (keytext) { ListKey tmpListKey = VerseKey::ParseVerseList(keytext); if (tmpListKey.Count()) { SWKey::setText((const char *)tmpListKey); for (int i = 1; i < 3; i++) { for (int j = 1; j <= BMAX[i-1]; j++) { int matchlen = strlen(books[i-1][j-1].name); if (!strncmp(keytext, books[i-1][j-1].name, matchlen)) { if (matchlen > booklen) { booklen = matchlen; testament = i; book = j; } } } } if (booklen) { sscanf(&keytext[booklen], "%d:%d", &chapter, &verse); } else error = 1; } else error = 1; } Normalize(1); freshtext(); return (this->error) ? this->error : (this->error = error); } /****************************************************************************** * VerseKey::freshtext - refreshes keytext based on * testament|book|chapter|verse */ void VerseKey::freshtext() const { char buf[2024]; int realtest = testament; int realbook = book; if (book < 1) { if (testament < 1) sprintf(buf, "[ Module Heading ]"); else sprintf(buf, "[ Testament %d Heading ]", (int)testament); } else { if (realbook > BMAX[realtest-1]) { realbook -= BMAX[realtest-1]; if (realtest < 2) realtest++; if (realbook > BMAX[realtest-1]) realbook = BMAX[realtest-1]; } sprintf(buf, "%s %d:%d", books[realtest-1][realbook-1].name, chapter, verse); } stdstr((char **)&keytext, buf); } /****************************************************************************** * VerseKey::getBookAbbrev - Attempts to find a book abbreviation for a buffer * * ENT: abbr - key for which to search; * RET: book number or < 0 = not valid */ int VerseKey::getBookAbbrev(const char *iabbr) { int loop, diff, abLen, min, max, target, retVal = -1; char *abbr = 0; for (int i = 0; i < 2; i++) { stdstr(&abbr, iabbr); strstrip(abbr); if (!i) toupperstr(abbr); abLen = strlen(abbr); if (abLen) { min = 0; // max = abbrevsCnt - 1; max = abbrevsCnt; while(1) { target = min + ((max - min) / 2); diff = strncmp(abbr, abbrevs[target].ab, abLen); if ((!diff)||(target >= max)||(target <= min)) break; if (diff > 0) min = target; else max = target; } for (; target > 0; target--) { if (strncmp(abbr, abbrevs[target-1].ab, abLen)) break; } retVal = (!diff) ? abbrevs[target].book : -1; } if (retVal > 0) break; } delete [] abbr; return retVal; } /****************************************************************************** * VerseKey::ParseVerseList - Attempts to parse a buffer into separate * verse entries returned in a ListKey * * ENT: buf - buffer to parse; * defaultKey - if verse, chap, book, or testament is left off, * pull info from this key (ie. Gen 2:3; 4:5; * Gen would be used when parsing the 4:5 section) * expandRange - whether or not to expand eg. John 1:10-12 or just * save John 1:10 * * RET: ListKey reference filled with verse entries contained in buf * * COMMENT: This code works but wreaks. Rewrite to make more maintainable. */ ListKey VerseKey::ParseVerseList(const char *buf, const char *defaultKey, bool expandRange) { SWKey textkey; char book[2048]; char number[2048]; int tobook = 0; int tonumber = 0; int chap = -1, verse = -1; int bookno = 0; VerseKey curkey, lBound; curkey.setLocale(getLocale()); lBound.setLocale(getLocale()); int loop; char comma = 0; char dash = 0; const char *orig = buf; ListKey tmpListKey; ListKey internalListKey; SWKey tmpDefaultKey = defaultKey; char lastPartial = 0; curkey.AutoNormalize(0); tmpListKey << tmpDefaultKey; tmpListKey.GetElement()->userData = (void *)buf; while (*buf) { switch (*buf) { case ':': number[tonumber] = 0; tonumber = 0; if (*number) chap = atoi(number); *number = 0; break; case '-': case ',': // on number new verse case ';': // on number new chapter number[tonumber] = 0; tonumber = 0; if (*number) { if (chap >= 0) verse = atoi(number); else chap = atoi(number); } *number = 0; book[tobook] = 0; tobook = 0; bookno = -1; if (*book) { for (loop = strlen(book) - 1; loop+1; loop--) { if ((isdigit(book[loop])) || (book[loop] == ' ')) { book[loop] = 0; continue; } else { if ((SW_toupper(book[loop])=='F')&&(loop)) { if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) { book[loop] = 0; continue; } } } break; } for (loop = strlen(book) - 1; loop+1; loop--) { if (book[loop] == ' ') { if (isroman(&book[loop+1])) { if (verse == -1) { verse = chap; chap = from_rom(&book[loop+1]); book[loop] = 0; } } break; } } if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev if (verse == -1) { verse = chap; chap = VerseKey(tmpListKey).Chapter(); *book = 0; } } bookno = getBookAbbrev(book); } if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) { char partial = 0; curkey.Verse(1); curkey.Chapter(1); curkey.Book(1); if (bookno < 0) { curkey.Testament(VerseKey(tmpListKey).Testament()); curkey.Book(VerseKey(tmpListKey).Book()); } else { curkey.Testament(1); curkey.Book(bookno); } if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) { // if (comma) { curkey.Chapter(VerseKey(tmpListKey).Chapter()); curkey.Verse(chap); // chap because this is the first number captured } else { if (chap >= 0) { curkey.Chapter(chap); } else { partial++; curkey.Chapter(1); } if (verse >= 0) { curkey.Verse(verse); } else { partial++; curkey.Verse(1); } } if ((*buf == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper VerseKey newElement; newElement.LowerBound(curkey); newElement.setPosition(TOP); tmpListKey << newElement; tmpListKey.GetElement()->userData = (void *)buf; } else { if (!dash) { // if last separator was not a dash just add if (expandRange && partial) { VerseKey newElement; newElement.LowerBound(curkey); if (partial > 1) curkey.setPosition(MAXCHAPTER); if (partial > 0) curkey = MAXVERSE; newElement.UpperBound(curkey); newElement = TOP; tmpListKey << newElement; tmpListKey.GetElement()->userData = (void *)buf; } else { tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey; tmpListKey.GetElement()->userData = (void *)buf; } } else if (expandRange) { VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement()); if (newElement) { if (partial > 1) curkey = MAXCHAPTER; if (partial > 0) curkey = MAXVERSE; newElement->UpperBound(curkey); *newElement = TOP; tmpListKey.GetElement()->userData = (void *)buf; } } } lastPartial = partial; } *book = 0; chap = -1; verse = -1; if (*buf == ',') comma = 1; else comma = 0; if (*buf == '-') dash = 1; else dash = 0; break; case 10: // ignore these case 13: break; case '.': if (buf > orig) // ignore (break) if preceeding char is not a digit if (!isdigit(*(buf-1))) break; default: if (isdigit(*buf)) { number[tonumber++] = *buf; } else { switch (*buf) { case ' ': // ignore these and don't reset number case 'f': case 'F': break; default: number[tonumber] = 0; tonumber = 0; break; } } if (chap == -1) book[tobook++] = *buf; } buf++; } number[tonumber] = 0; tonumber = 0; if (*number) { if (chap >= 0) verse = atoi(number); else chap = atoi(number); } *number = 0; book[tobook] = 0; tobook = 0; if (*book) { for (loop = strlen(book) - 1; loop+1; loop--) { if ((isdigit(book[loop])) || (book[loop] == ' ')) { book[loop] = 0; continue; } else { if ((SW_toupper(book[loop])=='F')&&(loop)) { if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) { book[loop] = 0; continue; } } } break; } for (loop = strlen(book) - 1; loop+1; loop--) { if (book[loop] == ' ') { if (isroman(&book[loop+1])) { if (verse == -1) { verse = chap; chap = from_rom(&book[loop+1]); book[loop] = 0; } } break; } } if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev. if (verse == -1) { verse = chap; chap = VerseKey(tmpListKey).Chapter(); *book = 0; } } bookno = getBookAbbrev(book); } if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) { char partial = 0; curkey.Verse(1); curkey.Chapter(1); curkey.Book(1); if (bookno < 0) { curkey.Testament(VerseKey(tmpListKey).Testament()); curkey.Book(VerseKey(tmpListKey).Book()); } else { curkey.Testament(1); curkey.Book(bookno); } if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) { // if (comma) { curkey.Chapter(VerseKey(tmpListKey).Chapter()); curkey.Verse(chap); // chap because this is the first number captured } else { if (chap >= 0) { curkey.Chapter(chap); } else { partial++; curkey.Chapter(1); } if (verse >= 0) { curkey.Verse(verse); } else { partial++; curkey.Verse(1); } } if ((*buf == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper VerseKey newElement; newElement.LowerBound(curkey); newElement = TOP; tmpListKey << newElement; tmpListKey.GetElement()->userData = (void *)buf; } else { if (!dash) { // if last separator was not a dash just add if (expandRange && partial) { VerseKey newElement; newElement.LowerBound(curkey); if (partial > 1) curkey = MAXCHAPTER; if (partial > 0) curkey = MAXVERSE; newElement.UpperBound(curkey); newElement = TOP; tmpListKey << newElement; tmpListKey.GetElement()->userData = (void *)buf; } else { tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey; tmpListKey.GetElement()->userData = (void *)buf; } } else if (expandRange) { VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement()); if (newElement) { if (partial > 1) curkey = MAXCHAPTER; if (partial > 0) curkey = MAXVERSE; newElement->UpperBound(curkey); *newElement = TOP; tmpListKey.GetElement()->userData = (void *)buf; } } } } *book = 0; tmpListKey = TOP; tmpListKey.Remove(); // remove defaultKey internalListKey = tmpListKey; internalListKey = TOP; // Align internalListKey to first element before passing back; return internalListKey; } /****************************************************************************** * VerseKey::LowerBound - sets / gets the lower boundary for this key */ VerseKey &VerseKey::LowerBound(const char *lb) { if (!lowerBound) initBounds(); (*lowerBound) = lb; lowerBound->Normalize(); lowerBound->setLocale( this->getLocale() ); boundSet = true; return (*lowerBound); } /****************************************************************************** * VerseKey::UpperBound - sets / gets the upper boundary for this key */ VerseKey &VerseKey::UpperBound(const char *ub) { if (!upperBound) initBounds(); // need to set upperbound parsing to resolve to max verse/chap if not specified (*upperBound) = ub; if (*upperBound < *lowerBound) *upperBound = *lowerBound; upperBound->Normalize(); upperBound->setLocale( this->getLocale() ); // until we have a proper method to resolve max verse/chap use this kludge int len = strlen(ub); bool alpha = false; bool versespec = false; bool chapspec = false; for (int i = 0; i < len; i++) { if (isalpha(ub[i])) alpha = true; if (ub[i] == ':') // if we have a : we assume verse spec versespec = true; if ((isdigit(ub[i])) && (alpha)) // if digit after alpha assume chap spec chapspec = true; } if (!chapspec) *upperBound = MAXCHAPTER; if (!versespec) *upperBound = MAXVERSE; // -- end kludge boundSet = true; return (*upperBound); } /****************************************************************************** * VerseKey::LowerBound - sets / gets the lower boundary for this key */ VerseKey &VerseKey::LowerBound() const { if (!lowerBound) initBounds(); return (*lowerBound); } /****************************************************************************** * VerseKey::UpperBound - sets / gets the upper boundary for this key */ VerseKey &VerseKey::UpperBound() const { if (!upperBound) initBounds(); return (*upperBound); } /****************************************************************************** * VerseKey::ClearBounds - clears bounds for this VerseKey */ void VerseKey::ClearBounds() { initBounds(); } void VerseKey::initBounds() const { if (!upperBound) { upperBound = new VerseKey(); upperBound->AutoNormalize(0); upperBound->Headings(1); } if (!lowerBound) { lowerBound = new VerseKey(); lowerBound->AutoNormalize(0); lowerBound->Headings(1); } lowerBound->Testament(0); lowerBound->Book(0); lowerBound->Chapter(0); lowerBound->Verse(0); upperBound->Testament(2); upperBound->Book(BMAX[1]); upperBound->Chapter(books[1][BMAX[1]-1].chapmax); upperBound->Verse(books[1][BMAX[1]-1].versemax[upperBound->Chapter()-1]); boundSet = false; } /****************************************************************************** * VerseKey::copyFrom - Equates this VerseKey to another VerseKey */ void VerseKey::copyFrom(const VerseKey &ikey) { SWKey::copyFrom(ikey); parse(); } /****************************************************************************** * VerseKey::copyFrom - Equates this VerseKey to another SWKey */ void VerseKey::copyFrom(const SWKey &ikey) { SWKey::copyFrom(ikey); parse(); } /****************************************************************************** * VerseKey::getText - refreshes keytext before returning if cast to * a (char *) is requested */ const char *VerseKey::getText() const { freshtext(); return keytext; } const char *VerseKey::getShortText() const { static char *stext = 0; char buf[2047]; freshtext(); if (book < 1) { if (testament < 1) sprintf(buf, "[ Module Heading ]"); else sprintf(buf, "[ Testament %d Heading ]", (int)testament); } else { sprintf(buf, "%s %d:%d", books[testament-1][book-1].prefAbbrev, chapter, verse); } stdstr(&stext, buf); return stext; } const char *VerseKey::getBookName() const { return books[testament-1][book-1].name; } const char *VerseKey::getBookAbbrev() const { return books[testament-1][book-1].prefAbbrev; } /****************************************************************************** * VerseKey::setPosition(SW_POSITION) - Positions this key * * ENT: p - position * * RET: *this */ void VerseKey::setPosition(SW_POSITION p) { switch (p) { case POS_TOP: testament = LowerBound().Testament(); book = LowerBound().Book(); chapter = LowerBound().Chapter(); verse = LowerBound().Verse(); break; case POS_BOTTOM: testament = UpperBound().Testament(); book = UpperBound().Book(); chapter = UpperBound().Chapter(); verse = UpperBound().Verse(); break; case POS_MAXVERSE: Normalize(); verse = books[testament-1][book-1].versemax[chapter-1]; break; case POS_MAXCHAPTER: verse = 1; Normalize(); chapter = books[testament-1][book-1].chapmax; break; } Normalize(1); Error(); // clear error from normalize } /****************************************************************************** * VerseKey::increment - Increments key a number of verses * * ENT: step - Number of verses to jump forward * * RET: *this */ void VerseKey::increment(int step) { char ierror = 0; Index(Index() + step); while ((!verse) && (!headings) && (!ierror)) { Index(Index() + 1); ierror = Error(); } error = (ierror) ? ierror : error; } /****************************************************************************** * VerseKey::decrement - Decrements key a number of verses * * ENT: step - Number of verses to jump backward * * RET: *this */ void VerseKey::decrement(int step) { char ierror = 0; Index(Index() - step); while ((!verse) && (!headings) && (!ierror)) { Index(Index() - 1); ierror = Error(); } if ((ierror) && (!headings)) (*this)++; error = (ierror) ? ierror : error; } /****************************************************************************** * VerseKey::Normalize - checks limits and normalizes if necessary (e.g. * Matthew 29:47 = Mark 2:2). If last verse is * exceeded, key is set to last Book CH:VS * RET: *this */ void VerseKey::Normalize(char autocheck) { error = 0; if ((autocheck) && (!autonorm)) // only normalize if we were explicitely called or if autonorm is turned on return; if ((headings) && (!verse)) // this is cheeze and temporary until deciding what actions should be taken. return; // so headings should only be turned on when positioning with Index() or incrementors while ((testament < 3) && (testament > 0)) { if (book > BMAX[testament-1]) { book -= BMAX[testament-1]; testament++; continue; } if (book < 1) { if (--testament > 0) { book += BMAX[testament-1]; } continue; } if (chapter > books[testament-1][book-1].chapmax) { chapter -= books[testament-1][book-1].chapmax; book++; continue; } if (chapter < 1) { if (--book > 0) { chapter += books[testament-1][book-1].chapmax; } else { if (testament > 1) { chapter += books[0][BMAX[0]-1].chapmax; } } continue; } if (verse > books[testament-1][book-1].versemax[chapter-1]) { // -1 because e.g chapter 1 of Matthew is books[1][0].versemax[0] verse -= books[testament-1][book-1].versemax[chapter++ - 1]; continue; } if (verse < 1) { if (--chapter > 0) { verse += books[testament-1][book-1].versemax[chapter-1]; } else { if (book > 1) { verse += books[testament-1][book-2].versemax[books[testament-1][book-2].chapmax-1]; } else { if (testament > 1) { verse += books[0][BMAX[0]-1].versemax[books[0][BMAX[0]-1].chapmax-1]; } } } continue; } break; // If we've made it this far (all failure checks continue) we're ok } if (testament > 2) { testament = 2; book = BMAX[testament-1]; chapter = books[testament-1][book-1].chapmax; verse = books[testament-1][book-1].versemax[chapter-1]; error = KEYERR_OUTOFBOUNDS; } if (testament < 1) { error = ((!headings) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0; testament = ((headings) ? 0 : 1); book = ((headings) ? 0 : 1); chapter = ((headings) ? 0 : 1); verse = ((headings) ? 0 : 1); } if (_compare(UpperBound()) > 0) { *this = UpperBound(); error = KEYERR_OUTOFBOUNDS; } if (_compare(LowerBound()) < 0) { *this = LowerBound(); error = KEYERR_OUTOFBOUNDS; } } /****************************************************************************** * VerseKey::Testament - Gets testament * * RET: value of testament */ char VerseKey::Testament() const { return testament; } /****************************************************************************** * VerseKey::Book - Gets book * * RET: value of book */ char VerseKey::Book() const { return book; } /****************************************************************************** * VerseKey::Chapter - Gets chapter * * RET: value of chapter */ int VerseKey::Chapter() const { return chapter; } /****************************************************************************** * VerseKey::Verse - Gets verse * * RET: value of verse */ int VerseKey::Verse() const { return verse; } /****************************************************************************** * VerseKey::Testament - Sets/gets testament * * ENT: itestament - value which to set testament * [MAXPOS(char)] - only get * * RET: if unchanged -> value of testament * if changed -> previous value of testament */ char VerseKey::Testament(char itestament) { char retval = testament; if (itestament != MAXPOS(char)) { testament = itestament; Normalize(1); } return retval; } /****************************************************************************** * VerseKey::Book - Sets/gets book * * ENT: ibook - value which to set book * [MAXPOS(char)] - only get * * RET: if unchanged -> value of book * if changed -> previous value of book */ char VerseKey::Book(char ibook) { char retval = book; Chapter(1); book = ibook; Normalize(1); return retval; } /****************************************************************************** * VerseKey::Chapter - Sets/gets chapter * * ENT: ichapter - value which to set chapter * [MAXPOS(int)] - only get * * RET: if unchanged -> value of chapter * if changed -> previous value of chapter */ int VerseKey::Chapter(int ichapter) { int retval = chapter; Verse(1); chapter = ichapter; Normalize(1); return retval; } /****************************************************************************** * VerseKey::Verse - Sets/gets verse * * ENT: iverse - value which to set verse * [MAXPOS(int)] - only get * * RET: if unchanged -> value of verse * if changed -> previous value of verse */ int VerseKey::Verse(int iverse) { int retval = verse; verse = iverse; Normalize(1); return retval; } /****************************************************************************** * VerseKey::AutoNormalize - Sets/gets flag that tells VerseKey to auto- * matically normalize itself when modified * * ENT: iautonorm - value which to set autonorm * [MAXPOS(char)] - only get * * RET: if unchanged -> value of autonorm * if changed -> previous value of autonorm */ char VerseKey::AutoNormalize(char iautonorm) { char retval = autonorm; if (iautonorm != MAXPOS(char)) { autonorm = iautonorm; Normalize(1); } return retval; } /****************************************************************************** * VerseKey::Headings - Sets/gets flag that tells VerseKey to include * chap/book/testmnt/module headings * * ENT: iheadings - value which to set headings * [MAXPOS(char)] - only get * * RET: if unchanged -> value of headings * if changed -> previous value of headings */ char VerseKey::Headings(char iheadings) { char retval = headings; if (iheadings != MAXPOS(char)) { headings = iheadings; Normalize(1); } return retval; } /****************************************************************************** * VerseKey::findindex - binary search to find the index closest, but less * than the given value. * * ENT: array - long * to array to search * size - number of elements in the array * value - value to find * * RET: the index into the array that is less than but closest to value */ int VerseKey::findindex(long *array, int size, long value) { int lbound, ubound, tval; lbound = 0; ubound = size - 1; while ((ubound - lbound) > 1) { tval = lbound + (ubound-lbound)/2; if (array[tval] <= value) lbound = tval; else ubound = tval; } return (array[ubound] <= value) ? ubound : lbound; } /****************************************************************************** * VerseKey::Index - Gets index based upon current verse * * RET: offset */ long VerseKey::Index() const { long offset; if (!testament) { // if we want module heading offset = 0; verse = 0; } else { if (!book) chapter = 0; if (!chapter) verse = 0; offset = offsets[testament-1][0][book]; offset = offsets[testament-1][1][(int)offset + chapter]; if (!(offset|verse)) // if we have a testament but nothing else. offset = 1; } return (offset + verse); } /****************************************************************************** * VerseKey::Index - Gets index based upon current verse * * RET: offset */ long VerseKey::NewIndex() const { static long otMaxIndex = 32300 - 8245; // total positions - new testament positions // static long otMaxIndex = offsets[0][1][(int)offsets[0][0][BMAX[0]] + books[0][BMAX[0]].chapmax]; return ((testament-1) * otMaxIndex) + Index(); } /****************************************************************************** * VerseKey::Index - Sets index based upon current verse * * ENT: iindex - value to set index to * * RET: offset */ long VerseKey::Index(long iindex) { long offset; // This is the dirty stuff -------------------------------------------- if (!testament) testament = 1; if (iindex < 1) { // if (-) or module heading if (testament < 2) { if (iindex < 0) { testament = 0; // previously we changed 0 -> 1 error = KEYERR_OUTOFBOUNDS; } else testament = 0; // we want module heading } else { testament--; iindex = (offsets[testament-1][1][offsize[testament-1][1]-1] + books[testament-1][BMAX[testament-1]-1].versemax[books[testament-1][BMAX[testament-1]-1].chapmax-1]) + iindex; // What a doozy! ((offset of last chapter + number of verses in the last chapter) + iindex) } } // -------------------------------------------------------------------- if (testament) { if ((!error) && (iindex)) { offset = findindex(offsets[testament-1][1], offsize[testament-1][1], iindex); verse = iindex - offsets[testament-1][1][offset]; book = findindex(offsets[testament-1][0], offsize[testament-1][0], offset); chapter = offset - offsets[testament-1][0][VerseKey::book]; verse = (chapter) ? verse : 0; // funny check. if we are index=1 (testmt header) all gets set to 0 exept verse. Don't know why. Fix if you figure out. Think its in the offsets table. if (verse) { // only check if -1 won't give negative if (verse > books[testament-1][book-1].versemax[chapter-1]) { if (testament > 1) { verse = books[testament-1][book-1].versemax[chapter-1]; error = KEYERR_OUTOFBOUNDS; } else { testament++; Index(verse - books[testament-2][book-1].versemax[chapter-1]); } } } } } if (_compare(UpperBound()) > 0) { *this = UpperBound(); error = KEYERR_OUTOFBOUNDS; } if (_compare(LowerBound()) < 0) { *this = LowerBound(); error = KEYERR_OUTOFBOUNDS; } return Index(); } /****************************************************************************** * VerseKey::compare - Compares another SWKey object * * ENT: ikey - key to compare with this one * * RET: >0 if this versekey is greater than compare versekey * <0 < * 0 = */ int VerseKey::compare(const SWKey &ikey) { VerseKey ivkey = (const char *)ikey; return _compare(ivkey); } /****************************************************************************** * VerseKey::_compare - Compares another VerseKey object * * ENT: ikey - key to compare with this one * * RET: >0 if this versekey is greater than compare versekey * <0 < * 0 = */ int VerseKey::_compare(const VerseKey &ivkey) { long keyval1 = 0; long keyval2 = 0; keyval1 += Testament() * 1000000000; keyval2 += ivkey.Testament() * 1000000000; keyval1 += Book() * 1000000; keyval2 += ivkey.Book() * 1000000; keyval1 += Chapter() * 1000; keyval2 += ivkey.Chapter() * 1000; keyval1 += Verse(); keyval2 += ivkey.Verse(); keyval1 -= keyval2; keyval1 = (keyval1) ? ((keyval1 > 0) ? 1 : -1) /*keyval1/labs(keyval1)*/:0; // -1 | 0 | 1 return keyval1; } const char *VerseKey::getOSISRef() const { static char buf[5][254]; static char loop = 0; if (loop > 4) loop = 0; static char *osisotbooks[] = { "Gen","Exod","Lev","Num","Deut","Josh","Judg","Ruth","1Sam","2Sam", "1Kgs","2Kgs","1Chr","2Chr","Ezra","Neh","Esth","Job","Ps", "Prov", // added this. Was not in OSIS spec "Eccl", "Song","Isa","Jer","Lam","Ezek","Dan","Hos","Joel","Amos","Obad", "Jonah","Mic","Nah","Hab","Zeph","Hag","Zech","Mal","Bar","PrAzar", "Bel","Sus","1Esd","2Esd","AddEsth","EpJer","Jdt","1Macc","2Macc","3Macc", "4Macc","PrMan","Ps151","Sir","Tob","Wis"}; static char *osisntbooks[] = { "Matt","Mark","Luke","John","Acts","Rom","1Cor","2Cor","Gal","Eph", "Phil","Col","1Thess","2Thess","1Tim","2Tim","Titus","Phlm","Heb","Jas", "1Pet","2Pet","1John","2John","3John","Jude","Rev"}; static char **osisbooks[] = { osisotbooks, osisntbooks }; if (Verse()) sprintf(buf[loop], "%s.%d.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter(), (int)Verse()); else if (Chapter()) sprintf(buf[loop], "%s.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter()); else if (Book()) sprintf(buf[loop], "%s", osisbooks[Testament()-1][Book()-1]); else sprintf(buf[loop], ""); return buf[loop++]; } /****************************************************************************** * VerseKey::getRangeText - returns parsable range text for this key */ const char *VerseKey::getRangeText() const { if (isBoundSet()) { char buf[1023]; sprintf(buf, "%s-%s", (const char *)LowerBound(), (const char *)UpperBound()); stdstr(&rangeText, buf); } else stdstr(&rangeText, getText()); return rangeText; } SWORD_NAMESPACE_END