/******************************************************************************
* VerseKey.cpp - code for class 'VerseKey'- a standard Biblical verse key
*/
#include <swmacs.h>
#include <utilfuns.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#ifndef __GNUC__
#include <io.h>
#else
#include <unistd.h>
#endif
#include <utilstr.h>
#include <swkey.h>
#include <swlog.h>
#include <versekey.h>
#include <localemgr.h>
extern "C" {
#include <roman.h>
}
static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0};
SWClass VerseKey::classdef(classes);
/******************************************************************************
* Initialize static members of VerseKey
*/
#include <canon.h> // 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 = 1;
book = 1;
chapter = 1;
verse = 1;
int error = 0;
if (keytext) {
ListKey tmpListKey = VerseKey::ParseVerseList(keytext);
if (tmpListKey.Count()) {
SWKey::setText((const char *)tmpListKey);
for (testament = 1; testament < 3; testament++) {
for (book = 1; book <= BMAX[testament-1]; book++) {
if (!strncmp(keytext, books[testament-1][book-1].name, strlen(books[testament-1][book-1].name)))
break;
}
if (book <= BMAX[testament-1])
break;
}
if (testament < 3) {
sscanf(&keytext[strlen(books[testament-1][book-1].name)], "%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[255];
char number[255];
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;
}