/****************************************************************************** * versekey.h - code for class 'versekey'- a standard Biblical verse key * * $Id: treekeyidx.cpp 2147 2008-03-14 06:54:18Z scribe $ * * 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 #include #include #include #include #include #include #include SWORD_NAMESPACE_START 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() { SWBuf buf; init(); path = 0; stdstr(&path, idxPath); if (fileMode == -1) { // try read/write if possible fileMode = FileMgr::RDWR; } buf.setFormatted("%s.idx", path); idxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true); buf.setFormatted("%s.dat", path); datfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true); if (datfd <= 0) { SWLog::getSystemLog()->logError("%d", errno); error = errno; } else { root(); } } void TreeKeyIdx::init() { myclass = &classdef; } TreeKeyIdx::~TreeKeyIdx () { if (path) delete [] path; FileMgr::getSystemFileMgr()->close(idxfd); FileMgr::getSystemFileMgr()->close(datfd); } const char *TreeKeyIdx::getLocalName() { unsnappedKeyText = ""; return currentNode.name; } const char *TreeKeyIdx::getUserData(int *size) { unsnappedKeyText = ""; if (size) *size = (int)currentNode.dsize; return currentNode.userData; } void TreeKeyIdx::setUserData(const char *userData, int size) { // this makes sure any unsnapped path exists assureKeyPath(); 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) { unsnappedKeyText = ""; stdstr(&(currentNode.name), newName); return currentNode.name; } void TreeKeyIdx::save() { saveTreeNode(¤tNode); } void TreeKeyIdx::root() { error = getTreeNodeFromIdxOffset(0, ¤tNode); } bool TreeKeyIdx::parent() { if (currentNode.parent > -1) { error = getTreeNodeFromIdxOffset(currentNode.parent, ¤tNode); return true; } return false; } bool TreeKeyIdx::firstChild() { if (currentNode.firstChild > -1) { error = getTreeNodeFromIdxOffset(currentNode.firstChild, ¤tNode); return true; } return false; } bool TreeKeyIdx::nextSibling() { if (currentNode.next > -1) { error = getTreeNodeFromIdxOffset(currentNode.next, ¤tNode); return true; } return false; } bool TreeKeyIdx::previousSibling() { TreeNode iterator; __s32 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, ¤tNode); 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 = idxfd->seek(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 = idxfd->seek(0, SEEK_END); currentNode.firstChild = idxOffset; saveTreeNodeOffsets(¤tNode); __u32 parent = currentNode.offset; currentNode.clear(); currentNode.offset = idxOffset; currentNode.parent = parent; } } void TreeKeyIdx::insertBefore() { } void TreeKeyIdx::remove() { TreeNode node; bool done = false; if (currentNode.offset) { getTreeNodeFromIdxOffset(currentNode.offset, &node); if (node.parent > -1) { TreeNode parent; getTreeNodeFromIdxOffset(node.parent, &parent); if (parent.firstChild == node.offset) { parent.firstChild = node.next; saveTreeNodeOffsets(&parent); getTreeNodeFromIdxOffset(parent.offset, ¤tNode); done = true; } } if (!done) { TreeNode iterator; __s32 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) { TreeNode prev; getTreeNodeFromIdxOffset(iterator.offset, &prev); prev.next = node.next; saveTreeNodeOffsets(&prev); getTreeNodeFromIdxOffset(prev.offset, ¤tNode); } } } } } } /****************************************************************************** * 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); FileMgr::removeFile(buf); fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE); fd->getFd(); FileMgr::getSystemFileMgr()->close(fd); sprintf(buf, "%s.idx", path); FileMgr::removeFile(buf); fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE); fd2->getFd(); FileMgr::getSystemFileMgr()->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 { unsnappedKeyText = ""; char ch; __s32 tmp; __u16 tmp2; if (datfd > 0) { datfd->seek(ioffset, SEEK_SET); datfd->read(&tmp, 4); node->parent = swordtoarch32(tmp); datfd->read(&tmp, 4); node->next = swordtoarch32(tmp); datfd->read(&tmp, 4); node->firstChild = swordtoarch32(tmp); SWBuf name; do { datfd->read(&ch, 1); name += ch; } while (ch); stdstr(&(node->name), name.c_str()); datfd->read(&tmp2, 2); node->dsize = swordtoarch16(tmp2); if (node->dsize) { if (node->userData) delete [] node->userData; node->userData = new char [node->dsize]; datfd->read(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 { unsnappedKeyText = ""; __u32 offset; char error = KEYERR_OUTOFBOUNDS; if (ioffset < 0) { ioffset = 0; error = 77; // out of bounds but still position to 0; } node->offset = ioffset; if (idxfd > 0) { if (idxfd->getFd() > 0) { idxfd->seek(ioffset, SEEK_SET); if (idxfd->read(&offset, 4) == 4) { offset = swordtoarch32(offset); error = (error == 77) ? KEYERR_OUTOFBOUNDS : 0; getTreeNodeFromDatOffset(offset, node); } else { idxfd->seek(-4, SEEK_END); if (idxfd->read(&offset, 4) == 4) { offset = swordtoarch32(offset); getTreeNodeFromDatOffset(offset, node); } } } } return error; } unsigned long TreeKeyIdx::getOffset() const { unsnappedKeyText = ""; return currentNode.offset; } void TreeKeyIdx::setOffset(unsigned long offset) { error = getTreeNodeFromIdxOffset(offset, ¤tNode); } void TreeKeyIdx::saveTreeNodeOffsets(TreeNode *node) { unsnappedKeyText = ""; long datOffset = 0; __s32 tmp; if (idxfd > 0) { idxfd->seek(node->offset, SEEK_SET); if (idxfd->read(&tmp, 4) != 4) { datOffset = datfd->seek(0, SEEK_END); tmp = archtosword32(datOffset); idxfd->write(&tmp, 4); } else { datOffset = swordtoarch32(tmp); datfd->seek(datOffset, SEEK_SET); } tmp = archtosword32(node->parent); datfd->write(&tmp, 4); tmp = archtosword32(node->next); datfd->write(&tmp, 4); tmp = archtosword32(node->firstChild); datfd->write(&tmp, 4); } } void TreeKeyIdx::copyFrom(const TreeKeyIdx &ikey) { unsnappedKeyText = ""; 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::getSystemFileMgr()->close(idxfd); FileMgr::getSystemFileMgr()->close(datfd); } idxfd = FileMgr::getSystemFileMgr()->open(ikey.idxfd->path, ikey.idxfd->mode, ikey.idxfd->perms); datfd = FileMgr::getSystemFileMgr()->open(ikey.datfd->path, ikey.datfd->mode, ikey.datfd->perms); } } void TreeKeyIdx::saveTreeNode(TreeNode *node) { long datOffset = 0; __s32 tmp; if (idxfd > 0) { idxfd->seek(node->offset, SEEK_SET); datOffset = datfd->seek(0, SEEK_END); tmp = archtosword32(datOffset); idxfd->write(&tmp, 4); saveTreeNodeOffsets(node); datfd->write(node->name, strlen(node->name)); char null = 0; datfd->write(&null, 1); __u16 tmp2 = archtosword16(node->dsize); datfd->write(&tmp2, 2); if (node->dsize) { datfd->write(node->userData, node->dsize); } } } void TreeKeyIdx::setText(const char *ikey) { char *buf = 0; stdstr(&buf, ikey); SWBuf leaf = strtok(buf, "/"); leaf.trim(); root(); while ((leaf.size()) && (!Error())) { bool ok, inChild = false; error = KEYERR_OUTOFBOUNDS; for (ok = firstChild(); ok; ok = nextSibling()) { inChild = true; if (leaf == getLocalName()) { error = 0; break; } } leaf = strtok(0, "/"); leaf.trim(); if (!ok) { if (inChild) { // if we didn't find a matching child node, default to first child parent(); firstChild(); } error = KEYERR_OUTOFBOUNDS; } } if (leaf.size()) error = KEYERR_OUTOFBOUNDS; delete [] buf; unsnappedKeyText = ikey; } void TreeKeyIdx::copyFrom(const SWKey &ikey) { unsnappedKeyText = ikey; SWKey::copyFrom(ikey); } void TreeKeyIdx::setPosition(SW_POSITION p) { switch (p) { case POS_TOP: root(); break; case POS_BOTTOM: error = getTreeNodeFromIdxOffset(idxfd->seek(-4, SEEK_END), ¤tNode); break; } Error(); // clear error from normalize } 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), ¤tNode); } void TreeKeyIdx::increment(int steps) { error = getTreeNodeFromIdxOffset(currentNode.offset + (4*steps), ¤tNode); /* // assert positive if (steps < 0) { decrement(steps * -1); return; } while (steps > 0) { if (!firstChild()) { if (!nextSibbling() { error = KEYERR_OUTOFBOUNDS; return; } } steps--; } */ } const char *TreeKeyIdx::getText() const { TreeNode parent; static SWBuf fullPath; fullPath = currentNode.name; parent.parent = currentNode.parent; while (parent.parent > -1) { getTreeNodeFromIdxOffset(parent.parent, &parent); fullPath = ((SWBuf)parent.name) + (SWBuf) "/" + fullPath; } // we've snapped; clear our unsnapped text holder unsnappedKeyText = ""; return fullPath.c_str(); } 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; } SWKey *TreeKeyIdx::clone() const { return new TreeKeyIdx(*this); } SWORD_NAMESPACE_END