aboutsummaryrefslogtreecommitdiffstats
path: root/src/keys
diff options
context:
space:
mode:
Diffstat (limited to 'src/keys')
-rw-r--r--src/keys/listkey.cpp257
-rw-r--r--src/keys/strkey.cpp41
-rw-r--r--src/keys/swkey.cpp196
-rw-r--r--src/keys/treekey.cpp30
-rw-r--r--src/keys/treekeyidx.cpp590
-rw-r--r--src/keys/versekey.cpp1450
6 files changed, 2564 insertions, 0 deletions
diff --git a/src/keys/listkey.cpp b/src/keys/listkey.cpp
new file mode 100644
index 0000000..0d1ff33
--- /dev/null
+++ b/src/keys/listkey.cpp
@@ -0,0 +1,257 @@
+/******************************************************************************
+ * listkey.cpp - code for base class 'ListKey'. ListKey is the basis for all
+ * types of keys that have lists of specified indexes
+ * (e.g. a list of verses, place, etc.)
+ */
+
+#include <utilfuns.h>
+#include <string.h>
+#include <stdlib.h>
+#include <swkey.h>
+#include <listkey.h>
+
+static const char *classes[] = {"ListKey", "SWKey", "SWObject", 0};
+SWClass ListKey::classdef(classes);
+
+/******************************************************************************
+ * ListKey Constructor - initializes instance of ListKey
+ *
+ * ENT: ikey - text key
+ */
+
+ListKey::ListKey(const char *ikey): SWKey(ikey) {
+ arraymax = 0;
+ ClearList();
+ init();
+}
+
+
+ListKey::ListKey(ListKey const &k) : SWKey(k.keytext) {
+ arraymax = k.arraymax;
+ arraypos = k.arraypos;
+ arraycnt = k.arraycnt;
+ array = (arraymax)?(SWKey **)malloc(k.arraymax * sizeof(SWKey *)):0;
+ for (int i = 0; i < arraycnt; i++)
+ array[i] = k.array[i]->clone();
+ init();
+}
+
+
+void ListKey::init() {
+ myclass = &classdef;
+}
+
+
+SWKey *ListKey::clone() const
+{
+ return new ListKey(*this);
+}
+
+/******************************************************************************
+ * ListKey Destructor - cleans up instance of ListKey
+ */
+
+ListKey::~ListKey()
+{
+ ClearList();
+}
+
+
+/******************************************************************************
+ * ListKey::ClearList - Clears out elements of list
+ */
+
+void ListKey::ClearList()
+{
+ int loop;
+
+ if (arraymax) {
+ for (loop = 0; loop < arraycnt; loop++)
+ delete array[loop];
+
+ free(array);
+ arraymax = 0;
+ }
+ arraycnt = 0;
+ arraypos = 0;
+ array = 0;
+}
+
+
+/******************************************************************************
+ * ListKey::copyFrom Equates this ListKey to another ListKey object
+ *
+ * ENT: ikey - other ListKey object
+ */
+
+void ListKey::copyFrom(const ListKey &ikey) {
+ ClearList();
+
+ arraymax = ikey.arraymax;
+ arraypos = ikey.arraypos;
+ arraycnt = ikey.arraycnt;
+ array = (arraymax)?(SWKey **)malloc(ikey.arraymax * sizeof(SWKey *)):0;
+ for (int i = 0; i < arraycnt; i++)
+ array[i] = ikey.array[i]->clone();
+
+ SetToElement(0);
+}
+
+
+/******************************************************************************
+ * ListKey::add - Adds an element to the list
+ */
+
+void ListKey::add(const SWKey &ikey) {
+ if (++arraycnt > arraymax) {
+ array = (SWKey **) ((array) ? realloc(array, (arraycnt + 32) * sizeof(SWKey *)) : calloc(arraycnt + 32, sizeof(SWKey *)));
+ arraymax = arraycnt + 32;
+ }
+ array[arraycnt-1] = ikey.clone();
+ SetToElement(arraycnt-1);
+}
+
+
+
+/******************************************************************************
+ * ListKey::setPosition(SW_POSITION) - Positions this key
+ *
+ * ENT: p - position
+ *
+ * RET: *this
+ */
+
+void ListKey::setPosition(SW_POSITION p) {
+ switch (p) {
+ case 1: // GCC won't compile P_TOP
+ SetToElement(0);
+ break;
+ case 2: // GCC won't compile P_BOTTOM
+ SetToElement(arraycnt-1);
+ break;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::increment - Increments a number of elements
+ */
+
+void ListKey::increment(int step) {
+ if (step < 0) {
+ decrement(step*-1);
+ return;
+ }
+ Error(); // clear error
+ for(; step && !Error(); step--) {
+ if (arraypos < arraycnt) {
+ (*(array[arraypos]))++;
+ if (array[arraypos]->Error()) {
+ SetToElement(arraypos+1);
+ }
+ else *this = (const char *)(*array[arraypos]);
+ }
+ else error = KEYERR_OUTOFBOUNDS;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::decrement - Decrements a number of elements
+ */
+
+void ListKey::decrement(int step) {
+ if (step < 0) {
+ increment(step*-1);
+ return;
+ }
+ Error(); // clear error
+ for(; step && !Error(); step--) {
+ if (arraypos > -1) {
+ (*(array[arraypos]))--;
+ if (array[arraypos]->Error()) {
+ SetToElement(arraypos-1, BOTTOM);
+ }
+ else *this = (const char *)(*array[arraypos]);
+ }
+ else error = KEYERR_OUTOFBOUNDS;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::Count - Returns number of elements in list
+ */
+
+int ListKey::Count() {
+ return arraycnt;
+}
+
+
+/******************************************************************************
+ * ListKey::SetToElement - Sets key to element number
+ *
+ * ENT: ielement - element number to set to
+ *
+ * RET: error status
+ */
+
+char ListKey::SetToElement(int ielement, SW_POSITION pos) {
+ arraypos = ielement;
+ if (arraypos >= arraycnt) {
+ arraypos = (arraycnt>0)?arraycnt - 1:0;
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else {
+ if (arraypos < 0) {
+ arraypos = 0;
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else {
+ error = 0;
+ }
+ }
+
+ if (arraycnt) {
+ (*array[arraypos]) = pos;
+ *this = (const char *)(*array[arraypos]);
+ }
+ else *this = "";
+
+ return error;
+}
+
+
+/******************************************************************************
+ * ListKey::GetElement - Gets a key element number
+ *
+ * ENT: pos - element number to get (or default current)
+ *
+ * RET: Key or null on error
+ */
+
+SWKey *ListKey::GetElement(int pos) {
+ if (pos < 0)
+ pos = arraypos;
+
+ if (pos >=arraycnt)
+ error = KEYERR_OUTOFBOUNDS;
+
+ return (error) ? 0:array[pos];
+}
+
+
+/******************************************************************************
+ * ListKey::Remove - Removes current element from list
+ */
+
+void ListKey::Remove() {
+ if ((arraypos > -1) && (arraypos < arraycnt)) {
+ delete array[arraypos];
+ if (arraypos < arraycnt - 1)
+ memmove(&array[arraypos], &array[arraypos+1], (arraycnt - arraypos - 1) * sizeof(SWKey *));
+ arraycnt--;
+
+ SetToElement((arraypos)?arraypos-1:0);
+ }
+}
diff --git a/src/keys/strkey.cpp b/src/keys/strkey.cpp
new file mode 100644
index 0000000..7e2d539
--- /dev/null
+++ b/src/keys/strkey.cpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * StrKey.cpp - code for class 'StrKey'- a standard string key class (used
+ * for modules that index on single strings (eg. cities,
+ * names, words, etc.)
+ */
+
+#include <swmacs.h>
+#include <utilfuns.h>
+#include <strkey.h>
+#include <string.h>
+#include <stdio.h>
+
+
+static const char *classes[] = {"StrKey", "SWKey", "SWObject", 0};
+SWClass StrKey::classdef(classes);
+
+/******************************************************************************
+ * StrKey Constructor - initializes instance of StrKey
+ *
+ * ENT: ikey - text key (word, city, name, etc.)
+ */
+
+StrKey::StrKey(const char *ikey) : SWKey(ikey)
+{
+ init();
+}
+
+
+void StrKey::init() {
+ myclass = &classdef;
+}
+
+
+/******************************************************************************
+ * StrKey Destructor - cleans up instance of StrKey
+ *
+ * ENT: ikey - text key
+ */
+
+StrKey::~StrKey() {
+}
diff --git a/src/keys/swkey.cpp b/src/keys/swkey.cpp
new file mode 100644
index 0000000..e633369
--- /dev/null
+++ b/src/keys/swkey.cpp
@@ -0,0 +1,196 @@
+/******************************************************************************
+ * swkey.cpp - code for base class 'SWKey'. SWKey is the basis for all
+ * types of keys for indexing into modules (e.g. verse, word,
+ * place, etc.)
+ */
+
+#include <swkey.h>
+#include <utilfuns.h>
+#include <string.h>
+
+static const char *classes[] = {"SWKey", "SWObject", 0};
+SWClass SWKey::classdef(classes);
+
+/******************************************************************************
+ * SWKey Constructor - initializes instance of SWKey
+ *
+ * ENT: ikey - text key
+ */
+
+SWKey::SWKey(const char *ikey)
+{
+ index = 0;
+ persist = 0;
+ keytext = 0;
+ error = 0;
+ stdstr(&keytext, ikey);
+ init();
+}
+
+SWKey::SWKey(SWKey const &k)
+{
+ index = k.index;
+ persist = k.persist;
+ userData = k.userData;
+ keytext = 0;
+ error = k.error;
+ stdstr(&keytext, k.keytext);
+ init();
+}
+
+void SWKey::init() {
+ myclass = &classdef;
+}
+
+SWKey *SWKey::clone() const
+{
+ return new SWKey(*this);
+}
+
+/******************************************************************************
+ * SWKey Destructor - cleans up instance of SWKey
+ */
+
+SWKey::~SWKey() {
+ if (keytext)
+ delete [] keytext;
+}
+
+
+/******************************************************************************
+ * SWKey::Persist - Gets whether this object itself persists within a
+ * module that it was used to SetKey or just a copy.
+ * (1 - persists in module; 0 - a copy is attempted
+ *
+ * RET: value of persist
+ */
+
+char SWKey::Persist() const
+{
+ return persist;
+}
+
+
+/******************************************************************************
+ * SWKey::Persist - Set/gets whether this object itself persists within a
+ * module that it was used to SetKey or just a copy.
+ * (1 - persists in module; 0 - a copy is attempted
+ *
+ * ENT: ipersist - value which to set persist
+ * [-1] - only get
+ *
+ * RET: value of persist
+ */
+
+char SWKey::Persist(signed char ipersist)
+{
+ if (ipersist != -1)
+ persist = ipersist;
+
+ return persist;
+}
+
+
+/******************************************************************************
+ * SWKey::Error - Gets and clears error status
+ *
+ * RET: error status
+ */
+
+char SWKey::Error()
+{
+ char retval = error;
+
+ error = 0;
+ return retval;
+}
+
+
+/******************************************************************************
+ * SWKey::setText Equates this SWKey to a character string
+ *
+ * ENT: ikey - other swkey object
+ */
+
+void SWKey::setText(const char *ikey) {
+ stdstr(&keytext, ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::copyFrom Equates this SWKey to another SWKey object
+ *
+ * ENT: ikey - other swkey object
+ */
+
+void SWKey::copyFrom(const SWKey &ikey) {
+// not desirable Persist(ikey.Persist());
+ setText((const char *)ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::getText - returns text key if (char *) cast is requested
+ */
+
+const char *SWKey::getText() const {
+ return keytext;
+}
+
+
+/******************************************************************************
+ * SWKey::compare - Compares another VerseKey object
+ *
+ * ENT: ikey - key to compare with this one
+ *
+ * RET: > 0 if this key is greater than compare key
+ * < 0
+ * 0
+ */
+
+int SWKey::compare(const SWKey &ikey)
+{
+ return strcmp((const char *)*this, (const char *)ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::setPosition(SW_POSITION) - Positions this key if applicable
+ */
+
+void SWKey::setPosition(SW_POSITION p) {
+ switch (p) {
+ case POS_TOP:
+// *this = "";
+ break;
+ case POS_BOTTOM:
+// *this = "zzzzzzzzz";
+ break;
+ }
+}
+
+
+/******************************************************************************
+ * SWKey::increment - Increments key a number of entries
+ *
+ * ENT: increment - Number of entries to jump forward
+ *
+ * RET: *this
+ */
+
+void SWKey::increment(int) {
+ error = KEYERR_OUTOFBOUNDS;
+}
+
+
+/******************************************************************************
+ * SWKey::decrement - Decrements key a number of entries
+ *
+ * ENT: decrement - Number of entries to jump backward
+ *
+ * RET: *this
+ */
+
+void SWKey::decrement(int) {
+ error = KEYERR_OUTOFBOUNDS;
+}
diff --git a/src/keys/treekey.cpp b/src/keys/treekey.cpp
new file mode 100644
index 0000000..d92b7a4
--- /dev/null
+++ b/src/keys/treekey.cpp
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * versekey.h - code for class 'versekey'- a standard Biblical verse key
+ *
+ * $Id: treekey.cpp,v 1.2 2002/04/15 21:26:44 scribe Exp $
+ *
+ * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org)
+ * CrossWire Bible Society
+ * P. O. Box 2528
+ * Tempe, AZ 85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <treekey.h>
+
+static const char *classes[] = {"TreeKey", "SWKey", "SWObject", 0};
+SWClass TreeKey::classdef(classes);
+
+void TreeKey::init() {
+ myclass = &classdef;
+}
diff --git a/src/keys/treekeyidx.cpp b/src/keys/treekeyidx.cpp
new file mode 100644
index 0000000..acd9b5a
--- /dev/null
+++ b/src/keys/treekeyidx.cpp
@@ -0,0 +1,590 @@
+/******************************************************************************
+ * versekey.h - code for class 'versekey'- a standard Biblical verse key
+ *
+ * $Id: treekeyidx.cpp,v 1.7 2002/04/15 21:26:44 scribe Exp $
+ *
+ * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org)
+ * CrossWire Bible Society
+ * P. O. Box 2528
+ * Tempe, AZ 85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <treekeyidx.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string>
+
+#ifndef __GNUC__
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+using namespace std;
+static const char nl = '\n';
+static const char *classes[] = {"TreeKeyIdx", "TreeKey", "SWKey", "SWObject", 0};
+SWClass TreeKeyIdx::classdef(classes);
+
+
+TreeKeyIdx::TreeKeyIdx(const TreeKeyIdx &ikey) : currentNode() {
+ init();
+ path = 0;
+ idxfd = 0;
+ datfd = 0;
+ copyFrom(ikey);
+}
+
+TreeKeyIdx::TreeKeyIdx(const char *idxPath, int fileMode) : currentNode() {
+ char buf[127];
+
+ init();
+ path = 0;
+ stdstr(&path, idxPath);
+
+#ifndef O_BINARY // O_BINARY is needed in Borland C++ 4.53
+#define O_BINARY 0 // If it hasn't been defined than we probably
+#endif // don't need it.
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = O_RDWR;
+ }
+
+ sprintf(buf, "%s.idx", path);
+ idxfd = FileMgr::systemFileMgr.open(buf, fileMode|O_BINARY, true);
+ sprintf(buf, "%s.dat", path);
+ datfd = FileMgr::systemFileMgr.open(buf, fileMode|O_BINARY, true);
+
+ if (datfd <= 0) {
+ sprintf(buf, "Error: %d", errno);
+ perror(buf);
+ error = errno;
+ }
+ else {
+ root();
+ }
+}
+
+
+void TreeKeyIdx::init() {
+ myclass = &classdef;
+}
+
+
+TreeKeyIdx::~TreeKeyIdx () {
+ if (path)
+ delete [] path;
+
+ FileMgr::systemFileMgr.close(idxfd);
+ FileMgr::systemFileMgr.close(datfd);
+}
+
+
+const char *TreeKeyIdx::getLocalName() {
+ return currentNode.name;
+}
+
+
+const char *TreeKeyIdx::getUserData(int *size) {
+ if (size)
+ *size = (int)currentNode.dsize;
+ return currentNode.userData;
+}
+
+
+void TreeKeyIdx::setUserData(const char *userData, int size) {
+ if (currentNode.userData)
+ delete currentNode.userData;
+
+ if (!size)
+ size = strlen(userData) + 1;
+
+ currentNode.userData = new char [ size ];
+ memcpy(currentNode.userData, userData, size);
+ currentNode.dsize = size;
+}
+
+const char *TreeKeyIdx::setLocalName(const char *newName) {
+ stdstr(&(currentNode.name), newName);
+ return currentNode.name;
+}
+
+
+void TreeKeyIdx::save() {
+ saveTreeNode(&currentNode);
+}
+
+
+const char *TreeKeyIdx::getFullName() const {
+ TreeNode parent;
+ static string fullPath;
+ fullPath = currentNode.name;
+ parent.parent = currentNode.parent;
+ while (parent.parent > -1) {
+ getTreeNodeFromIdxOffset(parent.parent, &parent);
+ fullPath = ((string)parent.name) + (string) "/" + fullPath;
+ }
+ return fullPath.c_str();
+}
+
+
+void TreeKeyIdx::root() {
+ error = getTreeNodeFromIdxOffset(0, &currentNode);
+}
+
+
+bool TreeKeyIdx::parent() {
+ if (currentNode.parent > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.parent, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::firstChild() {
+ if (currentNode.firstChild > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.firstChild, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::nextSibling() {
+ if (currentNode.next > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.next, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::previousSibling() {
+ TreeNode iterator;
+ __u32 target = currentNode.offset;
+ if (currentNode.parent > -1) {
+ getTreeNodeFromIdxOffset(currentNode.parent, &iterator);
+ getTreeNodeFromIdxOffset(iterator.firstChild, &iterator);
+ if (iterator.offset != target) {
+ while ((iterator.next != target) && (iterator.next > -1))
+ getTreeNodeFromIdxOffset(iterator.next, &iterator);
+ if (iterator.next > -1) {
+ error = getTreeNodeFromIdxOffset(iterator.offset, &currentNode);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::hasChildren() {
+ return (currentNode.firstChild > -1);
+}
+
+
+void TreeKeyIdx::append() {
+ TreeNode lastSib;
+ if (currentNode.offset) {
+ getTreeNodeFromIdxOffset(currentNode.offset, &lastSib);
+ while (lastSib.next > -1) {
+ getTreeNodeFromIdxOffset(lastSib.next, &lastSib);
+ }
+ __u32 idxOffset = lseek(idxfd->getFd(), 0, SEEK_END);
+ lastSib.next = idxOffset;
+ saveTreeNodeOffsets(&lastSib);
+ __u32 parent = currentNode.parent;
+ currentNode.clear();
+ currentNode.offset = idxOffset;
+ currentNode.parent = parent;
+ }
+}
+
+
+void TreeKeyIdx::appendChild() {
+ if (firstChild()) {
+ append();
+ }
+ else {
+ __u32 idxOffset = lseek(idxfd->getFd(), 0, SEEK_END);
+ currentNode.firstChild = idxOffset;
+ saveTreeNodeOffsets(&currentNode);
+ __u32 parent = currentNode.offset;
+ currentNode.clear();
+ currentNode.offset = idxOffset;
+ currentNode.parent = parent;
+ }
+}
+
+
+void TreeKeyIdx::insertBefore() {
+}
+
+
+void TreeKeyIdx::remove() {
+}
+
+
+/******************************************************************************
+ * TreeKeyIdx::Create - Creates new key idx/dat files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+signed char TreeKeyIdx::create(const char *ipath) {
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s.dat", path);
+ unlink(buf);
+ fd = FileMgr::systemFileMgr.open(buf, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE);
+ fd->getFd();
+ FileMgr::systemFileMgr.close(fd);
+
+ sprintf(buf, "%s.idx", path);
+ unlink(buf);
+ fd2 = FileMgr::systemFileMgr.open(buf, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE);
+ fd2->getFd();
+ FileMgr::systemFileMgr.close(fd2);
+
+ TreeKeyIdx newTree(path);
+ TreeKeyIdx::TreeNode root;
+ stdstr(&(root.name), "");
+ newTree.saveTreeNode(&root);
+
+ delete [] path;
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * zStr::getidxbufdat - Gets the index string at the given dat offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd and must
+ * be free'd by calling function
+ *
+ * ENT: ioffset - offset in dat file to lookup
+ * node - address of pointer to allocate for storage of string
+ */
+
+void TreeKeyIdx::getTreeNodeFromDatOffset(long ioffset, TreeNode *node) const {
+ char ch;
+ __s32 tmp;
+ __u16 tmp2;
+
+ if (datfd > 0) {
+
+ lseek(datfd->getFd(), ioffset, SEEK_SET);
+
+ read(datfd->getFd(), &tmp, 4);
+ node->parent = swordtoarch32(tmp);
+
+ read(datfd->getFd(), &tmp, 4);
+ node->next = swordtoarch32(tmp);
+
+ read(datfd->getFd(), &tmp, 4);
+ node->firstChild = swordtoarch32(tmp);
+
+ string name;
+ do {
+ read(datfd->getFd(), &ch, 1);
+ name += ch;
+ } while (ch);
+
+ stdstr(&(node->name), name.c_str());
+
+ read(datfd->getFd(), &tmp2, 2);
+ node->dsize = swordtoarch16(tmp2);
+
+ if (node->dsize) {
+ if (node->userData)
+ delete [] node->userData;
+ node->userData = new char [node->dsize];
+ read(datfd->getFd(), node->userData, node->dsize);
+ }
+ }
+}
+
+
+/******************************************************************************
+ * zStr::getidxbuf - Gets the index string at the given idx offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd
+ * and must be freed by calling function
+ *
+ * ENT: ioffset - offset in idx file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+char TreeKeyIdx::getTreeNodeFromIdxOffset(long ioffset, TreeNode *node) const {
+ __u32 offset;
+ char error = 0;
+
+ if (ioffset < 0) {
+ ioffset = 0;
+ error = KEYERR_OUTOFBOUNDS;
+ }
+
+ node->offset = ioffset;
+ if (idxfd > 0) {
+ lseek(idxfd->getFd(), ioffset, SEEK_SET);
+ if (read(idxfd->getFd(), &offset, 4) == 4) {
+ offset = swordtoarch32(offset);
+ getTreeNodeFromDatOffset(offset, node);
+ }
+ else {
+ lseek(idxfd->getFd(), -4, SEEK_END);
+ if (read(idxfd->getFd(), &offset, 4) == 4) {
+ offset = swordtoarch32(offset);
+ getTreeNodeFromDatOffset(offset, node);
+ }
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ }
+ return error;
+}
+
+
+unsigned long TreeKeyIdx::getOffset() const {
+ return currentNode.offset;
+}
+
+void TreeKeyIdx::setOffset(unsigned long offset) {
+ error = getTreeNodeFromIdxOffset(offset, &currentNode);
+}
+
+
+void TreeKeyIdx::saveTreeNodeOffsets(TreeNode *node) {
+ long datOffset = 0;
+ __s32 tmp;
+
+ if (idxfd > 0) {
+ lseek(idxfd->getFd(), node->offset, SEEK_SET);
+ if (read(idxfd->getFd(), &tmp, 4) != 4) {
+ datOffset = lseek(datfd->getFd(), 0, SEEK_END);
+ tmp = archtosword32(datOffset);
+ write(idxfd->getFd(), &tmp, 4);
+ }
+ else {
+ datOffset = swordtoarch32(tmp);
+ lseek(datfd->getFd(), datOffset, SEEK_SET);
+ }
+
+ tmp = archtosword32(node->parent);
+ write(datfd->getFd(), &tmp, 4);
+
+ tmp = archtosword32(node->next);
+ write(datfd->getFd(), &tmp, 4);
+
+ tmp = archtosword32(node->firstChild);
+ write(datfd->getFd(), &tmp, 4);
+ }
+}
+
+
+void TreeKeyIdx::copyFrom(const TreeKeyIdx &ikey) {
+
+ SWKey::copyFrom(ikey);
+
+ currentNode.offset = ikey.currentNode.offset;
+ currentNode.parent = ikey.currentNode.parent;
+ currentNode.next = ikey.currentNode.next;
+ currentNode.firstChild = ikey.currentNode.firstChild;
+ stdstr(&(currentNode.name), ikey.currentNode.name);
+ currentNode.dsize = ikey.currentNode.dsize;
+
+ if (currentNode.userData)
+ delete [] currentNode.userData;
+ if (currentNode.dsize) {
+ currentNode.userData = new char [ currentNode.dsize ];
+ memcpy(currentNode.userData, ikey.currentNode.userData, currentNode.dsize);
+ }
+ else currentNode.userData = 0;
+
+ bool newFiles = true;
+
+ if (path && ikey.path)
+ newFiles = strcmp(path, ikey.path);
+
+ if (newFiles) {
+ stdstr(&path, ikey.path);
+
+ if (idxfd) {
+ FileMgr::systemFileMgr.close(idxfd);
+ FileMgr::systemFileMgr.close(datfd);
+ }
+ idxfd = FileMgr::systemFileMgr.open(ikey.idxfd->path, ikey.idxfd->mode, ikey.idxfd->perms);
+ datfd = FileMgr::systemFileMgr.open(ikey.datfd->path, ikey.datfd->mode, ikey.datfd->perms);
+ }
+}
+
+
+void TreeKeyIdx::saveTreeNode(TreeNode *node) {
+ long datOffset = 0;
+ __s32 tmp;
+ if (idxfd > 0) {
+
+ lseek(idxfd->getFd(), node->offset, SEEK_SET);
+ datOffset = lseek(datfd->getFd(), 0, SEEK_END);
+ tmp = archtosword32(datOffset);
+ write(idxfd->getFd(), &tmp, 4);
+
+ saveTreeNodeOffsets(node);
+
+ write(datfd->getFd(), node->name, strlen(node->name));
+ char null = 0;
+ write(datfd->getFd(), &null, 1);
+
+ __u16 tmp2 = archtosword16(node->dsize);
+ write(datfd->getFd(), &tmp2, 2);
+
+ if (node->dsize) {
+ write(datfd->getFd(), node->userData, node->dsize);
+ }
+ }
+}
+
+
+void TreeKeyIdx::setText(const char *ikey) {
+ char *buf = 0;
+ stdstr(&buf, ikey);
+ char *leaf = strtok(buf, "/");
+ root();
+ while ((leaf) && (!Error())) {
+ bool ok, inChild = false;
+ for (ok = firstChild(); ok; ok = nextSibling()) {
+ inChild = true;
+ if (!stricmp(leaf, getLocalName()))
+ break;
+ }
+ leaf = strtok(0, "/");
+ if (!ok) {
+ if (inChild) { // if we didn't find a matching child node, default to first child
+ parent();
+ firstChild();
+ }
+ if (leaf)
+ error = KEYERR_OUTOFBOUNDS;
+ break;
+ }
+ }
+ delete [] buf;
+}
+
+
+
+void TreeKeyIdx::copyFrom(const SWKey &ikey) {
+ SWKey::copyFrom(ikey);
+}
+
+void TreeKeyIdx::setPosition(SW_POSITION p) {
+ switch (p) {
+ case POS_TOP:
+ root();
+ break;
+ case POS_BOTTOM:
+ error = getTreeNodeFromIdxOffset(lseek(idxfd->getFd(), -4, SEEK_END), &currentNode);
+ break;
+ }
+ Error(); // clear error from normalize
+}
+
+const char *TreeKeyIdx::getText() const {
+ return getFullName();
+}
+
+
+int TreeKeyIdx::_compare (const TreeKeyIdx & ikey) {
+ return (getOffset() - ikey.getOffset());
+}
+
+
+int TreeKeyIdx::compare(const SWKey &ikey) {
+ TreeKeyIdx *treeKey = SWDYNAMIC_CAST(TreeKeyIdx, (&ikey));
+ if (treeKey)
+ return _compare(*treeKey);
+ return SWKey::compare(ikey);
+}
+
+
+void TreeKeyIdx::decrement(int steps) {
+ error = getTreeNodeFromIdxOffset(currentNode.offset - (4*steps), &currentNode);
+}
+
+void TreeKeyIdx::increment(int steps) {
+ error = getTreeNodeFromIdxOffset(currentNode.offset + (4*steps), &currentNode);
+
+/*
+ // assert positive
+ if (steps < 0) {
+ decrement(steps * -1);
+ return;
+ }
+
+ while (steps > 0) {
+ if (!firstChild()) {
+ if (!nextSibbling() {
+ error = KEYERR_OUTOFBOUNDS;
+ return;
+ }
+ }
+ steps--;
+ }
+*/
+}
+
+
+
+TreeKeyIdx::TreeNode::TreeNode() {
+
+ name = 0;
+ stdstr(&name, "");
+ userData = 0;
+
+ clear();
+}
+
+
+void TreeKeyIdx::TreeNode::clear() {
+ offset = 0;
+ parent = -1;
+ next = -1;
+ firstChild = -1;
+ dsize = 0;
+
+ if (name)
+ delete [] name;
+ name = 0;
+ stdstr(&name, "");
+
+ if (userData)
+ delete [] userData;
+ userData = 0;
+}
+
+
+TreeKeyIdx::TreeNode::~TreeNode() {
+ if (name)
+ delete [] name;
+
+ if (userData)
+ delete [] userData;
+}
diff --git a/src/keys/versekey.cpp b/src/keys/versekey.cpp
new file mode 100644
index 0000000..05f1b8b
--- /dev/null
+++ b/src/keys/versekey.cpp
@@ -0,0 +1,1450 @@
+/******************************************************************************
+ * 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;
+ 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();
+ 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);
+}
+
+
+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;
+
+ stdstr(&abbr, iabbr);
+ strstrip(abbr);
+ 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;
+ }
+ 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();
+
+ 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();
+
+// 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
+
+ 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]);
+}
+
+
+/******************************************************************************
+ * 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++];
+}