diff options
Diffstat (limited to 'src/mgr')
-rw-r--r-- | src/mgr/Makefile | 4 | ||||
-rw-r--r-- | src/mgr/Makefile.am | 33 | ||||
-rw-r--r-- | src/mgr/curlftpt.cpp | 159 | ||||
-rw-r--r-- | src/mgr/encfiltmgr.cpp | 154 | ||||
-rw-r--r-- | src/mgr/filemgr.cpp | 566 | ||||
-rw-r--r-- | src/mgr/ftplibftpt.cpp | 108 | ||||
-rw-r--r-- | src/mgr/ftptrans.cpp | 170 | ||||
-rw-r--r-- | src/mgr/installmgr.cpp | 579 | ||||
-rw-r--r-- | src/mgr/localemgr.cpp | 257 | ||||
-rw-r--r-- | src/mgr/markupfiltmgr.cpp | 295 | ||||
-rw-r--r-- | src/mgr/stringmgr.cpp | 280 | ||||
-rw-r--r-- | src/mgr/swcacher.cpp | 47 | ||||
-rw-r--r-- | src/mgr/swconfig.cpp | 155 | ||||
-rw-r--r-- | src/mgr/swfiltermgr.cpp | 93 | ||||
-rw-r--r-- | src/mgr/swlocale.cpp | 204 | ||||
-rw-r--r-- | src/mgr/swmgr.cpp | 1275 | ||||
-rw-r--r-- | src/mgr/swsearchable.cpp | 53 |
17 files changed, 4432 insertions, 0 deletions
diff --git a/src/mgr/Makefile b/src/mgr/Makefile new file mode 100644 index 0000000..339f87a --- /dev/null +++ b/src/mgr/Makefile @@ -0,0 +1,4 @@ +root := ../.. + +all: + make -C ${root} diff --git a/src/mgr/Makefile.am b/src/mgr/Makefile.am new file mode 100644 index 0000000..1b1e33b --- /dev/null +++ b/src/mgr/Makefile.am @@ -0,0 +1,33 @@ +mgrdir = $(top_srcdir)/src/mgr + +if CONFDEF +globdef = -DGLOBCONFPATH=\"${globalconfdir}/sword.conf\" +else +globdef = +endif + +AM_CPPFLAGS += $(globdef) +AM_CPPFLAGS += -D_FTPLIB_NO_COMPAT + +if WITHCURL +FTP_SOURCES = $(mgrdir)/curlftpt.cpp +else +FTP_SOURCES = $(mgrdir)/ftplibftpt.cpp +endif + +libsword_la_SOURCES += $(FTP_SOURCES) +libsword_la_SOURCES += $(mgrdir)/swconfig.cpp +libsword_la_SOURCES += $(mgrdir)/swmgr.cpp +libsword_la_SOURCES += $(mgrdir)/swfiltermgr.cpp +libsword_la_SOURCES += $(mgrdir)/encfiltmgr.cpp +libsword_la_SOURCES += $(mgrdir)/markupfiltmgr.cpp +libsword_la_SOURCES += $(mgrdir)/filemgr.cpp +libsword_la_SOURCES += $(mgrdir)/ftptrans.cpp +libsword_la_SOURCES += $(mgrdir)/swlocale.cpp +libsword_la_SOURCES += $(mgrdir)/localemgr.cpp +libsword_la_SOURCES += $(mgrdir)/swcacher.cpp +libsword_la_SOURCES += $(mgrdir)/swsearchable.cpp +libsword_la_SOURCES += $(mgrdir)/installmgr.cpp +libsword_la_SOURCES += $(mgrdir)/stringmgr.cpp + + diff --git a/src/mgr/curlftpt.cpp b/src/mgr/curlftpt.cpp new file mode 100644 index 0000000..bb47958 --- /dev/null +++ b/src/mgr/curlftpt.cpp @@ -0,0 +1,159 @@ + /***************************************************************************** + * CURLFTPTransport functions + * + */ + + +#include <curlftpt.h> + +#include <fcntl.h> + +#include <curl/curl.h> +#include <curl/types.h> +#include <curl/easy.h> + +#include <swlog.h> + +SWORD_NAMESPACE_START + + +struct FtpFile { + const char *filename; + FILE *stream; + SWBuf *destBuf; +}; + + +int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream); +int my_fprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); + +static CURLFTPTransport_init _CURLFTPTransport_init; + +CURLFTPTransport_init::CURLFTPTransport_init() { + //curl_global_init(CURL_GLOBAL_DEFAULT); // curl_easy_init automatically calls it if needed +} + +CURLFTPTransport_init::~CURLFTPTransport_init() { + curl_global_cleanup(); +} + +int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) { + struct FtpFile *out=(struct FtpFile *)stream; + if (out && !out->stream && !out->destBuf) { + /* open file for writing */ + out->stream=fopen(out->filename, "wb"); + if (!out->stream) + return -1; /* failure, can't open file to write */ + } + if (out->destBuf) { + int s = out->destBuf->size(); + out->destBuf->size(s+(size*nmemb)); + memcpy(out->destBuf->getRawData()+s, buffer, size*nmemb); + return nmemb; + } + return fwrite(buffer, size, nmemb, out->stream); +} + + +int my_fprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { + if (clientp) { + ((StatusReporter *)clientp)->statusUpdate(dltotal, dlnow); + } + return 0; +} + + +static int my_trace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, void *userp) { + SWBuf header; + (void)userp; /* prevent compiler warning */ + (void)handle; /* prevent compiler warning */ + + switch (type) { + case CURLINFO_TEXT: header = "TEXT"; break; + case CURLINFO_HEADER_OUT: header = "=> Send header"; break; + case CURLINFO_HEADER_IN: header = "<= Recv header"; break; + + // these we don't want to log (HUGE) + case CURLINFO_DATA_OUT: header = "=> Send data"; + case CURLINFO_SSL_DATA_OUT: header = "=> Send SSL data"; + case CURLINFO_DATA_IN: header = "<= Recv data"; + case CURLINFO_SSL_DATA_IN: header = "<= Recv SSL data"; + default: /* in case a new one is introduced to shock us */ + return 0; + } + + if (size > 120) size = 120; + SWBuf text; + text.size(size); + memcpy(text.getRawData(), data, size); + SWLog::getSystemLog()->logDebug("CURLFTPTransport: %s: %s", header.c_str(), text.c_str()); + return 0; +} + +CURLFTPTransport::CURLFTPTransport(const char *host, StatusReporter *sr) : FTPTransport(host, sr) { + session = (CURL *)curl_easy_init(); +} + + +CURLFTPTransport::~CURLFTPTransport() { + curl_easy_cleanup(session); +} + + +char CURLFTPTransport::getURL(const char *destPath, const char *sourceURL, SWBuf *destBuf) { + signed char retVal = 0; + struct FtpFile ftpfile = {destPath, 0, destBuf}; + + CURLcode res; + + if (session) { + curl_easy_setopt(session, CURLOPT_URL, sourceURL); + + curl_easy_setopt(session, CURLOPT_USERPWD, "ftp:installmgr@user.com"); + curl_easy_setopt(session, CURLOPT_WRITEFUNCTION, my_fwrite); + if (!passive) + curl_easy_setopt(session, CURLOPT_FTPPORT, "-"); + curl_easy_setopt(session, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(session, CURLOPT_PROGRESSDATA, statusReporter); + curl_easy_setopt(session, CURLOPT_PROGRESSFUNCTION, my_fprogress); + curl_easy_setopt(session, CURLOPT_DEBUGFUNCTION, my_trace); + /* Set a pointer to our struct to pass to the callback */ + curl_easy_setopt(session, CURLOPT_FILE, &ftpfile); + + /* Switch on full protocol/debug output */ + curl_easy_setopt(session, CURLOPT_VERBOSE, true); + + /* FTP connection settings */ + +#if (LIBCURL_VERSION_MAJOR > 7) || \ + ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR > 10)) || \ + ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 10) && (LIBCURL_VERSION_PATCH >= 5)) +# define EPRT_AVAILABLE 1 +#endif + +#ifdef EPRT_AVAILABLE + curl_easy_setopt(session, CURLOPT_FTP_USE_EPRT, 0); + SWLog::getSystemLog()->logDebug("***** using CURLOPT_FTP_USE_EPRT\n"); +#endif + + + SWLog::getSystemLog()->logDebug("***** About to perform curl easy action. \n"); + SWLog::getSystemLog()->logDebug("***** destPath: %s \n", destPath); + SWLog::getSystemLog()->logDebug("***** sourceURL: %s \n", sourceURL); + res = curl_easy_perform(session); + SWLog::getSystemLog()->logDebug("***** Finished performing curl easy action. \n"); + + if(CURLE_OK != res) { + retVal = -1; + } + } + + if (ftpfile.stream) + fclose(ftpfile.stream); /* close the local file */ + + return retVal; +} + + +SWORD_NAMESPACE_END + diff --git a/src/mgr/encfiltmgr.cpp b/src/mgr/encfiltmgr.cpp new file mode 100644 index 0000000..970900c --- /dev/null +++ b/src/mgr/encfiltmgr.cpp @@ -0,0 +1,154 @@ +/****************************************************************************** + * swencodingmgr.cpp - implementaion of class EncodingFilterMgr, subclass of + * used to transcode all module text to a requested + * encoding. + * + * 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 <encfiltmgr.h> +#include <utilstr.h> + +#include <scsuutf8.h> +#include <latin1utf8.h> + +#include <unicodertf.h> +#include <utf8latin1.h> +#include <utf8utf16.h> +#include <utf8html.h> +#include <swmodule.h> + +#include <swmgr.h> + +SWORD_NAMESPACE_START + +/****************************************************************************** + * EncodingFilterMgr Constructor - initializes instance of EncodingFilterMgr + * + * ENT: + * enc - Encoding format to emit + */ + +EncodingFilterMgr::EncodingFilterMgr (char enc) + : SWFilterMgr() { + + scsuutf8 = new SCSUUTF8(); + latin1utf8 = new Latin1UTF8(); + + encoding = enc; + + switch (encoding) { + case ENC_LATIN1: + targetenc = new UTF8Latin1(); + break; + case ENC_UTF16: + targetenc = new UTF8UTF16(); + break; + case ENC_RTF: + targetenc = new UnicodeRTF(); + break; + case ENC_HTML: + targetenc = new UTF8HTML(); + break; + default: // i.e. case ENC_UTF8 + targetenc = NULL; + } +} + +/****************************************************************************** + * EncodingFilterMgr Destructor - Cleans up instance of EncodingFilterMgr + */ +EncodingFilterMgr::~EncodingFilterMgr() { + if (scsuutf8) + delete scsuutf8; + if (latin1utf8) + delete latin1utf8; + if (targetenc) + delete targetenc; +} + +void EncodingFilterMgr::AddRawFilters(SWModule *module, ConfigEntMap §ion) { + + ConfigEntMap::iterator entry; + + SWBuf encoding = ((entry = section.find("Encoding")) != section.end()) ? (*entry).second : (SWBuf)""; + if (!encoding.length() || !stricmp(encoding.c_str(), "Latin-1")) { + module->AddRawFilter(latin1utf8); + } + else if (!stricmp(encoding.c_str(), "SCSU")) { + module->AddRawFilter(scsuutf8); + } +} + +void EncodingFilterMgr::AddEncodingFilters(SWModule *module, ConfigEntMap §ion) { + if (targetenc) + module->AddEncodingFilter(targetenc); +} + +/****************************************************************************** + * EncodingFilterMgr::Encoding - sets/gets encoding + * + * ENT: enc - new encoding or 0 to simply get the current encoding + * + * RET: encoding + */ +char EncodingFilterMgr::Encoding(char enc) { + if (enc && enc != encoding) { + encoding = enc; + SWFilter * oldfilter = targetenc; + + switch (encoding) { + case ENC_LATIN1: + targetenc = new UTF8Latin1(); + break; + case ENC_UTF16: + targetenc = new UTF8UTF16(); + break; + case ENC_RTF: + targetenc = new UnicodeRTF(); + break; + case ENC_HTML: + targetenc = new UTF8HTML(); + break; + default: // i.e. case ENC_UTF8 + targetenc = NULL; + } + + ModMap::const_iterator module; + + if (oldfilter != targetenc) { + if (oldfilter) { + if (!targetenc) { + for (module = getParentMgr()->Modules.begin(); module != getParentMgr()->Modules.end(); module++) + module->second->RemoveRenderFilter(oldfilter); + } + else { + for (module = getParentMgr()->Modules.begin(); module != getParentMgr()->Modules.end(); module++) + module->second->ReplaceRenderFilter(oldfilter, targetenc); + } + delete oldfilter; + } + else if (targetenc) { + for (module = getParentMgr()->Modules.begin(); module != getParentMgr()->Modules.end(); module++) + module->second->AddRenderFilter(targetenc); + } + } + + } + return encoding; +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/filemgr.cpp b/src/mgr/filemgr.cpp new file mode 100644 index 0000000..4a91dfa --- /dev/null +++ b/src/mgr/filemgr.cpp @@ -0,0 +1,566 @@ +/****************************************************************************** + * filemgr.cpp - implementation of class FileMgr used for pooling file + * handles + * + * $Id: filemgr.cpp 2108 2007-10-13 20:35:02Z 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 <filemgr.h> +#include <utilstr.h> + +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <swbuf.h> +#if !defined(__GNUC__) && !defined(_WIN32_WCE) +#include <io.h> +#include <direct.h> +#else +#include <unistd.h> +#endif + + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef S_IRGRP +#define S_IRGRP 0 +#endif + +#ifndef S_IROTH +#define S_IROTH 0 +#endif + +// Fix for VC6 +#ifndef S_IREAD +#ifdef _S_IREAD +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE +#endif +#endif +// ----------- + + +SWORD_NAMESPACE_START + + +int FileMgr::CREAT = O_CREAT; +int FileMgr::APPEND = O_APPEND; +int FileMgr::TRUNC = O_TRUNC; +int FileMgr::RDONLY = O_RDONLY; +int FileMgr::RDWR = O_RDWR; +int FileMgr::WRONLY = O_WRONLY; +int FileMgr::IREAD = S_IREAD; +int FileMgr::IWRITE = S_IWRITE; + + +// ---------------- statics ----------------- +FileMgr *FileMgr::systemFileMgr = 0; + +class __staticsystemFileMgr { +public: + __staticsystemFileMgr() { } + ~__staticsystemFileMgr() { delete FileMgr::systemFileMgr; } +} _staticsystemFileMgr; + + +FileMgr *FileMgr::getSystemFileMgr() { + if (!systemFileMgr) + systemFileMgr = new FileMgr(); + + return systemFileMgr; +} + + +void FileMgr::setSystemFileMgr(FileMgr *newFileMgr) { + if (systemFileMgr) + delete systemFileMgr; + systemFileMgr = newFileMgr; +} + +// --------------- end statics -------------- + + +FileDesc::FileDesc(FileMgr *parent, const char *path, int mode, int perms, bool tryDowngrade) { + this->parent = parent; + this->path = 0; + stdstr(&this->path, path); + this->mode = mode; + this->perms = perms; + this->tryDowngrade = tryDowngrade; + offset = 0; + fd = -77; +} + + +FileDesc::~FileDesc() { + if (fd > 0) + close(fd); + + if (path) + delete [] path; +} + + +int FileDesc::getFd() { + if (fd == -77) + fd = parent->sysOpen(this); +// if ((fd < -1) && (fd != -77)) // kludge to hand ce +// return 777; + return fd; +} + + +long FileDesc::seek(long offset, int whence) { + return lseek(getFd(), offset, whence); +} + + +long FileDesc::read(void *buf, long count) { + return ::read(getFd(), buf, count); +} + + +long FileDesc::write(const void *buf, long count) { + return ::write(getFd(), buf, count); +} + + +FileMgr::FileMgr(int maxFiles) { + this->maxFiles = maxFiles; // must be at least 2 + files = 0; +} + + +FileMgr::~FileMgr() { + FileDesc *tmp; + + while(files) { + tmp = files->next; + delete files; + files = tmp; + } +} + + +FileDesc *FileMgr::open(const char *path, int mode, bool tryDowngrade) { + return open(path, mode, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH, tryDowngrade); +} + + +FileDesc *FileMgr::open(const char *path, int mode, int perms, bool tryDowngrade) { + FileDesc **tmp, *tmp2; + + for (tmp = &files; *tmp; tmp = &((*tmp)->next)) { + if ((*tmp)->fd < 0) // insert as first non-system_open file + break; + } + + tmp2 = new FileDesc(this, path, mode, perms, tryDowngrade); + tmp2->next = *tmp; + *tmp = tmp2; + + return tmp2; +} + + +void FileMgr::close(FileDesc *file) { + FileDesc **loop; + + for (loop = &files; *loop; loop = &((*loop)->next)) { + if (*loop == file) { + *loop = (*loop)->next; + delete file; + break; + } + } +} + + +int FileMgr::sysOpen(FileDesc *file) { + FileDesc **loop; + int openCount = 1; // because we are presently opening 1 file, and we need to be sure to close files to accomodate, if necessary + + for (loop = &files; *loop; loop = &((*loop)->next)) { + + if ((*loop)->fd > 0) { + if (++openCount > maxFiles) { + (*loop)->offset = lseek((*loop)->fd, 0, SEEK_CUR); + ::close((*loop)->fd); + (*loop)->fd = -77; + } + } + + if (*loop == file) { + if (*loop != files) { + *loop = (*loop)->next; + file->next = files; + files = file; + } + if ((!access(file->path, 04)) || ((file->mode & O_CREAT) == O_CREAT)) { // check for at least file exists / read access before we try to open + char tries = (((file->mode & O_RDWR) == O_RDWR) && (file->tryDowngrade)) ? 2 : 1; // try read/write if possible + for (int i = 0; i < tries; i++) { + if (i > 0) { + file->mode = (file->mode & ~O_RDWR); // remove write access + file->mode = (file->mode | O_RDONLY);// add read access + } + file->fd = ::open(file->path, file->mode|O_BINARY, file->perms); + + if (file->fd >= 0) + break; + } + + if (file->fd >= 0) + lseek(file->fd, file->offset, SEEK_SET); + } + else file->fd = -1; + if (!*loop) + break; + } + } + return file->fd; +} + + +// to truncate a file at its current position +// leaving byte at current possition intact +// deleting everything afterward. +signed char FileMgr::trunc(FileDesc *file) { + + static const char *writeTest = "x"; + long size = file->seek(1, SEEK_CUR); + if (size == 1) // was empty + size = 0; + char nibble [ 32767 ]; + bool writable = file->write(writeTest, 1); + int bytes = 0; + + if (writable) { + // get tmpfilename + char *buf = new char [ strlen(file->path) + 10 ]; + int i; + for (i = 0; i < 9999; i++) { + sprintf(buf, "%stmp%.4d", file->path, i); + if (!existsFile(buf)) + break; + } + if (i == 9999) + return -2; + + int fd = ::open(buf, O_CREAT|O_RDWR, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); + if (fd < 0) + return -3; + + file->seek(0, SEEK_SET); + while (size > 0) { + bytes = file->read(nibble, 32767); + write(fd, nibble, (bytes < size)?bytes:size); + size -= bytes; + } + // zero out the file + ::close(file->fd); + file->fd = ::open(file->path, O_TRUNC, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); + ::close(file->fd); + file->fd = -77; // force file open by filemgr + // copy tmp file back (dumb, but must preserve file permissions) + lseek(fd, 0, SEEK_SET); + do { + bytes = read(fd, nibble, 32767); + file->write(nibble, bytes); + } while (bytes == 32767); + + ::close(fd); + ::close(file->fd); + removeFile(buf); // remove our tmp file + file->fd = -77; // causes file to be swapped out forcing open on next call to getFd() + } + else { // put offset back and return failure + file->seek(-1, SEEK_CUR); + return -1; + } + return 0; +} + + +signed char FileMgr::existsFile(const char *ipath, const char *ifileName) +{ + int len = strlen(ipath) + ((ifileName)?strlen(ifileName):0) + 3; + char *ch; + char *path = new char [ len ]; + strcpy(path, ipath); + + if ((path[strlen(path)-1] == '\\') || (path[strlen(path)-1] == '/')) + path[strlen(path)-1] = 0; + + if (ifileName) { + ch = path + strlen(path); + sprintf(ch, "/%s", ifileName); + } + signed char retVal = !access(path, 04); + delete [] path; + return retVal; +} + + +signed char FileMgr::existsDir(const char *ipath, const char *idirName) +{ + char *ch; + int len = strlen(ipath) + ((idirName)?strlen(idirName):0) + 1; + if (idirName) + len += strlen(idirName); + char *path = new char [ len ]; + strcpy(path, ipath); + + if ((path[strlen(path)-1] == '\\') || (path[strlen(path)-1] == '/')) + path[strlen(path)-1] = 0; + + if (idirName) { + ch = path + strlen(path); + sprintf(ch, "/%s", idirName); + } + signed char retVal = !access(path, 04); + delete [] path; + return retVal; +} + + +int FileMgr::createParent(const char *pName) { + char *buf = new char [ strlen(pName) + 1 ]; + int retCode = 0; + + strcpy(buf, pName); + int end = strlen(buf) - 1; + while (end) { + if ((buf[end] == '/') || (buf[end] == '\\')) + break; + end--; + } + buf[end] = 0; + if (strlen(buf)>0) { + if (access(buf, 02)) { // not exists with write access? + if ((retCode = mkdir(buf +#ifndef WIN32 + , 0755 +#endif + ))) { + createParent(buf); + retCode = mkdir(buf +#ifndef WIN32 + , 0755 +#endif + ); + } + } + } + else retCode = -1; + delete [] buf; + return retCode; +} + + +int FileMgr::openFileReadOnly(const char *fName) { + int fd = ::open(fName, O_RDONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); + return fd; +} + + +int FileMgr::createPathAndFile(const char *fName) { + int fd; + + fd = ::open(fName, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); + if (fd < 1) { + createParent(fName); + fd = ::open(fName, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); + } + return fd; +} + + +int FileMgr::copyFile(const char *sourceFile, const char *targetFile) { + int sfd, dfd, len; + char buf[4096]; + + if ((sfd = ::open(sourceFile, O_RDONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH)) < 1) + return -1; + if ((dfd = createPathAndFile(targetFile)) < 1) + return -1; + + do { + len = read(sfd, buf, 4096); + write(dfd, buf, len); + } + while(len == 4096); + ::close(dfd); + ::close(sfd); + + return 0; +} + + +int FileMgr::removeFile(const char *fName) { + return ::remove(fName); +} + +char FileMgr::getLine(FileDesc *fDesc, SWBuf &line) { + int len; + bool more = true; + char chunk[255]; + + line = ""; + + // assert we have a valid file handle + if (fDesc->getFd() < 1) + return 0; + + while (more) { + more = false; + long index = fDesc->seek(0, SEEK_CUR); + len = fDesc->read(chunk, 254); + + // assert we have a readable file (not a directory) + if (len < 1) + break; + + int start = 0; + // clean up any preceding white space if we're at the beginning of line + if (!line.length()) { + for (;start < len; start++) { + if ((chunk[start] != 13) && (chunk[start] != ' ') && (chunk[start] != '\t')) + break; + } + } + + // find the end + int end; + for (end = start; ((end < (len-1)) && (chunk[end] != 10)); end++); + + if ((chunk[end] != 10) && (len == 254)) { + more = true; + } + index += (end + 1); + + // reposition to next valid place to read + fDesc->seek(index, SEEK_SET); + + // clean up any trailing junk on line if we're at the end + if (!more) { + for (; end > start; end--) { + if ((chunk[end] != 10) && (chunk[end] != 13) && (chunk[end] != ' ') && (chunk[end] != '\t')) { + if (chunk[end] == '\\') { + more = true; + end--; + } + break; + } + } + } + + int size = (end - start) + 1; + + if (size > 0) { + // line.appendFormatted("%.*s", size, chunk+start); + line.append(chunk+start, size); + } + } + return ((len>0) || line.length()); +} + + +char FileMgr::isDirectory(const char *path) { + struct stat stats; + if (stat(path, &stats)) + return 0; + return ((stats.st_mode & S_IFDIR) == S_IFDIR); +} + + +int FileMgr::copyDir(const char *srcDir, const char *destDir) { + DIR *dir; + struct dirent *ent; + if ((dir = opendir(srcDir))) { + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + SWBuf srcPath = (SWBuf)srcDir + (SWBuf)"/" + ent->d_name; + SWBuf destPath = (SWBuf)destDir + (SWBuf)"/" + ent->d_name; + if (!isDirectory(srcPath.c_str())) { + copyFile(srcPath.c_str(), destPath.c_str()); + } + else { + copyDir(srcPath.c_str(), destPath.c_str()); + } + } + } + closedir(dir); + } + return 0; +} + + +int FileMgr::removeDir(const char *targetDir) { + DIR *dir = opendir(targetDir); + struct dirent *ent; + if (dir) { + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + SWBuf targetPath = (SWBuf)targetDir + (SWBuf)"/" + ent->d_name; + if (!isDirectory(targetPath.c_str())) { + FileMgr::removeFile(targetPath.c_str()); + } + else { + removeDir(targetPath.c_str()); + } + } + } + closedir(dir); + removeFile(targetDir); + } + return 0; +} + + +void FileMgr::flush() { + FileDesc **loop; + + for (loop = &files; *loop; loop = &((*loop)->next)) { + if ((*loop)->fd > 0) { + (*loop)->offset = lseek((*loop)->fd, 0, SEEK_CUR); + ::close((*loop)->fd); + (*loop)->fd = -77; + } + } +} + +long FileMgr::resourceConsumption() { + long count = 0; + FileDesc **loop; + for (loop = &files; *loop; loop = &((*loop)->next)) { + if ((*loop)->fd > 0) { + count++; + } + } + return count; +} + + +SWORD_NAMESPACE_END diff --git a/src/mgr/ftplibftpt.cpp b/src/mgr/ftplibftpt.cpp new file mode 100644 index 0000000..4921dd5 --- /dev/null +++ b/src/mgr/ftplibftpt.cpp @@ -0,0 +1,108 @@ + /***************************************************************************** + * FTPLibFTPTransport functions + * + */ + +#include <stdio.h> +#include <fcntl.h> + +#include <ftplib.h> + +#include <ftplibftpt.h> +#include <swlog.h> +#include <filemgr.h> + + +SWORD_NAMESPACE_START + + +static FTPLibFTPTransport_init _FTPLibFTPTransport_init; + +FTPLibFTPTransport_init::FTPLibFTPTransport_init() { + FtpInit(); +} + +FTPLibFTPTransport_init::~FTPLibFTPTransport_init() { +} + + +FTPLibFTPTransport::FTPLibFTPTransport(const char *host, StatusReporter *sr) : FTPTransport(host, sr) { + + ftpConnection = 0; +} + + +FTPLibFTPTransport::~FTPLibFTPTransport() { + if (ftpConnection) + FtpQuit(ftpConnection); +} + + +char FTPLibFTPTransport::assureLoggedIn() { + char retVal = 0; + if (ftpConnection == 0) { + SWLog::getSystemLog()->logDebug("connecting to host %s\n", host.c_str()); + if (FtpConnect(host, &ftpConnection)) + if (FtpLogin("anonymous", "installmgr@user.com", ftpConnection)) { + retVal = 0; + } + else { + SWLog::getSystemLog()->logError("Failed to login to %s\n", host.c_str()); + retVal = -2; + } + else { + SWLog::getSystemLog()->logError("Failed to connect to %s\n", host.c_str()); + retVal = -1; + } + } + return retVal; +} + + +char FTPLibFTPTransport::getURL(const char *destPath, const char *sourceURL, SWBuf *destBuf) { + + char retVal = 0; + + // assert we can login + retVal = assureLoggedIn(); + if (retVal) return retVal; + + SWBuf sourcePath = sourceURL; + SWBuf outFile = (!destBuf) ? destPath : "swftplib.tmp"; + sourcePath << (6 + host.length()); // shift << "ftp://hostname"; + SWLog::getSystemLog()->logDebug("getting file %s to %s\n", sourcePath.c_str(), outFile.c_str()); + if (passive) + FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE, ftpConnection); + else + FtpOptions(FTPLIB_CONNMODE, FTPLIB_PORT, ftpConnection); + // !!!WDG also want to set callback options + if (sourcePath.endsWith("/") || sourcePath.endsWith("\\")) { + SWLog::getSystemLog()->logDebug("getting test directory %s\n", sourcePath.c_str()); + FtpDir(NULL, sourcePath, ftpConnection); + SWLog::getSystemLog()->logDebug("getting real directory %s\n", sourcePath.c_str()); + retVal = FtpDir(outFile.c_str(), sourcePath, ftpConnection) - 1; + } + else { + SWLog::getSystemLog()->logDebug("getting file %s\n", sourcePath.c_str()); + retVal = FtpGet(outFile.c_str(), sourcePath, FTPLIB_IMAGE, ftpConnection) - 1; + } + + // Is there a way to FTPGet directly to a buffer? + // If not, we probably want to add x-platform way to open a tmp file with FileMgr + // This wreaks and will easily fail if a user's CWD is not writable. + if (destBuf) { + FileDesc *fd = FileMgr::getSystemFileMgr()->open("swftplib.tmp", FileMgr::RDONLY); + long size = fd->seek(0, SEEK_END); + fd->seek(0, SEEK_SET); + destBuf->size(size); + fd->read(destBuf->getRawData(), size); + FileMgr::getSystemFileMgr()->close(fd); + FileMgr::removeFile("swftplib.tmp"); + } + + return retVal; +} + + +SWORD_NAMESPACE_END + diff --git a/src/mgr/ftptrans.cpp b/src/mgr/ftptrans.cpp new file mode 100644 index 0000000..ab0a605 --- /dev/null +++ b/src/mgr/ftptrans.cpp @@ -0,0 +1,170 @@ + /***************************************************************************** + * FTPTransport functions + * + */ + + +#include <ftptrans.h> +#include <filemgr.h> + +#include <fcntl.h> +#include <dirent.h> +#include <swlog.h> + + +using std::vector; + + +SWORD_NAMESPACE_START + + +namespace { + +void removeTrailingSlash(SWBuf &buf) { + int len = buf.size(); + if ((buf[len-1] == '/') + || (buf[len-1] == '\\')) + buf.size(len-1); +} + +}; + + +void StatusReporter::preStatus(long totalBytes, long completedBytes, const char *message) { +} + +void StatusReporter::statusUpdate(double dtTotal, double dlNow) { +} + + +FTPTransport::FTPTransport(const char *host, StatusReporter *statusReporter) { + this->statusReporter = statusReporter; + this->host = host; + term = false; +} + + +FTPTransport::~FTPTransport() { +} + + +// override this method in your real transport class +char FTPTransport::getURL(const char *destPath, const char *sourceURL, SWBuf *destBuf) { + char retVal = 0; + return retVal; +} + + +vector<struct DirEntry> FTPTransport::getDirList(const char *dirURL) { + + vector<struct DirEntry> dirList; + + SWBuf dirBuf; + if (!getURL("", dirURL, &dirBuf)) { + char *start = dirBuf.getRawData(); + char *end = start; + while (start < (dirBuf.getRawData()+dirBuf.size())) { + struct ftpparse item; + bool looking = true; + for (end = start; *end; end++) { + if (looking) { + if ((*end == 10) || (*end == 13)) { + *end = 0; + looking = false; + } + } + else if ((*end != 10) && (*end != 13)) + break; + } + SWLog::getSystemLog()->logWarning("FTPURLGetDir: parsing item %s(%d)\n", start, end-start); + int status = ftpparse(&item, start, end - start); + SWLog::getSystemLog()->logWarning("FTPURLGetDir: got item %s\n", item.name); + if (status) { + struct DirEntry i; + i.name = item.name; + i.size = item.size; + i.isDirectory = (item.flagtrycwd == 1); + dirList.push_back(i); + } + start = end; + } + } + else + { + SWLog::getSystemLog()->logWarning("FTPURLGetDir: failed to get dir %s\n", dirURL); + } + return dirList; +} + + +int FTPTransport::copyDirectory(const char *urlPrefix, const char *dir, const char *dest, const char *suffix) { + unsigned int i; + int retVal = 0; + + SWBuf url = SWBuf(urlPrefix) + SWBuf(dir); + removeTrailingSlash(url); + url += '/'; + + SWLog::getSystemLog()->logWarning("FTPCopy: getting dir %s\n", url.c_str()); + vector<struct DirEntry> dirList = getDirList(url.c_str()); + + if (!dirList.size()) { + SWLog::getSystemLog()->logWarning("FTPCopy: failed to read dir %s\n", url.c_str()); + return -1; + } + + long totalBytes = 0; + for (i = 0; i < dirList.size(); i++) + totalBytes += dirList[i].size; + long completedBytes = 0; + for (i = 0; i < dirList.size(); i++) { + struct DirEntry &dirEntry = dirList[i]; + SWBuf buffer = (SWBuf)dest; + removeTrailingSlash(buffer); + buffer += "/"; + buffer += dirEntry.name; + if (!strcmp(&buffer.c_str()[buffer.length()-strlen(suffix)], suffix)) { + SWBuf buffer2 = "Downloading ("; + buffer2.appendFormatted("%d", i+1); + buffer2 += " of "; + buffer2.appendFormatted("%d", dirList.size()); + buffer2 += "): "; + buffer2 += dirEntry.name; + if (statusReporter) + statusReporter->preStatus(totalBytes, completedBytes, buffer2.c_str()); + FileMgr::createParent(buffer.c_str()); // make sure parent directory exists + SWTRY { + SWBuf url = (SWBuf)urlPrefix + (SWBuf)dir; + removeTrailingSlash(url); + url += "/"; + url += dirEntry.name; //dont forget the final slash + if (!dirEntry.isDirectory) { + if (getURL(buffer.c_str(), url.c_str())) { + SWLog::getSystemLog()->logWarning("FTPCopy: failed to get file %s\n", url.c_str()); + return -2; + } + completedBytes += dirEntry.size; + } + else { + SWBuf subdir = (SWBuf)dir; + removeTrailingSlash(subdir); + subdir += (SWBuf)"/" + dirEntry.name; + if (copyDirectory(urlPrefix, subdir, buffer.c_str(), suffix)) { + SWLog::getSystemLog()->logWarning("FTPCopy: failed to get file %s\n", subdir.c_str()); + return -2; + } + } + } + SWCATCH (...) {} + if (term) { + retVal = -3; + break; + } + } + } + return retVal; +} + + +SWORD_NAMESPACE_END + diff --git a/src/mgr/installmgr.cpp b/src/mgr/installmgr.cpp new file mode 100644 index 0000000..6a1704f --- /dev/null +++ b/src/mgr/installmgr.cpp @@ -0,0 +1,579 @@ + /***************************************************************************** + * InstallMgr functions to be made into something usefully exposed by + * master Glassey + * + */ + + +#ifndef EXCLUDEZLIB +extern "C" { +#include <untgz.h> +} +#endif + +#include <installmgr.h> +#include <filemgr.h> +#include <utilstr.h> + +#include <fcntl.h> + +#include <swmgr.h> +#include <swmodule.h> +#include <swversion.h> +#include <swlog.h> +#include <dirent.h> + +#include <stdio.h> +#include <map> + +#ifdef CURLAVAILABLE +#include <curlftpt.h> +#else +#include <ftplibftpt.h> +#endif + +SWORD_NAMESPACE_START + +namespace { + +void removeTrailingSlash(SWBuf &buf) { + int len = buf.size(); + if ((buf[len-1] == '/') + || (buf[len-1] == '\\')) + buf.size(len-1); +} + +}; + + +using std::map; + +const int InstallMgr::MODSTAT_OLDER = 0x001; +const int InstallMgr::MODSTAT_SAMEVERSION = 0x002; +const int InstallMgr::MODSTAT_UPDATED = 0x004; +const int InstallMgr::MODSTAT_NEW = 0x008; +const int InstallMgr::MODSTAT_CIPHERED = 0x010; +const int InstallMgr::MODSTAT_CIPHERKEYPRESENT = 0x020; + +// override this method and provide your own custom FTPTransport subclass +// here we try a couple defaults if sword was compiled with support for them. +// see these classes for examples of how to make your own +FTPTransport *InstallMgr::createFTPTransport(const char *host, StatusReporter *statusReporter) { +#ifdef CURLAVAILABLE + return new CURLFTPTransport(host, statusReporter); +#else + return new FTPLibFTPTransport(host, statusReporter); +#endif +} + + + + +InstallMgr::InstallMgr(const char *privatePath, StatusReporter *sr) { + statusReporter = sr; + this->privatePath = 0; + this->transport = 0; + stdstr(&(this->privatePath), privatePath); + if (this->privatePath) { + int len = strlen(this->privatePath); + if ((this->privatePath[len-1] == '/') + || (this->privatePath[len-1] == '\\')) + this->privatePath[len-1] = 0; + } + SWBuf confPath = (SWBuf)privatePath + "/InstallMgr.conf"; + FileMgr::createParent(confPath.c_str()); + + installConf = new SWConfig(confPath.c_str()); + + SectionMap::iterator sourcesSection; + ConfigEntMap::iterator sourceBegin; + ConfigEntMap::iterator sourceEnd; + + sources.clear(); + + setFTPPassive(stricmp((*installConf)["General"]["PassiveFTP"].c_str(), "false")!=0); + + sourcesSection = installConf->Sections.find("Sources"); + if (sourcesSection != installConf->Sections.end()) { + sourceBegin = sourcesSection->second.lower_bound("FTPSource"); + sourceEnd = sourcesSection->second.upper_bound("FTPSource"); + + while (sourceBegin != sourceEnd) { + InstallSource *is = new InstallSource("FTP", sourceBegin->second.c_str()); + sources[is->caption] = is; + SWBuf parent = (SWBuf)privatePath + "/" + is->source + "/file"; + FileMgr::createParent(parent.c_str()); + is->localShadow = (SWBuf)privatePath + "/" + is->source; + sourceBegin++; + } + } + + defaultMods.clear(); + sourcesSection = installConf->Sections.find("General"); + if (sourcesSection != installConf->Sections.end()) { + sourceBegin = sourcesSection->second.lower_bound("DefaultMod"); + sourceEnd = sourcesSection->second.upper_bound("DefaultMod"); + + while (sourceBegin != sourceEnd) { + defaultMods.insert(sourceBegin->second.c_str()); + sourceBegin++; + } + } +} + + +InstallMgr::~InstallMgr() { + delete [] privatePath; + delete installConf; + + for (InstallSourceMap::iterator it = sources.begin(); it != sources.end(); ++it) { + delete it->second; + } +} + + +void InstallMgr::terminate() { if (transport) transport->terminate(); } + +int InstallMgr::removeModule(SWMgr *manager, const char *moduleName) { + SectionMap::iterator module; + ConfigEntMap::iterator fileBegin; + ConfigEntMap::iterator fileEnd, entry; + + // save our own copy, cuz when we remove the module from the SWMgr + // it's likely we'll free the memory passed to us in moduleName + SWBuf modName = moduleName; + module = manager->config->Sections.find(modName); + + if (module != manager->config->Sections.end()) { + // to be sure all files are closed + // this does not remove the .conf information from SWMgr + manager->deleteModule(modName); + + fileBegin = module->second.lower_bound("File"); + fileEnd = module->second.upper_bound("File"); + + SWBuf modFile; + SWBuf modDir; + entry = module->second.find("AbsoluteDataPath"); + modDir = entry->second.c_str(); + removeTrailingSlash(modDir); + if (fileBegin != fileEnd) { // remove each file + while (fileBegin != fileEnd) { + modFile = modDir; + modFile += "/"; + modFile += fileBegin->second.c_str(); + //remove file + FileMgr::removeFile(modFile.c_str()); + fileBegin++; + } + } + else { //remove all files in DataPath directory + + DIR *dir; + struct dirent *ent; + ConfigEntMap::iterator entry; + + FileMgr::removeDir(modDir.c_str()); + + if ((dir = opendir(manager->configPath))) { // find and remove .conf file + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + modFile = manager->configPath; + removeTrailingSlash(modFile); + modFile += "/"; + modFile += ent->d_name; + SWConfig *config = new SWConfig(modFile.c_str()); + if (config->Sections.find(modName) != config->Sections.end()) { + delete config; + FileMgr::removeFile(modFile.c_str()); + } + else delete config; + } + } + closedir(dir); + } + } + return 0; + } + return 1; +} + + +int InstallMgr::ftpCopy(InstallSource *is, const char *src, const char *dest, bool dirTransfer, const char *suffix) { + int retVal = 0; + FTPTransport *trans = createFTPTransport(is->source, statusReporter); + transport = trans; // set classwide current transport for other thread terminate() call + trans->setPassive(passive); + + SWBuf urlPrefix = (SWBuf)"ftp://" + is->source; + + // let's be sure we can connect. This seems to be necessary but sucks +// SWBuf url = urlPrefix + is->directory.c_str() + "/"; //dont forget the final slash +// if (trans->getURL("swdirlist.tmp", url.c_str())) { +// SWLog::getSystemLog()->logDebug("FTPCopy: failed to get dir %s\n", url.c_str()); +// return -1; +// } + + + if (dirTransfer) { + SWBuf dir = (SWBuf)is->directory.c_str(); + removeTrailingSlash(dir); + dir += (SWBuf)"/" + src; //dont forget the final slash + + retVal = trans->copyDirectory(urlPrefix, dir, dest, suffix); + + + } + else { + SWTRY { + SWBuf url = urlPrefix + is->directory.c_str(); + removeTrailingSlash(url); + url += (SWBuf)"/" + src; //dont forget the final slash + if (trans->getURL(dest, url.c_str())) { + SWLog::getSystemLog()->logDebug("FTPCopy: failed to get file %s", url.c_str()); + retVal = -1; + } + } + SWCATCH (...) { + retVal = -1; + } + } + SWTRY { + FTPTransport *deleteMe = trans; + // do this order for threadsafeness + // (see terminate()) + trans = transport = 0; + delete deleteMe; + } + SWCATCH (...) {} + return retVal; +} + + +int InstallMgr::installModule(SWMgr *destMgr, const char *fromLocation, const char *modName, InstallSource *is) { + SectionMap::iterator module, section; + ConfigEntMap::iterator fileBegin; + ConfigEntMap::iterator fileEnd; + ConfigEntMap::iterator entry; + SWBuf sourceDir; + SWBuf buffer; + bool aborted = false; + bool cipher = false; + DIR *dir; + struct dirent *ent; + SWBuf modFile; + + SWLog::getSystemLog()->logDebug("***** InstallMgr::installModule\n"); + if (fromLocation) + SWLog::getSystemLog()->logDebug("***** fromLocation: %s \n", fromLocation); + SWLog::getSystemLog()->logDebug("***** modName: %s \n", modName); + + if (is) + sourceDir = (SWBuf)privatePath + "/" + is->source; + else sourceDir = fromLocation; + + removeTrailingSlash(sourceDir); + sourceDir += '/'; + + SWMgr mgr(sourceDir.c_str()); + + module = mgr.config->Sections.find(modName); + + if (module != mgr.config->Sections.end()) { + + entry = module->second.find("CipherKey"); + if (entry != module->second.end()) + cipher = true; + + // + // This first check is a method to allow a module to specify each + // file that needs to be copied + // + fileEnd = module->second.upper_bound("File"); + fileBegin = module->second.lower_bound("File"); + + if (fileBegin != fileEnd) { // copy each file + if (is) { + while (fileBegin != fileEnd) { // ftp each file first + buffer = sourceDir + fileBegin->second.c_str(); + if (ftpCopy(is, fileBegin->second.c_str(), buffer.c_str())) { + aborted = true; + break; // user aborted + } + fileBegin++; + } + fileBegin = module->second.lower_bound("File"); + } + + if (!aborted) { + // DO THE INSTALL + while (fileBegin != fileEnd) { + SWBuf sourcePath = sourceDir; + sourcePath += fileBegin->second.c_str(); + SWBuf dest = destMgr->prefixPath; + removeTrailingSlash(dest); + dest += '/'; + dest += fileBegin->second.c_str(); + FileMgr::copyFile(sourcePath.c_str(), dest.c_str()); + + fileBegin++; + } + } + //--------------- + + if (is) { + fileBegin = module->second.lower_bound("File"); + while (fileBegin != fileEnd) { // delete each tmp ftp file + buffer = sourceDir + fileBegin->second.c_str(); + FileMgr::removeFile(buffer.c_str()); + fileBegin++; + } + } + } + + // This is the REAL install code, the above code I don't think has + // ever been used + // + // Copy all files in DataPath directory + // + else { + ConfigEntMap::iterator entry; + + entry = module->second.find("AbsoluteDataPath"); + if (entry != module->second.end()) { + SWBuf absolutePath = entry->second.c_str(); + SWBuf relativePath = absolutePath; + entry = module->second.find("PrefixPath"); + if (entry != module->second.end()) { + relativePath << strlen(entry->second.c_str()); + } + else { + relativePath << strlen(mgr.prefixPath); + } + SWLog::getSystemLog()->logDebug("***** mgr.prefixPath: %s \n", mgr.prefixPath); + SWLog::getSystemLog()->logDebug("***** destMgr->prefixPath: %s \n", destMgr->prefixPath); + SWLog::getSystemLog()->logDebug("***** absolutePath: %s \n", absolutePath.c_str()); + SWLog::getSystemLog()->logDebug("***** relativePath: %s \n", relativePath.c_str()); + + if (is) { + if (ftpCopy(is, relativePath.c_str(), absolutePath.c_str(), true)) { + aborted = true; // user aborted + } + } + if (!aborted) { + SWBuf destPath = (SWBuf)destMgr->prefixPath + relativePath; + FileMgr::copyDir(absolutePath.c_str(), destPath.c_str()); + } + if (is) { // delete tmp ftp files +// mgr->deleteModule(modName); + FileMgr::removeDir(absolutePath.c_str()); + } + } + } + if (!aborted) { + SWBuf confDir = sourceDir + "mods.d/"; + if ((dir = opendir(confDir.c_str()))) { // find and copy .conf file + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + modFile = confDir; + modFile += ent->d_name; + SWConfig *config = new SWConfig(modFile.c_str()); + if (config->Sections.find(modName) != config->Sections.end()) { + SWBuf targetFile = destMgr->configPath; //"./mods.d/"; + removeTrailingSlash(targetFile); + targetFile += "/"; + targetFile += ent->d_name; + FileMgr::copyFile(modFile.c_str(), targetFile.c_str()); + if (cipher) { + if (getCipherCode(modName, config)) { + SWMgr newDest(destMgr->prefixPath); + removeModule(&newDest, modName); + aborted = true; + } + else { + config->Save(); + FileMgr::copyFile(modFile.c_str(), targetFile.c_str()); + } + } + } + delete config; + } + } + closedir(dir); + } + } + return (aborted) ? -1 : 0; + } + return 1; +} + + +// override this and provide an input mechanism to allow your users +// to enter the decipher code for a module. +// return true you added the cipher code to the config. +// default to return 'aborted' +bool InstallMgr::getCipherCode(const char *modName, SWConfig *config) { + return false; + +/* a sample implementation, roughly taken from the windows installmgr + + SectionMap::iterator section; + ConfigEntMap::iterator entry; + SWBuf tmpBuf; + section = config->Sections.find(modName); + if (section != config->Sections.end()) { + entry = section->second.find("CipherKey"); + if (entry != section->second.end()) { + entry->second = GET_USER_INPUT(); + config->Save(); + + // LET'S SHOW THE USER SOME SAMPLE TEXT FROM THE MODULE + SWMgr *mgr = new SWMgr(); + SWModule *mod = mgr->Modules[modName]; + mod->setKey("Ipet 2:12"); + tmpBuf = mod->StripText(); + mod->setKey("gen 1:10"); + tmpBuf += "\n\n"; + tmpBuf += mod->StripText(); + SOME_DIALOG_CONTROL->SETTEXT(tmpBuf.c_str()); + delete mgr; + + // if USER CLICKS OK means we should return true + return true; + } + } + return false; +*/ + +} + + +int InstallMgr::refreshRemoteSource(InstallSource *is) { + SWBuf root = (SWBuf)privatePath + (SWBuf)"/" + is->source.c_str(); + removeTrailingSlash(root); + SWBuf target = root + "/mods.d"; + int errorCode = -1; //0 means successful + + FileMgr::removeDir(target.c_str()); + + if (!FileMgr::existsDir(target)) + FileMgr::createPathAndFile(target+"/globals.conf"); + +#ifndef EXCLUDEZLIB + SWBuf archive = root + "/mods.d.tar.gz"; + + errorCode = ftpCopy(is, "mods.d.tar.gz", archive.c_str(), false); + if (!errorCode) { //sucessfully downloaded the tar,gz of module configs + FileDesc *fd = FileMgr::getSystemFileMgr()->open(archive.c_str(), FileMgr::RDONLY); + untargz(fd->getFd(), root.c_str()); + FileMgr::getSystemFileMgr()->close(fd); + } + else if (!term) //if the tar.gz download was canceled don't continue with another download +#endif + errorCode = ftpCopy(is, "mods.d", target.c_str(), true, ".conf"); //copy the whole directory + + is->flush(); + return errorCode; +} + + +bool InstallMgr::isDefaultModule(const char *modName) { + return defaultMods.count(modName); +} + +/************************************************************************ + * getModuleStatus - compare the modules of two SWMgrs and return a + * vector describing the status of each. See MODSTAT_* + */ +map<SWModule *, int> InstallMgr::getModuleStatus(const SWMgr &base, const SWMgr &other) { + map<SWModule *, int> retVal; + SWBuf targetVersion; + SWBuf sourceVersion; + SWBuf softwareVersion; + bool cipher; + bool keyPresent; + int modStat; + + for (ModMap::const_iterator mod = other.Modules.begin(); mod != other.Modules.end(); mod++) { + + modStat = 0; + + cipher = false; + keyPresent = false; + + const char *v = mod->second->getConfigEntry("CipherKey"); + if (v) { + cipher = true; + keyPresent = *v; + } + + targetVersion = "0.0"; + sourceVersion = "1.0"; + softwareVersion = (const char *)SWVersion::currentVersion; + + v = mod->second->getConfigEntry("Version"); + if (v) sourceVersion = v; + + v = mod->second->getConfigEntry("MinimumVersion"); + if (v) softwareVersion = v; + + const SWModule *baseMod = base.getModule(mod->first); + if (baseMod) { + targetVersion = "1.0"; + v = baseMod->getConfigEntry("Version"); + if (v) targetVersion = v; + modStat |= (SWVersion(sourceVersion.c_str()) > SWVersion(targetVersion.c_str())) ? MODSTAT_UPDATED : (SWVersion(sourceVersion.c_str()) < SWVersion(targetVersion.c_str())) ? MODSTAT_OLDER : MODSTAT_SAMEVERSION; + } + else modStat |= MODSTAT_NEW; + + if (cipher) modStat |= MODSTAT_CIPHERED; + if (keyPresent) modStat |= MODSTAT_CIPHERKEYPRESENT; + retVal[mod->second] = modStat; + } + return retVal; +} + + +InstallSource::InstallSource(const char *type, const char *confEnt) { + this->type = type; + mgr = 0; + userData = 0; + if (confEnt) { + char *buf = 0; + stdstr(&buf, confEnt); + + caption = strtok(buf, "|"); + source = strtok(0, "|"); + directory = strtok(0, "|"); + removeTrailingSlash(directory); + delete [] buf; + } +} + + +InstallSource::~InstallSource() { + if (mgr) + delete mgr; +} + + +void InstallSource::flush() { + if (mgr) { + delete mgr; + mgr = 0; + } +} + + +SWMgr *InstallSource::getMgr() { + if (!mgr) + // ..., false = don't augment ~home directory. + mgr = new SWMgr(localShadow.c_str(), true, 0, false, false); + return mgr; +} + + +SWORD_NAMESPACE_END + diff --git a/src/mgr/localemgr.cpp b/src/mgr/localemgr.cpp new file mode 100644 index 0000000..ead076a --- /dev/null +++ b/src/mgr/localemgr.cpp @@ -0,0 +1,257 @@ +/****************************************************************************** + * localemgr.cpp - implementation of class LocaleMgr used to interact with + * registered locales for a sword installation + * + * $Id: localemgr.cpp 2080 2007-09-17 06:21:29Z 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 <stdio.h> +#include <stdlib.h> +#include <fcntl.h> + +#include <sys/stat.h> +#include <dirent.h> + +#include <swmgr.h> +#include <utilstr.h> + +#include <stringmgr.h> +#include <filemgr.h> +#include <localemgr.h> +#include <swlocale.h> +#include <swlog.h> + + +SWORD_NAMESPACE_START + +LocaleMgr *LocaleMgr::systemLocaleMgr = 0; + +class __staticsystemLocaleMgr { +public: + __staticsystemLocaleMgr() { } + ~__staticsystemLocaleMgr() { delete LocaleMgr::systemLocaleMgr; } +} _staticsystemLocaleMgr; + + +LocaleMgr *LocaleMgr::getSystemLocaleMgr() { + if (!systemLocaleMgr) + systemLocaleMgr = new LocaleMgr(); + + return systemLocaleMgr; +} + + +void LocaleMgr::setSystemLocaleMgr(LocaleMgr *newLocaleMgr) { + if (systemLocaleMgr) + delete systemLocaleMgr; + systemLocaleMgr = newLocaleMgr; +} + + +LocaleMgr::LocaleMgr(const char *iConfigPath) { + locales = new LocaleMap(); + char *prefixPath = 0; + char *configPath = 0; + char configType = 0; + SWBuf path; + std::list<SWBuf> augPaths; + + defaultLocaleName = 0; + + if (!iConfigPath) { + SWLog::getSystemLog()->logDebug("LOOKING UP LOCALE DIRECTORY..."); + SWMgr::findConfig(&configType, &prefixPath, &configPath, &augPaths); + SWLog::getSystemLog()->logDebug("LOOKING UP LOCALE DIRECTORY COMPLETE."); + } + else configPath = (char *)iConfigPath; + + if (prefixPath) { + switch (configType) { + case 2: + int i; + for (i = strlen(configPath)-1; ((i) && (configPath[i] != '/') && (configPath[i] != '\\')); i--); + configPath[i] = 0; + path = configPath; + path += "/"; + break; + default: + path = prefixPath; + if ((prefixPath[strlen(prefixPath)-1] != '\\') && (prefixPath[strlen(prefixPath)-1] != '/')) + path += "/"; + + break; + } + if (FileMgr::existsDir(path.c_str(), "locales.d")) { + path += "locales.d"; + loadConfigDir(path.c_str()); + } + } + + if (augPaths.size()) { //load locale files from all augmented paths + std::list<SWBuf>::iterator it = augPaths.begin(); + std::list<SWBuf>::iterator end = augPaths.end(); + + for (;it != end; ++it) { + if (FileMgr::existsDir((*it).c_str(), "locales.d")) { + SWBuf path = (*it) + "locales.d"; + loadConfigDir(path.c_str()); + } + } + } + + // Locales will be invalidated if you change the StringMgr + // So only use the default hardcoded locale and let the + // frontends change the locale if they want + stdstr(&defaultLocaleName, "en_US"); + + if (prefixPath) + delete [] prefixPath; + + if (configPath) + delete [] configPath; +} + + +LocaleMgr::~LocaleMgr() { + if (defaultLocaleName) + delete [] defaultLocaleName; + deleteLocales(); + delete locales; +} + + +void LocaleMgr::loadConfigDir(const char *ipath) { + DIR *dir; + struct dirent *ent; + SWBuf newmodfile; + LocaleMap::iterator it; + SWLog::getSystemLog()->logInformation("LocaleMgr::loadConfigDir loading %s", ipath); + + if ((dir = opendir(ipath))) { + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + newmodfile = ipath; + if ((ipath[strlen(ipath)-1] != '\\') && (ipath[strlen(ipath)-1] != '/')) + newmodfile += "/"; + newmodfile += ent->d_name; + SWLocale *locale = new SWLocale(newmodfile.c_str()); + + if (locale->getName()) { + bool supported = false; + if (StringMgr::hasUTF8Support()) { + supported = (locale->getEncoding() && (!strcmp(locale->getEncoding(), "UTF-8") || !strcmp(locale->getEncoding(), "ASCII")) ); + } + else { + supported = !locale->getEncoding() || (locale->getEncoding() && (strcmp(locale->getEncoding(), "UTF-8") != 0)); //exclude UTF-8 locales + } + + if (!supported) { //not supported + delete locale; + continue; + } + + it = locales->find(locale->getName()); + if (it != locales->end()) { // already present + *((*it).second) += *locale; + delete locale; + } + else locales->insert(LocaleMap::value_type(locale->getName(), locale)); + } + else delete locale; + } + } + closedir(dir); + } +} + + +void LocaleMgr::deleteLocales() { + + LocaleMap::iterator it; + + for (it = locales->begin(); it != locales->end(); it++) + delete (*it).second; + + locales->erase(locales->begin(), locales->end()); +} + + +SWLocale *LocaleMgr::getLocale(const char *name) { + LocaleMap::iterator it; + + it = locales->find(name); + if (it != locales->end()) + return (*it).second; + + SWLog::getSystemLog()->logWarning("LocaleMgr::getLocale failed to find %s\n", name); + return 0; +} + + +std::list <SWBuf> LocaleMgr::getAvailableLocales() { + std::list <SWBuf> retVal; + for (LocaleMap::iterator it = locales->begin(); it != locales->end(); it++) + retVal.push_back((*it).second->getName()); + + return retVal; +} + + +const char *LocaleMgr::translate(const char *text, const char *localeName) { + SWLocale *target; + if (!localeName) { + localeName = getDefaultLocaleName(); + } + target = getLocale(localeName); + if (target) + return target->translate(text); + return text; +} + + +const char *LocaleMgr::getDefaultLocaleName() { + return defaultLocaleName; +} + + +void LocaleMgr::setDefaultLocaleName(const char *name) { + char *tmplang=0; + stdstr(&tmplang, name); + // discard everything after '.' usually encoding e.g. .UTF-8 + strtok(tmplang, "."); + // also discard after '@' so e.g. @euro locales are found + strtok(tmplang, "@"); + + stdstr(&defaultLocaleName, tmplang); + + // First check for what we ask for + if (!getLocale(tmplang)) { + // check for locale without country + char *nocntry=0; + stdstr(&nocntry, tmplang); + strtok(nocntry, "_"); + if (getLocale(nocntry)) { + stdstr(&defaultLocaleName, nocntry); + } + delete [] nocntry; + } + delete [] tmplang; +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/markupfiltmgr.cpp b/src/mgr/markupfiltmgr.cpp new file mode 100644 index 0000000..5dca845 --- /dev/null +++ b/src/mgr/markupfiltmgr.cpp @@ -0,0 +1,295 @@ +/****************************************************************************** + * swmarkupmgr.cpp - implementaion of class MarkupFilterMgr, subclass of + * used to transcode all module text to a requested + * markup. + * + * 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 <thmlplain.h> +#include <gbfplain.h> +#include <osisplain.h> +#include <teiplain.h> +#include <thmlgbf.h> +#include <gbfthml.h> +#include <thmlhtml.h> +#include <gbfhtml.h> +#include <plainhtml.h> +#include <thmlhtmlhref.h> +#include <gbfhtmlhref.h> +#include <teihtmlhref.h> +#include <thmlrtf.h> +#include <gbfrtf.h> +#include <gbfosis.h> +#include <thmlosis.h> +#include <osisrtf.h> +#include <teirtf.h> +#include <osisosis.h> +#include <osishtmlhref.h> +#include <gbfwebif.h> +#include <thmlwebif.h> +#include <osiswebif.h> +#include <swmodule.h> + +#include <markupfiltmgr.h> + +#include <swmgr.h> + +SWORD_NAMESPACE_START + +/****************************************************************************** + * MarkupFilterMgr Constructor - initializes instance of MarkupFilterMgr + * + * ENT: + * enc - Encoding format to emit + * mark - Markup format to emit + */ + +MarkupFilterMgr::MarkupFilterMgr (char mark, char enc) + : EncodingFilterMgr(enc) { + + markup = mark; + + CreateFilters(markup); +} + + +/****************************************************************************** + * MarkupFilterMgr Destructor - Cleans up instance of MarkupFilterMgr + */ + +MarkupFilterMgr::~MarkupFilterMgr() { + if (fromthml) + delete (fromthml); + if (fromgbf) + delete (fromgbf); + if (fromplain) + delete (fromplain); + if (fromosis) + delete (fromosis); + if (fromtei) + delete (fromtei); +} + +/****************************************************************************** + * MarkupFilterMgr::Markup - sets/gets markup + * + * ENT: mark - new encoding or 0 to simply get the current markup + * + * RET: markup + */ +char MarkupFilterMgr::Markup(char mark) { + if (mark && mark != markup) { + markup = mark; + ModMap::const_iterator module; + + SWFilter * oldplain = fromplain; + SWFilter * oldthml = fromthml; + SWFilter * oldgbf = fromgbf; + SWFilter * oldosis = fromosis; + SWFilter * oldtei = fromtei; + + CreateFilters(markup); + + for (module = getParentMgr()->Modules.begin(); module != getParentMgr()->Modules.end(); module++) + switch (module->second->Markup()) { + case FMT_THML: + if (oldthml != fromthml) { + if (oldthml) { + if (!fromthml) { + module->second->RemoveRenderFilter(oldthml); + } + else { + module->second->ReplaceRenderFilter(oldthml, fromthml); + } + } + else if (fromthml) { + module->second->AddRenderFilter(fromthml); + } + } + break; + case FMT_GBF: + if (oldgbf != fromgbf) { + if (oldgbf) { + if (!fromgbf) { + module->second->RemoveRenderFilter(oldgbf); + } + else { + module->second->ReplaceRenderFilter(oldgbf, fromgbf); + } + } + else if (fromgbf) { + module->second->AddRenderFilter(fromgbf); + } + break; + } + case FMT_PLAIN: + if (oldplain != fromplain) { + if (oldplain) { + if (!fromplain) { + module->second->RemoveRenderFilter(oldplain); + } + else { + module->second->ReplaceRenderFilter(oldplain, fromplain); + } + } + else if (fromplain) { + module->second->AddRenderFilter(fromplain); + } + break; + } + case FMT_OSIS: + if (oldosis != fromosis) { + if (oldosis) { + if (!fromosis) { + module->second->RemoveRenderFilter(oldosis); + } + else { + module->second->ReplaceRenderFilter(oldosis, fromosis); + } + } + else if (fromosis) { + module->second->AddRenderFilter(fromosis); + } + break; + } + case FMT_TEI: + if (oldtei != fromtei) { + if (oldtei) { + if (!fromtei) { + module->second->RemoveRenderFilter(oldtei); + } + else { + module->second->ReplaceRenderFilter(oldtei, fromtei); + } + } + else if (fromtei) { + module->second->AddRenderFilter(fromtei); + } + break; + } + } + + if (oldthml) + delete oldthml; + if (oldgbf) + delete oldgbf; + if (oldplain) + delete oldplain; + if (oldosis) + delete oldosis; + if (oldtei) + delete oldtei; + } + return markup; +} + +void MarkupFilterMgr::AddRenderFilters(SWModule *module, ConfigEntMap §ion) { + switch (module->Markup()) { + case FMT_THML: + if (fromthml) + module->AddRenderFilter(fromthml); + break; + case FMT_GBF: + if (fromgbf) + module->AddRenderFilter(fromgbf); + break; + case FMT_PLAIN: + if (fromplain) + module->AddRenderFilter(fromplain); + break; + case FMT_OSIS: + if (fromosis) + module->AddRenderFilter(fromosis); + break; + case FMT_TEI: + if (fromtei) + module->AddRenderFilter(fromtei); + break; + } +} + +void MarkupFilterMgr::CreateFilters(char markup) { + + switch (markup) { + case FMT_PLAIN: + fromplain = NULL; + fromthml = new ThMLPlain(); + fromgbf = new GBFPlain(); + fromosis = new OSISPlain(); + fromtei = new TEIPlain(); + break; + case FMT_THML: + fromplain = NULL; + fromthml = NULL; + fromgbf = new GBFThML(); + fromosis = NULL; + fromtei = NULL; + break; + case FMT_GBF: + fromplain = NULL; + fromthml = new ThMLGBF(); + fromgbf = NULL; + fromosis = NULL; + fromtei = NULL; + break; + case FMT_HTML: + fromplain = new PLAINHTML(); + fromthml = new ThMLHTML(); + fromgbf = new GBFHTML(); + fromosis = NULL; + fromtei = NULL; + break; + case FMT_HTMLHREF: + fromplain = new PLAINHTML(); + fromthml = new ThMLHTMLHREF(); + fromgbf = new GBFHTMLHREF(); + fromosis = new OSISHTMLHREF(); + fromtei = new TEIHTMLHREF(); + break; + case FMT_RTF: + fromplain = NULL; + fromthml = new ThMLRTF(); + fromgbf = new GBFRTF(); + fromosis = new OSISRTF(); + fromtei = new TEIRTF(); + break; + case FMT_OSIS: + fromplain = NULL; + fromthml = new ThMLOSIS(); + fromgbf = new GBFOSIS(); + fromosis = new OSISOSIS(); + fromtei = NULL; + break; + case FMT_WEBIF: + fromplain = NULL; + fromthml = new ThMLWEBIF(); + fromgbf = new GBFWEBIF(); + fromosis = new OSISWEBIF(); + fromtei = NULL; + break; + case FMT_TEI: + fromplain = NULL; + fromthml = NULL; + fromgbf = NULL; + fromosis = NULL; + fromtei = NULL; + break; + } + +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/stringmgr.cpp b/src/mgr/stringmgr.cpp new file mode 100644 index 0000000..c4a994e --- /dev/null +++ b/src/mgr/stringmgr.cpp @@ -0,0 +1,280 @@ +/****************************************************************************** + * stringmgr.cpp - implementation of class StringMgr + * + * $Id: stringmgr.cpp 2115 2007-10-16 18:29:00Z 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 <stringmgr.h> +#include <swlog.h> +#include <localemgr.h> +#include <utilstr.h> + +#ifdef _ICU_ + +#include <unicode/utypes.h> +#include <unicode/ucnv.h> +#include <unicode/ustring.h> +#include <unicode/uchar.h> + +#include <unicode/unistr.h> +#include <unicode/translit.h> + +#include <unicode/locid.h> + +#endif + +SWORD_NAMESPACE_START + +StringMgr *StringMgr::systemStringMgr = 0; + +class __staticsystemStringMgr { +public: + __staticsystemStringMgr() { } + ~__staticsystemStringMgr() { if (StringMgr::systemStringMgr) delete StringMgr::systemStringMgr; StringMgr::systemStringMgr = 0; } +} _staticsystemStringMgr; + +/** + * Determine whether the string contains a valid unicode sequence. The following table give the pattern of a valid UTF-8 character. + * Unicode Range 1st 2nd 3rd 4th 5th 6th + * U-00000000 - U-0000007F 0nnnnnnn + * U-00000080 - U-000007FF 110nnnnn 10nnnnnn + * U-00000800 - U-0000FFFF 1110nnnn 10nnnnnn 10nnnnnn + * U-00010000 - U-001FFFFF 11110nnn 10nnnnnn 10nnnnnn 10nnnnnn + * U-00200000 - U-03FFFFFF 111110nn 10nnnnnn 10nnnnnn 10nnnnnn 10nnnnnn + * U-04000000 - U-7FFFFFFF 1111110n 10nnnnnn 10nnnnnn 10nnnnnn 10nnnnnn 10nnnnnn + * Note: + * The latest UTF-8 RFC allows for a max of 4 bytes. Earlier allowed 6. + * The number of bits of the leading byte before the first 0 is the total number of bytes + * The "n" are the bits of the unicode codepoint. + * + * This routine does not check to see if the code point is in the range. It could. + * + * @param txt the text to check + * @return 1 if all high order characters form a valid unicode sequence + * -1 if there are no high order characters + * 0 if there are high order characters that do not form a valid unicode sequence + * @author DM Smith [dmsmith555 at yahoo dot com] + */ +int isValidUTF8(unsigned char *txt) { + unsigned int countUTF8 = 0; +#if 0 + unsigned char parts = 0; + + + unsigned char *p = txt; + while (*p) { + // Is the high order bit set? + if (*p & 0x80) { + // then count the number of high order bits that are set + // this determines the number of following bytes need to have high order bits set + unsigned char i = *p; + for (parts = 0; i & 0x80; parts++) { + i <<= 1; + } + + + // The pattern 10nnnnnn is not a unicode character + if (parts == 1) { + return 0; + } + else { + while (--parts && ++*p) { + // The pattern of each following character must be: 10nnnnnn + if (0xc0 & *p != 0x80) { + return 0; + } + } + + // Oops, we've run out of bytes too soon: Cannot be UTF-8 + if (parts) { + return 0; + } + } + countUTF8++; + } + } + + // At this point it is either UTF-8 or ascii +#endif + return countUTF8 ? 1 : -1; +} + + +#ifdef _ICU_ + +//here comes our ICUStringMgr reimplementation +class ICUStringMgr : public StringMgr { +public: + virtual char *upperUTF8(char *, unsigned int maxlen = 0) const; + +protected: + virtual bool supportsUnicode() const { return true; }; +}; + +#endif + + +/** Default constructor +*/ +StringMgr::StringMgr() { +} + +/** Copy constructor +*/ +StringMgr::StringMgr(const StringMgr &m) { +} + +/** Destructor +*/ +StringMgr::~StringMgr() { +} + +/** Sets the global StringMgr handle +* @param newStringMgr The new global StringMgr. This pointer will be deleted by this StringMgr +*/ +void StringMgr::setSystemStringMgr(StringMgr *newStringMgr) { + if (systemStringMgr) + delete systemStringMgr; + + systemStringMgr = newStringMgr; + + // TODO: this is magic. apparently we have to reset the system localemgr upon changing stringmgr. + // setting system stringmgr should be set before localemgr and not possible to change. + // rework this design. + LocaleMgr::getSystemLocaleMgr()->setSystemLocaleMgr(new LocaleMgr()); +} + +/** Returns the global StringMgr handle +* @return The global string handle +*/ +StringMgr* StringMgr::getSystemStringMgr() { + if (!systemStringMgr) { +#ifdef _ICU_ + systemStringMgr = new ICUStringMgr(); +// SWLog::getSystemLog()->logInformation("created default ICUStringMgr"); +#else + systemStringMgr = new StringMgr(); +// SWLog::getSystemLog()->logInformation("created default StringMgr"); +#endif + } + + return systemStringMgr; +} + + +/** + * This is a fallback method. It should never be called. + * If UTF8 support is desired, then a UTF8 StringMgr needs + * to be used. + * + * Here we just do our best. + * + * Converts the param to an upper case UTF8 string + * @param t - The text encoded in utf8 which should be turned into an upper case string + * + */ +char *StringMgr::upperUTF8(char *t, unsigned int maxlen) const { + // try to decide if it's worth trying to toupper. Do we have more + // characters which are probably lower latin than not? + // we still don't use isValidUTF8 optimally. what if we have 1 unicode + // character in the string? should we not try to upper any of the string? + // dunno. Best solution is to upper all other characters. Don't have + // time to write that before release. + long performOp = 0; + if (!isValidUTF8((unsigned char *)t)) { + performOp = 1; + } + else { + for (const char *ch = t; *ch; ch++) { + performOp += (*ch > 0) ? 1 : -1; + } + } + + if (performOp > 0) { + return upperLatin1(t); + } + + return t; +} + + +/** + * Converts the param to an uppercase latin1 string + * @param The text encoded in latin1 which should be turned into an upper case string + */ +char *StringMgr::upperLatin1(char *buf, unsigned int maxlen) const { + if (!buf) + return 0; + + char *ret = buf; + bool checkMax = maxlen; + + while (*buf && (!checkMax || maxlen--)) { + *buf = SW_toupper(*buf); + buf++; + } + + return ret; +} + +bool StringMgr::supportsUnicode() const { + return false; //default impl has no UTF8 support +} + + +#ifdef _ICU_ + +char *ICUStringMgr::upperUTF8(char *buf, unsigned int maxlen) const { + char *ret = buf; + int max = (maxlen) ? maxlen : strlen(buf); + + UErrorCode err = U_ZERO_ERROR; + + if (!buf || !max) { + return ret; + } + + UChar *lowerStr = new UChar[max+10]; + UChar *upperStr = new UChar[max+10]; + + u_strFromUTF8(lowerStr, max+9, 0, buf, -1, &err); + if (err != U_ZERO_ERROR) { +// SWLog::getSystemLog()->logError("from: %s", u_errorName(err)); + delete [] lowerStr; + delete [] upperStr; + return ret; + } + + u_strToUpper(upperStr, max+9, lowerStr, -1, 0, &err); + if (err != U_ZERO_ERROR) { +// SWLog::getSystemLog()->logError("upperCase: %s", u_errorName(err)); + delete [] lowerStr; + delete [] upperStr; + return ret; + } + + ret = u_strToUTF8(ret, max, 0, upperStr, -1, &err); + + delete [] lowerStr; + delete [] upperStr; + return ret; +} + +#endif + +SWORD_NAMESPACE_END diff --git a/src/mgr/swcacher.cpp b/src/mgr/swcacher.cpp new file mode 100644 index 0000000..57d0817 --- /dev/null +++ b/src/mgr/swcacher.cpp @@ -0,0 +1,47 @@ +/****************************************************************************** + * swcacher.h - definition of class SWCacher used to provide an interface for + * objects that cache and want a standard interface for cleaning up. + * + * $Id: swcacher.cpp 1688 2005-01-01 04:42:26Z 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 <swcacher.h> + +SWORD_NAMESPACE_START + + +SWCacher::SWCacher() { +} + + +SWCacher::~SWCacher() { +} + + +void SWCacher::flush() { +} + +long SWCacher::resourceConsumption() { + return 0; +} + +long SWCacher::lastAccess() { + return 0; +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/swconfig.cpp b/src/mgr/swconfig.cpp new file mode 100644 index 0000000..376c206 --- /dev/null +++ b/src/mgr/swconfig.cpp @@ -0,0 +1,155 @@ +/****************************************************************************** + * swconfig.cpp - implementation of Class SWConfig used for saving and + * retrieval of configuration information + * + * $Id: swconfig.cpp 1828 2005-06-10 16:24:46Z 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 <swconfig.h> +#include <utilstr.h> +#include <filemgr.h> +#include <fcntl.h> + + +SWORD_NAMESPACE_START + +SWConfig::SWConfig(const char * ifilename) { + filename = ifilename; + Load(); +} + + +SWConfig::~SWConfig() { +} + +void SWConfig::Load() { + FileDesc *cfile; + char *buf, *data; + SWBuf line; + ConfigEntMap cursect; + SWBuf sectname; + bool first = true; + + Sections.erase(Sections.begin(), Sections.end()); + + cfile = FileMgr::getSystemFileMgr()->open(filename.c_str(), FileMgr::RDONLY); + if (cfile->getFd() > 0) { + bool goodLine = FileMgr::getLine(cfile, line); + + // clean UTF encoding tags at start of file + while (goodLine && line.length() && + ((((unsigned char)line[0]) == 0xEF) || + (((unsigned char)line[0]) == 0xBB) || + (((unsigned char)line[0]) == 0xBF))) { + line << 1; + } + + while (goodLine) { + buf = new char [ line.length() + 1 ]; + strcpy(buf, line.c_str()); + if (*strstrip(buf) == '[') { + if (!first) + Sections.insert(SectionMap::value_type(sectname, cursect)); + else first = false; + + cursect.erase(cursect.begin(), cursect.end()); + + strtok(buf, "]"); + sectname = buf+1; + } + else { + strtok(buf, "="); + if ((*buf) && (*buf != '=')) { + if ((data = strtok(NULL, ""))) + cursect.insert(ConfigEntMap::value_type(buf, strstrip(data))); + else cursect.insert(ConfigEntMap::value_type(buf, "")); + } + } + delete [] buf; + goodLine = FileMgr::getLine(cfile, line); + } + if (!first) + Sections.insert(SectionMap::value_type(sectname, cursect)); + + FileMgr::getSystemFileMgr()->close(cfile); + } +} + + +void SWConfig::Save() { + FileDesc *cfile; + SWBuf buf; + SectionMap::iterator sit; + ConfigEntMap::iterator entry; + SWBuf sectname; + + cfile = FileMgr::getSystemFileMgr()->open(filename.c_str(), FileMgr::RDWR|FileMgr::CREAT|FileMgr::TRUNC); + if (cfile->getFd() > 0) { + + for (sit = Sections.begin(); sit != Sections.end(); sit++) { + buf = "\n["; + buf += (*sit).first.c_str(); + buf += "]\n"; + cfile->write(buf.c_str(), buf.length()); + for (entry = (*sit).second.begin(); entry != (*sit).second.end(); entry++) { + buf = (*entry).first.c_str(); + buf += "="; + buf += (*entry).second.c_str(); + buf += "\n"; + cfile->write(buf.c_str(), buf.length()); + } + } + buf = "\n"; + cfile->write(buf.c_str(), buf.length()); + FileMgr::getSystemFileMgr()->close(cfile); + } +} + + +void SWConfig::augment(SWConfig &addFrom) { + + SectionMap::iterator section; + ConfigEntMap::iterator entry, start, end; + + for (section = addFrom.Sections.begin(); section != addFrom.Sections.end(); section++) { + for (entry = (*section).second.begin(); entry != (*section).second.end(); entry++) { + start = Sections[section->first].lower_bound(entry->first); + end = Sections[section->first].upper_bound(entry->first); + if (start != end) { + if (((++start) != end) + || ((++(addFrom.Sections[section->first].lower_bound(entry->first))) != addFrom.Sections[section->first].upper_bound(entry->first))) { + for (--start; start != end; start++) { + if (!strcmp(start->second.c_str(), entry->second.c_str())) + break; + } + if (start == end) + Sections[(*section).first].insert(ConfigEntMap::value_type((*entry).first, (*entry).second)); + } + else Sections[section->first][entry->first.c_str()] = entry->second.c_str(); + } + else Sections[section->first][entry->first.c_str()] = entry->second.c_str(); + } + } +} + + +ConfigEntMap & SWConfig::operator [] (const char *section) { + return Sections[section]; +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/swfiltermgr.cpp b/src/mgr/swfiltermgr.cpp new file mode 100644 index 0000000..434f2e0 --- /dev/null +++ b/src/mgr/swfiltermgr.cpp @@ -0,0 +1,93 @@ +/****************************************************************************** + * swfiltermgr.cpp - definition of class SWFilterMgr used as an interface to + * manage filters on a module + * + * $Id: swfiltermgr.cpp 1688 2005-01-01 04:42:26Z 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 <swfiltermgr.h> + +SWORD_NAMESPACE_START + + +SWFilterMgr::SWFilterMgr() { +} + + +SWFilterMgr::~SWFilterMgr() { +} + + +void SWFilterMgr::setParentMgr(SWMgr *parentMgr) { + this->parentMgr = parentMgr; +} + + +SWMgr *SWFilterMgr::getParentMgr() { + return parentMgr; +} + + +void SWFilterMgr::AddGlobalOptions(SWModule * module, ConfigEntMap & section, ConfigEntMap::iterator start, ConfigEntMap::iterator end) { +} + + +void SWFilterMgr::AddLocalOptions(SWModule * module, ConfigEntMap & section, ConfigEntMap::iterator start, ConfigEntMap::iterator end) { +} + + +/** +* Adds the encoding filters which are defined in "section" to the SWModule object "module". +* @param module To this module the encoding filter(s) are added +* @param section We use this section to get a list of filters we should apply to the module +*/ + +void SWFilterMgr::AddEncodingFilters(SWModule * module, ConfigEntMap & section) { +} + + +/** +* Adds the render filters which are defined in "section" to the SWModule object "module". +* @param module To this module the render filter(s) are added +* @param section We use this section to get a list of filters we should apply to the module +*/ + +void SWFilterMgr::AddRenderFilters(SWModule * module, ConfigEntMap & section) { +} + + +/** +* Adds the strip filters which are defined in "section" to the SWModule object "module". +* @param module To this module the strip filter(s) are added +* @param section We use this section to get a list of filters we should apply to the module +*/ + +void SWFilterMgr::AddStripFilters(SWModule * module, ConfigEntMap & section) { +} + + +/** +* Adds the raw filters which are defined in "section" to the SWModule object "module". +* @param module To this module the raw filter(s) are added +* @param section We use this section to get a list of filters we should apply to the module +*/ + +void SWFilterMgr::AddRawFilters(SWModule * module, ConfigEntMap & section) { +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/swlocale.cpp b/src/mgr/swlocale.cpp new file mode 100644 index 0000000..facb1ee --- /dev/null +++ b/src/mgr/swlocale.cpp @@ -0,0 +1,204 @@ +/****************************************************************************** + * swlocale.cpp - implementation of Class SWLocale used for retrieval + * of locale lookups + * + * $Id: swlocale.cpp 1864 2005-11-20 06:06:40Z scribe $ + * + * Copyright 2000 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 <swlocale.h> +#include <utilstr.h> +#include <map> +#include <swconfig.h> +#include <versekey.h> + +SWORD_NAMESPACE_START + +typedef std::map < SWBuf, SWBuf, std::less < SWBuf > >LookupMap; + +// I have bridge patterns, but this hides swconfig and map from lots o stuff +class SWLocale::Private { +public: + LookupMap lookupTable; +}; + + +SWLocale::SWLocale(const char * ifilename) { + p = new Private; + ConfigEntMap::iterator confEntry; + + name = 0; + description = 0; + encoding = 0; + bookAbbrevs = 0; + BMAX = 0; + books = 0; + localeSource = new SWConfig(ifilename); + + confEntry = localeSource->Sections["Meta"].find("Name"); + if (confEntry != localeSource->Sections["Meta"].end()) + stdstr(&name, (*confEntry).second.c_str()); + + confEntry = localeSource->Sections["Meta"].find("Description"); + if (confEntry != localeSource->Sections["Meta"].end()) + stdstr(&description, (*confEntry).second.c_str()); + + confEntry = localeSource->Sections["Meta"].find("Encoding"); //Either empty (==Latin1) or UTF-8 + if (confEntry != localeSource->Sections["Meta"].end()) + stdstr(&encoding, (*confEntry).second.c_str()); +} + + +SWLocale::~SWLocale() { + + delete localeSource; + + if (encoding) + delete [] encoding; + + if (description) + delete [] description; + + if (name) + delete [] name; + + if (bookAbbrevs) + delete [] bookAbbrevs; + + if (BMAX) { + for (int i = 0; i < 2; i++) + delete [] books[i]; + delete [] BMAX; + delete [] books; + } + delete p; +} + + +const char *SWLocale::translate(const char *text) { + LookupMap::iterator entry; + + entry = p->lookupTable.find(text); + + if (entry == p->lookupTable.end()) { + ConfigEntMap::iterator confEntry; + confEntry = localeSource->Sections["Text"].find(text); + if (confEntry == localeSource->Sections["Text"].end()) + p->lookupTable.insert(LookupMap::value_type(text, text)); + else {//valid value found + /* + - If Encoding==Latin1 and we have a StringHelper, convert to UTF-8 + - If StringHelper present and Encoding is UTF-8, use UTF8 + - If StringHelper not present and Latin1, use Latin1 + - If StringHelper not present and UTF-8, no idea what to do. Should't happen + */ +/* if (StringHelper::getSystemStringHelper()) { + if (!strcmp(encoding, "UTF-8")) { + p->lookupTable.insert(LookupMap::value_type(text, (*confEntry).second.c_str())); + } + else { //latin1 expected, convert to UTF-8 + SWBuf t((*confEntry).second.c_str()); + t = StringHelper::getSystemStringHelper()->latin2unicode( t ); + + p->lookupTable.insert(LookupMap::value_type(text, t.c_str())); + } + } + else { //no stringhelper, just insert. Nothing we can do*/ + p->lookupTable.insert(LookupMap::value_type(text, (*confEntry).second.c_str())); +// } + + } + entry = p->lookupTable.find(text); + } + return (*entry).second.c_str(); +} + + +const char *SWLocale::getName() { + return name; +} + + +const char *SWLocale::getDescription() { + return description; +} + +const char *SWLocale::getEncoding() { + return encoding; +} + +void SWLocale::augment(SWLocale &addFrom) { + *localeSource += *addFrom.localeSource; +} + +//#define NONNUMERICLOCALETHING 1 + +const struct abbrev *SWLocale::getBookAbbrevs() { + static const char *nullstr = ""; + if (!bookAbbrevs) { + ConfigEntMap::iterator it; + int i, j; + int size = localeSource->Sections["Book Abbrevs"].size(); + bookAbbrevs = new struct abbrev[size + 1]; + for (i = 0, j = 0, it = localeSource->Sections["Book Abbrevs"].begin(); it != localeSource->Sections["Book Abbrevs"].end(); it++, i++) { + #ifdef NONNUMERICLOCALETHING + int booknum = VerseKey::getOSISBookNum((*it).second.c_str()); + if (booknum != -1) { + bookAbbrevs[j].ab = (*it).first.c_str(); + bookAbbrevs[j].book = booknum; + j++; + } + #else + bookAbbrevs[i].ab = (*it).first.c_str(); + bookAbbrevs[i].book = atoi((*it).second.c_str()); + j++; + #endif + //printf("SWLocale::getBookAbbrevs %s:%s %d\n",bookAbbrevs[i].ab, + // (*it).second.c_str(), bookAbbrevs[i].book); + } + bookAbbrevs[j].ab = nullstr; + bookAbbrevs[j].book = -1; + } + + return bookAbbrevs; +} + + +void SWLocale::getBooks(char **iBMAX, struct sbook ***ibooks) { + if (!BMAX) { + BMAX = new char [2]; + BMAX[0] = VerseKey::builtin_BMAX[0]; + BMAX[1] = VerseKey::builtin_BMAX[1]; + + books = new struct sbook *[2]; + books[0] = new struct sbook[BMAX[0]]; + books[1] = new struct sbook[BMAX[1]]; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < BMAX[i]; j++) { + books[i][j] = VerseKey::builtin_books[i][j]; + books[i][j].name = translate(VerseKey::builtin_books[i][j].name); + } + } + } + + *iBMAX = BMAX; + *ibooks = books; +} + + +SWORD_NAMESPACE_END diff --git a/src/mgr/swmgr.cpp b/src/mgr/swmgr.cpp new file mode 100644 index 0000000..86c04d7 --- /dev/null +++ b/src/mgr/swmgr.cpp @@ -0,0 +1,1275 @@ +/****************************************************************************** + * swmgr.cpp - implementaion of class SWMgr used to interact with an install + * base of sword modules. + * + * $Id: swmgr.cpp 2169 2008-05-18 02:50:53Z 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 <stdio.h> +#include <stdlib.h> +#include <fcntl.h> + +#include <sys/stat.h> +#ifndef _MSC_VER +#include <iostream> +#endif +#include <dirent.h> + +#include <swmgr.h> +#include <rawtext.h> +#include <rawtext4.h> +#include <filemgr.h> +#include <rawgenbook.h> +#include <rawcom.h> +#include <rawcom4.h> +#include <hrefcom.h> +#include <rawld.h> +#include <rawld4.h> +#include <utilstr.h> +#include <gbfplain.h> +#include <thmlplain.h> +#include <osisplain.h> +#include <teiplain.h> +#include <papyriplain.h> +#include <gbfstrongs.h> +#include <gbffootnotes.h> +#include <gbfheadings.h> +#include <gbfredletterwords.h> +#include <gbfmorph.h> +#include <osisheadings.h> +#include <osisfootnotes.h> +#include <osisstrongs.h> +#include <osismorph.h> +#include <osislemma.h> +#include <osisredletterwords.h> +#include <osismorphsegmentation.h> +#include <osisscripref.h> +#include <thmlstrongs.h> +#include <thmlfootnotes.h> +#include <thmlheadings.h> +#include <thmlmorph.h> +#include <thmlvariants.h> +#include <thmllemma.h> +#include <thmlscripref.h> +#include <cipherfil.h> +#include <rawfiles.h> +#include <ztext.h> +#include <zld.h> +#include <zcom.h> +#include <lzsscomprs.h> +#include <utf8greekaccents.h> +#include <utf8cantillation.h> +#include <utf8hebrewpoints.h> +#include <greeklexattribs.h> +#include <swfiltermgr.h> +#include <swcipher.h> +#include <swoptfilter.h> + +#include <swlog.h> + +#include <iterator> + +#ifndef EXCLUDEZLIB +#include "zipcomprs.h" +#endif + + +#ifdef _ICU_ +#include <utf8transliterator.h> +#endif + +SWORD_NAMESPACE_START + +#ifdef _ICU_ +bool SWMgr::isICU = true; +#else +bool SWMgr::isICU = false; +#endif + + +#ifdef GLOBCONFPATH +const char *SWMgr::globalConfPath = GLOBCONFPATH; +#else +const char *SWMgr::globalConfPath = "/etc/sword.conf:/usr/local/etc/sword.conf"; +#endif + +void SWMgr::init() { + SWOptionFilter *tmpFilter = 0; + configPath = 0; + prefixPath = 0; + configType = 0; + myconfig = 0; + mysysconfig = 0; + homeConfig = 0; + augmentHome = true; + + cipherFilters.clear(); + optionFilters.clear(); + cleanupFilters.clear(); + tmpFilter = new ThMLVariants(); + optionFilters.insert(OptionFilterMap::value_type("ThMLVariants", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GBFStrongs(); + optionFilters.insert(OptionFilterMap::value_type("GBFStrongs", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GBFFootnotes(); + optionFilters.insert(OptionFilterMap::value_type("GBFFootnotes", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GBFRedLetterWords(); + optionFilters.insert(OptionFilterMap::value_type("GBFRedLetterWords", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GBFMorph(); + optionFilters.insert(OptionFilterMap::value_type("GBFMorph", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GBFHeadings(); + optionFilters.insert(OptionFilterMap::value_type("GBFHeadings", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISHeadings(); + optionFilters.insert(OptionFilterMap::value_type("OSISHeadings", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISStrongs(); + optionFilters.insert(OptionFilterMap::value_type("OSISStrongs", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISMorph(); + optionFilters.insert(OptionFilterMap::value_type("OSISMorph", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISLemma(); + optionFilters.insert(OptionFilterMap::value_type("OSISLemma", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISFootnotes(); + optionFilters.insert(OptionFilterMap::value_type("OSISFootnotes", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISScripref(); + optionFilters.insert(OptionFilterMap::value_type("OSISScripref", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISRedLetterWords(); + optionFilters.insert(OptionFilterMap::value_type("OSISRedLetterWords", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new OSISMorphSegmentation(); + optionFilters.insert(OptionFilterMap::value_type("OSISMorphSegmentation", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLStrongs(); + optionFilters.insert(OptionFilterMap::value_type("ThMLStrongs", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLFootnotes(); + optionFilters.insert(OptionFilterMap::value_type("ThMLFootnotes", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLMorph(); + optionFilters.insert(OptionFilterMap::value_type("ThMLMorph", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLHeadings(); + optionFilters.insert(OptionFilterMap::value_type("ThMLHeadings", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLLemma(); + optionFilters.insert(OptionFilterMap::value_type("ThMLLemma", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new ThMLScripref(); + optionFilters.insert(OptionFilterMap::value_type("ThMLScripref", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new UTF8GreekAccents(); + optionFilters.insert(OptionFilterMap::value_type("UTF8GreekAccents", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new UTF8HebrewPoints(); + optionFilters.insert(OptionFilterMap::value_type("UTF8HebrewPoints", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new UTF8Cantillation(); + optionFilters.insert(OptionFilterMap::value_type("UTF8Cantillation", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new GreekLexAttribs(); + optionFilters.insert(OptionFilterMap::value_type("GreekLexAttribs", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + tmpFilter = new PapyriPlain(); + optionFilters.insert(OptionFilterMap::value_type("PapyriPlain", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + +// UTF8Transliterator needs to be handled differently because it should always available as an option, for all modules +#ifdef _ICU_ + transliterator = new UTF8Transliterator(); + optionFilters.insert(OptionFilterMap::value_type("UTF8Transliterator", transliterator)); + options.push_back(transliterator->getOptionName()); + cleanupFilters.push_back(transliterator); +#endif + + gbfplain = new GBFPlain(); + cleanupFilters.push_back(gbfplain); + + thmlplain = new ThMLPlain(); + cleanupFilters.push_back(thmlplain); + + osisplain = new OSISPlain(); + cleanupFilters.push_back(osisplain); + + teiplain = new TEIPlain(); + cleanupFilters.push_back(teiplain); +//#endif +} + + +void SWMgr::commonInit(SWConfig *iconfig, SWConfig *isysconfig, bool autoload, SWFilterMgr *filterMgr, bool multiMod) { + + init(); + + mgrModeMultiMod = multiMod; + this->filterMgr = filterMgr; + if (filterMgr) + filterMgr->setParentMgr(this); + + if (iconfig) { + config = iconfig; + myconfig = 0; + } + else config = 0; + if (isysconfig) { + sysconfig = isysconfig; + mysysconfig = 0; + } + else sysconfig = 0; + + if (autoload) + Load(); +} + + +SWMgr::SWMgr(SWFilterMgr *filterMgr, bool multiMod) { + commonInit(0, 0, true, filterMgr, multiMod); +} + + +SWMgr::SWMgr(SWConfig *iconfig, SWConfig *isysconfig, bool autoload, SWFilterMgr *filterMgr, bool multiMod) { + commonInit(iconfig, isysconfig, autoload, filterMgr, multiMod); +} + + +SWMgr::SWMgr(const char *iConfigPath, bool autoload, SWFilterMgr *filterMgr, bool multiMod, bool augmentHome) { + + init(); + + mgrModeMultiMod = multiMod; + SWBuf path; + + this->filterMgr = filterMgr; + if (filterMgr) + filterMgr->setParentMgr(this); + + this->augmentHome = augmentHome; + + path = iConfigPath; + int len = path.length(); + if ((len < 1) || ((iConfigPath[len-1] != '\\') && (iConfigPath[len-1] != '/'))) + path += "/"; + if (FileMgr::existsFile(path.c_str(), "mods.conf")) { + stdstr(&prefixPath, path.c_str()); + path += "mods.conf"; + stdstr(&configPath, path.c_str()); + } + else { + if (FileMgr::existsDir(path.c_str(), "mods.d")) { + stdstr(&prefixPath, path.c_str()); + path += "mods.d"; + stdstr(&configPath, path.c_str()); + configType = 1; + } + } + + config = 0; + sysconfig = 0; + + if (autoload && configPath) + Load(); +} + + +SWMgr::~SWMgr() { + + DeleteMods(); + + for (FilterList::iterator it = cleanupFilters.begin(); it != cleanupFilters.end(); it++) + delete (*it); + + if (homeConfig) + delete homeConfig; + + if (mysysconfig) + delete mysysconfig; + + if (myconfig) + delete myconfig; + + if (prefixPath) + delete [] prefixPath; + + if (configPath) + delete [] configPath; + + if (filterMgr) + delete filterMgr; +} + + +void SWMgr::findConfig(char *configType, char **prefixPath, char **configPath, std::list<SWBuf> *augPaths, SWConfig *providedSysConf) { + SWBuf path; + SWBuf sysConfPath; + ConfigEntMap::iterator entry; + ConfigEntMap::iterator lastEntry; + + char *envsworddir = getenv("SWORD_PATH"); + char *envhomedir = getenv("HOME"); + SWConfig *sysConf = 0; + + *configType = 0; + + // check for a sysConf passed in to us + SWLog::getSystemLog()->logDebug("Checking for provided SWConfig(\"sword.conf\")..."); + if (providedSysConf) { + sysConf = providedSysConf; + SWLog::getSystemLog()->logDebug("found."); + } + else { + // check working directory + SWLog::getSystemLog()->logDebug("Checking working directory for sword.conf..."); + if (FileMgr::existsFile(".", "sword.conf")) { + SWLog::getSystemLog()->logDebug("Overriding any systemwide or ~/.sword/ sword.conf with one found in current directory."); + sysConfPath = "./sword.conf"; + } + else { + SWLog::getSystemLog()->logDebug("Checking working directory for mods.conf..."); + if (FileMgr::existsFile(".", "mods.conf")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, "./"); + stdstr(configPath, "./mods.conf"); + return; + } + + SWLog::getSystemLog()->logDebug("Checking working directory for mods.d..."); + if (FileMgr::existsDir(".", "mods.d")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, "./"); + stdstr(configPath, "./mods.d"); + *configType = 1; + return; + } + + // check working directory ../library/ + SWLog::getSystemLog()->logDebug("Checking working directory ../library/ for mods.d..."); + if (FileMgr::existsDir("../library", "mods.d")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, "../library/"); + stdstr(configPath, "../library/mods.d"); + *configType = 1; + return; + } + + // check environment variable SWORD_PATH + SWLog::getSystemLog()->logDebug("Checking SWORD_PATH..."); + + if (envsworddir != NULL) { + + SWLog::getSystemLog()->logDebug("found (%s).", envsworddir); + path = envsworddir; + if ((envsworddir[strlen(envsworddir)-1] != '\\') && (envsworddir[strlen(envsworddir)-1] != '/')) + path += "/"; + + SWLog::getSystemLog()->logDebug("Checking $SWORD_PATH for mods.conf..."); + if (FileMgr::existsFile(path.c_str(), "mods.conf")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.conf"; + stdstr(configPath, path.c_str()); + return; + } + + SWLog::getSystemLog()->logDebug("Checking $SWORD_PATH for mods.d..."); + if (FileMgr::existsDir(path.c_str(), "mods.d")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.d"; + stdstr(configPath, path.c_str()); + *configType = 1; + return; + } + } + + + // check for systemwide globalConfPath + + SWLog::getSystemLog()->logDebug("Parsing %s...", globalConfPath); + char *globPaths = 0; + char *gfp; + stdstr(&globPaths, globalConfPath); + for (gfp = strtok(globPaths, ":"); gfp; gfp = strtok(0, ":")) { + SWLog::getSystemLog()->logDebug("Checking for %s...", gfp); + if (FileMgr::existsFile(gfp)) { + SWLog::getSystemLog()->logDebug("found."); + break; + } + } + if (gfp) + sysConfPath = gfp; + delete [] globPaths; + + SWBuf homeDir = envhomedir; + if (homeDir.size() > 0) { + if ((homeDir[homeDir.size()-1] != '\\') && (homeDir[homeDir.size()-1] != '/')) + homeDir += "/"; + homeDir += ".sword/sword.conf"; + if (FileMgr::existsFile(homeDir)) { + SWLog::getSystemLog()->logDebug("Overriding any systemwide sword.conf with one found in users home directory."); + sysConfPath = homeDir; + } + } + } + } + + if (sysConfPath.size()) { + sysConf = new SWConfig(sysConfPath); + } + + if (sysConf) { + if ((entry = sysConf->Sections["Install"].find("DataPath")) != sysConf->Sections["Install"].end()) { + path = (*entry).second; + if (((*entry).second.c_str()[strlen((*entry).second.c_str())-1] != '\\') && ((*entry).second.c_str()[strlen((*entry).second.c_str())-1] != '/')) + path += "/"; + + SWLog::getSystemLog()->logDebug("DataPath in %s is set to %s.", sysConfPath.c_str(), path.c_str()); + SWLog::getSystemLog()->logDebug("Checking for mods.conf in DataPath..."); + + if (FileMgr::existsFile(path.c_str(), "mods.conf")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.conf"; + stdstr(configPath, path.c_str()); + *configType = 1; + } + + SWLog::getSystemLog()->logDebug("Checking for mods.d in DataPath..."); + + if (FileMgr::existsDir(path.c_str(), "mods.d")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.d"; + stdstr(configPath, path.c_str()); + *configType = 1; + } + } + if (augPaths) { + augPaths->clear(); + entry = sysConf->Sections["Install"].lower_bound("AugmentPath"); + lastEntry = sysConf->Sections["Install"].upper_bound("AugmentPath"); + for (;entry != lastEntry; entry++) { + path = entry->second; + if ((entry->second.c_str()[strlen(entry->second.c_str())-1] != '\\') && (entry->second.c_str()[strlen(entry->second.c_str())-1] != '/')) + path += "/"; + augPaths->push_back(path); + } + } + } + + if ((sysConf) && (!providedSysConf)) { + delete sysConf; + } + + if (*configType) + return; + + // check ~/.sword/ + + SWLog::getSystemLog()->logDebug("Checking home directory for ~/.sword..."); + + if (envhomedir != NULL) { + path = envhomedir; + if ((envhomedir[strlen(envhomedir)-1] != '\\') && (envhomedir[strlen(envhomedir)-1] != '/')) + path += "/"; + path += ".sword/"; + SWLog::getSystemLog()->logDebug(" Checking for %smods.conf...", path.c_str()); + if (FileMgr::existsFile(path.c_str(), "mods.conf")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.conf"; + stdstr(configPath, path.c_str()); + return; + } + + SWLog::getSystemLog()->logDebug(" Checking for %smods.d...", path.c_str()); + if (FileMgr::existsDir(path.c_str(), "mods.d")) { + SWLog::getSystemLog()->logDebug("found."); + stdstr(prefixPath, path.c_str()); + path += "mods.d"; + stdstr(configPath, path.c_str()); + *configType = 2; + return; + } + } +} + + +void SWMgr::loadConfigDir(const char *ipath) +{ + DIR *dir; + struct dirent *ent; + SWBuf newmodfile; + + if ((dir = opendir(ipath))) { + rewinddir(dir); + while ((ent = readdir(dir))) { + //check whether it ends with .conf, if it doesn't skip it! + if (ent->d_name && (strlen(ent->d_name) > 5) && strncmp(".conf", (ent->d_name + strlen(ent->d_name) - 5), 5 )) { + continue; + } + + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + newmodfile = ipath; + if ((ipath[strlen(ipath)-1] != '\\') && (ipath[strlen(ipath)-1] != '/')) + newmodfile += "/"; + newmodfile += ent->d_name; + if (config) { + SWConfig tmpConfig(newmodfile.c_str()); + *config += tmpConfig; + } + else config = myconfig = new SWConfig(newmodfile.c_str()); + } + } + closedir(dir); + + if (!config) { // if no .conf file exist yet, create a default + newmodfile = ipath; + if ((ipath[strlen(ipath)-1] != '\\') && (ipath[strlen(ipath)-1] != '/')) + newmodfile += "/"; + newmodfile += "globals.conf"; + config = myconfig = new SWConfig(newmodfile.c_str()); + } + } +} + + +void SWMgr::augmentModules(const char *ipath, bool multiMod) { + SWBuf path = ipath; + if ((ipath[strlen(ipath)-1] != '\\') && (ipath[strlen(ipath)-1] != '/')) + path += "/"; + if (FileMgr::existsDir(path.c_str(), "mods.d")) { + char *savePrefixPath = 0; + char *saveConfigPath = 0; + SWConfig *saveConfig = 0; + stdstr(&savePrefixPath, prefixPath); + stdstr(&prefixPath, path.c_str()); + path += "mods.d"; + stdstr(&saveConfigPath, configPath); + stdstr(&configPath, path.c_str()); + saveConfig = config; + config = myconfig = 0; + loadConfigDir(configPath); + + if (multiMod) { + // fix config's Section names to rename modules which are available more than once + // find out which sections are in both config objects + // inserting all configs first is not good because that overwrites old keys and new modules would share the same config + for (SectionMap::iterator it = config->Sections.begin(); it != config->Sections.end(); ++it) { + if (saveConfig->Sections.find( (*it).first ) != saveConfig->Sections.end()) { //if the new section is already present rename it + ConfigEntMap entMap((*it).second); + + SWBuf name; + int i = 1; + do { //module name already used? + name.setFormatted("%s_%d", (*it).first.c_str(), i); + i++; + } while (config->Sections.find(name) != config->Sections.end()); + + config->Sections.insert(SectionMap::value_type(name, entMap) ); + config->Sections.erase(it); + } + } + } + + CreateMods(multiMod); + + stdstr(&prefixPath, savePrefixPath); + delete []savePrefixPath; + stdstr(&configPath, saveConfigPath); + delete []saveConfigPath; + + (*saveConfig) += *config; + + homeConfig = myconfig; + config = myconfig = saveConfig; + } +} + + +/*********************************************************************** + * SWMgr::Load - loads actual modules + * + * RET: status - 0 = ok; -1 no config found; 1 = no modules installed + * + */ + +signed char SWMgr::Load() { + signed char ret = 0; + + if (!config) { // If we weren't passed a config object at construction, find a config file + if (!configPath) { // If we weren't passed a config path at construction... + SWLog::getSystemLog()->logDebug("LOOKING UP MODULE CONFIGURATION..."); + findConfig(&configType, &prefixPath, &configPath, &augPaths, sysconfig); + SWLog::getSystemLog()->logDebug("LOOKING UP MODULE CONFIGURATION COMPLETE."); + } + if (configPath) { + if (configType) + loadConfigDir(configPath); + else config = myconfig = new SWConfig(configPath); + } + } + + if (config) { + SectionMap::iterator Sectloop, Sectend; + ConfigEntMap::iterator Entryloop, Entryend; + + DeleteMods(); + + for (Sectloop = config->Sections.lower_bound("Globals"), Sectend = config->Sections.upper_bound("Globals"); Sectloop != Sectend; Sectloop++) { // scan thru all 'Globals' sections + for (Entryloop = (*Sectloop).second.lower_bound("AutoInstall"), Entryend = (*Sectloop).second.upper_bound("AutoInstall"); Entryloop != Entryend; Entryloop++) // scan thru all AutoInstall entries + InstallScan((*Entryloop).second.c_str()); // Scan AutoInstall entry directory for new modules and install + } + if (configType) { // force reload on config object because we may have installed new modules + delete myconfig; + config = myconfig = 0; + loadConfigDir(configPath); + } + else config->Load(); + + CreateMods(mgrModeMultiMod); + + for (std::list<SWBuf>::iterator pathIt = augPaths.begin(); pathIt != augPaths.end(); pathIt++) { + augmentModules(pathIt->c_str(), mgrModeMultiMod); + } + if (augmentHome) { + // augment config with ~/.sword/mods.d if it exists --------------------- + char *envhomedir = getenv("HOME"); + if (envhomedir != NULL && configType != 2) { // 2 = user only + SWBuf path = envhomedir; + if ((envhomedir[strlen(envhomedir)-1] != '\\') && (envhomedir[strlen(envhomedir)-1] != '/')) + path += "/"; + path += ".sword/"; + augmentModules(path.c_str(), mgrModeMultiMod); + } + } +// ------------------------------------------------------------------------- + if ( !Modules.size() ) // config exists, but no modules + ret = 1; + + } + else { + SWLog::getSystemLog()->logError("SWMgr: Can't find 'mods.conf' or 'mods.d'. Try setting:\n\tSWORD_PATH=<directory containing mods.conf>\n\tOr see the README file for a full description of setup options (%s)", (configPath) ? configPath : "<configPath is null>"); + ret = -1; + } + + return ret; +} + +SWModule *SWMgr::CreateMod(const char *name, const char *driver, ConfigEntMap §ion) +{ + SWBuf description, datapath, misc1; + ConfigEntMap::iterator entry; + SWModule *newmod = 0; + SWBuf lang, sourceformat, encoding; + signed char direction, enc, markup; + + description = ((entry = section.find("Description")) != section.end()) ? (*entry).second : (SWBuf)""; + lang = ((entry = section.find("Lang")) != section.end()) ? (*entry).second : (SWBuf)"en"; + sourceformat = ((entry = section.find("SourceType")) != section.end()) ? (*entry).second : (SWBuf)""; + encoding = ((entry = section.find("Encoding")) != section.end()) ? (*entry).second : (SWBuf)""; + datapath = prefixPath; + if ((prefixPath[strlen(prefixPath)-1] != '\\') && (prefixPath[strlen(prefixPath)-1] != '/')) + datapath += "/"; + + // DataPath - relative path to data used by module driver. May be a directory, may be a File. + // Typically not useful by outside world. See AbsoluteDataPath, PrefixPath, and RelativePrefixPath + // below. + misc1 += ((entry = section.find("DataPath")) != section.end()) ? (*entry).second : (SWBuf)""; + char *buf = new char [ strlen(misc1.c_str()) + 1 ]; + char *buf2 = buf; + strcpy(buf, misc1.c_str()); +// for (; ((*buf2) && ((*buf2 == '.') || (*buf2 == '/') || (*buf2 == '\\'))); buf2++); + for (; ((*buf2) && ((*buf2 == '/') || (*buf2 == '\\'))); buf2++); + if (!strncmp(buf2, "./", 2)) { //remove the leading ./ in the module data path to make it look better + buf2 += 2; + } + // PrefixPath - absolute directory path to the repository in which this module was found + section["PrefixPath"] = datapath; + if (*buf2) + datapath += buf2; + delete [] buf; + + section["AbsoluteDataPath"] = datapath; + + if (!stricmp(sourceformat.c_str(), "GBF")) + markup = FMT_GBF; + else if (!stricmp(sourceformat.c_str(), "ThML")) + markup = FMT_THML; + else if (!stricmp(sourceformat.c_str(), "OSIS")) + markup = FMT_OSIS; + else if (!stricmp(sourceformat.c_str(), "TEI")) + markup = FMT_TEI; + else + markup = FMT_GBF; + + if (!stricmp(encoding.c_str(), "SCSU")) + enc = ENC_SCSU; + else if (!stricmp(encoding.c_str(), "UTF-8")) { + enc = ENC_UTF8; + } + else enc = ENC_LATIN1; + + if ((entry = section.find("Direction")) == section.end()) { + direction = DIRECTION_LTR; + } + else if (!stricmp((*entry).second.c_str(), "rtol")) { + direction = DIRECTION_RTL; + } + else if (!stricmp((*entry).second.c_str(), "bidi")) { + direction = DIRECTION_BIDI; + } + else { + direction = DIRECTION_LTR; + } + + if ((!stricmp(driver, "zText")) || (!stricmp(driver, "zCom"))) { + SWCompress *compress = 0; + int blockType = CHAPTERBLOCKS; + int blockNum = 1; + misc1 = ((entry = section.find("BlockType")) != section.end()) ? (*entry).second : (SWBuf)"CHAPTER"; + if (!stricmp(misc1.c_str(), "VERSE")) + blockType = VERSEBLOCKS; + else if (!stricmp(misc1.c_str(), "CHAPTER")) + blockType = CHAPTERBLOCKS; + else if (!stricmp(misc1.c_str(), "BOOK")) + blockType = BOOKBLOCKS; + + misc1 = ((entry = section.find("BlockNumber")) != section.end()) ? (*entry).second : (SWBuf)"1"; + blockNum = atoi(misc1.c_str()); + + misc1 = ((entry = section.find("CompressType")) != section.end()) ? (*entry).second : (SWBuf)"LZSS"; +#ifndef EXCLUDEZLIB + if (!stricmp(misc1.c_str(), "ZIP")) + compress = new ZipCompress(); + else +#endif + if (!stricmp(misc1.c_str(), "LZSS")) + compress = new LZSSCompress(); + + if (compress) { + if (!stricmp(driver, "zText")) + newmod = new zText(datapath.c_str(), name, description.c_str(), blockType, compress, 0, enc, direction, markup, lang.c_str()); + else newmod = new zCom(datapath.c_str(), name, description.c_str(), blockType, compress, 0, enc, direction, markup, lang.c_str()); + } + } + + if (!stricmp(driver, "RawText")) { + newmod = new RawText(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + if (!stricmp(driver, "RawText4")) { + newmod = new RawText4(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + // backward support old drivers + if (!stricmp(driver, "RawGBF")) { + newmod = new RawText(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + if (!stricmp(driver, "RawCom")) { + newmod = new RawCom(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + if (!stricmp(driver, "RawCom4")) { + newmod = new RawCom4(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + if (!stricmp(driver, "RawFiles")) { + newmod = new RawFiles(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + } + + if (!stricmp(driver, "HREFCom")) { + misc1 = ((entry = section.find("Prefix")) != section.end()) ? (*entry).second : (SWBuf)""; + newmod = new HREFCom(datapath.c_str(), misc1.c_str(), name, description.c_str()); + } + + int pos = 0; //used for position of final / in AbsoluteDataPath, but also set to 1 for modules types that need to strip module name + if (!stricmp(driver, "RawLD")) { + newmod = new RawLD(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + pos = 1; + } + + if (!stricmp(driver, "RawLD4")) { + newmod = new RawLD4(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str()); + pos = 1; + } + + if (!stricmp(driver, "zLD")) { + SWCompress *compress = 0; + int blockCount; + misc1 = ((entry = section.find("BlockCount")) != section.end()) ? (*entry).second : (SWBuf)"200"; + blockCount = atoi(misc1.c_str()); + blockCount = (blockCount) ? blockCount : 200; + + misc1 = ((entry = section.find("CompressType")) != section.end()) ? (*entry).second : (SWBuf)"LZSS"; +#ifndef EXCLUDEZLIB + if (!stricmp(misc1.c_str(), "ZIP")) + compress = new ZipCompress(); + else +#endif + if (!stricmp(misc1.c_str(), "LZSS")) + compress = new LZSSCompress(); + + if (compress) { + newmod = new zLD(datapath.c_str(), name, description.c_str(), blockCount, compress, 0, enc, direction, markup, lang.c_str()); + } + pos = 1; + } + + if (!stricmp(driver, "RawGenBook")) { + misc1 = ((entry = section.find("KeyType")) != section.end()) ? (*entry).second : (SWBuf)"TreeKey"; + newmod = new RawGenBook(datapath.c_str(), name, description.c_str(), 0, enc, direction, markup, lang.c_str(), misc1.c_str()); + pos = 1; + } + + if (pos == 1) { + SWBuf &dp = section["AbsoluteDataPath"]; + for (int i = dp.length() - 1; i; i--) { + if (dp[i] == '/') { + dp.setSize(i); + break; + } + } +/* + SWBuf &rdp = section["RelativeDataPath"]; + for (int i = rdp.length() - 1; i; i--) { + if (rdp[i] == '/') { + rdp.setSize(i); + break; + } + } +*/ + } + + // if a specific module type is set in the config, use this + if ((entry = section.find("Type")) != section.end()) + newmod->Type(entry->second.c_str()); + + if (newmod){ + newmod->setConfig(§ion); + } + + return newmod; +} + + +void SWMgr::AddGlobalOptions(SWModule *module, ConfigEntMap §ion, ConfigEntMap::iterator start, ConfigEntMap::iterator end) { + for (;start != end; start++) { + OptionFilterMap::iterator it; + it = optionFilters.find((*start).second); + if (it != optionFilters.end()) { + module->AddOptionFilter((*it).second); // add filter to module and option as a valid option + StringList::iterator loop; + for (loop = options.begin(); loop != options.end(); loop++) { + if (!strcmp((*loop).c_str(), (*it).second->getOptionName())) + break; + } + if (loop == options.end()) // if we have not yet included the option + options.push_back((*it).second->getOptionName()); + } + } + if (filterMgr) + filterMgr->AddGlobalOptions(module, section, start, end); +#ifdef _ICU_ + module->AddOptionFilter(transliterator); +#endif +} + + +char SWMgr::filterText(const char *filterName, SWBuf &text, const SWKey *key, const SWModule *module) + { + char retVal = -1; + for (OptionFilterMap::iterator it = optionFilters.begin(); it != optionFilters.end(); it++) { + if ((*it).second->getOptionName()) { + if (!stricmp(filterName, (*it).second->getOptionName())) + retVal = it->second->processText(text, key, module); // add filter to module + } + } + return retVal; +} + + +void SWMgr::AddLocalOptions(SWModule *module, ConfigEntMap §ion, ConfigEntMap::iterator start, ConfigEntMap::iterator end) +{ + for (;start != end; start++) { + OptionFilterMap::iterator it; + it = optionFilters.find((*start).second); + if (it != optionFilters.end()) { + module->AddOptionFilter((*it).second); // add filter to module + } + } + + if (filterMgr) + filterMgr->AddLocalOptions(module, section, start, end); +} + + +// manually specified StripFilters for special cases, like Papyri marks and such +void SWMgr::AddStripFilters(SWModule *module, ConfigEntMap §ion, ConfigEntMap::iterator start, ConfigEntMap::iterator end) +{ + for (;start != end; start++) { + OptionFilterMap::iterator it; + it = optionFilters.find((*start).second); + if (it != optionFilters.end()) { + module->AddStripFilter((*it).second); // add filter to module + } + } +} + + +void SWMgr::AddRawFilters(SWModule *module, ConfigEntMap §ion) { + SWBuf sourceformat, cipherKey; + ConfigEntMap::iterator entry; + + cipherKey = ((entry = section.find("CipherKey")) != section.end()) ? (*entry).second : (SWBuf)""; + if (cipherKey.length()) { + SWFilter *cipherFilter = new CipherFilter(cipherKey.c_str()); + cipherFilters.insert(FilterMap::value_type(module->Name(), cipherFilter)); + cleanupFilters.push_back(cipherFilter); + module->AddRawFilter(cipherFilter); + } + + if (filterMgr) + filterMgr->AddRawFilters(module, section); +} + + +void SWMgr::AddEncodingFilters(SWModule *module, ConfigEntMap §ion) { + if (filterMgr) + filterMgr->AddEncodingFilters(module, section); +} + + +void SWMgr::AddRenderFilters(SWModule *module, ConfigEntMap §ion) { + SWBuf sourceformat; + ConfigEntMap::iterator entry; + + sourceformat = ((entry = section.find("SourceType")) != section.end()) ? (*entry).second : (SWBuf)""; + + // Temporary: To support old module types + // TODO: Remove at 1.6.0 release? + if (!sourceformat.length()) { + sourceformat = ((entry = section.find("ModDrv")) != section.end()) ? (*entry).second : (SWBuf)""; + if (!stricmp(sourceformat.c_str(), "RawGBF")) + sourceformat = "GBF"; + else sourceformat = ""; + } + +// process module - eg. follows +// if (!stricmp(sourceformat.c_str(), "GBF")) { +// module->AddRenderFilter(gbftortf); +// } + + if (filterMgr) + filterMgr->AddRenderFilters(module, section); + +} + + +void SWMgr::AddStripFilters(SWModule *module, ConfigEntMap §ion) +{ + SWBuf sourceformat; + ConfigEntMap::iterator entry; + + sourceformat = ((entry = section.find("SourceType")) != section.end()) ? (*entry).second : (SWBuf)""; + // Temporary: To support old module types + if (!sourceformat.length()) { + sourceformat = ((entry = section.find("ModDrv")) != section.end()) ? (*entry).second : (SWBuf)""; + if (!stricmp(sourceformat.c_str(), "RawGBF")) + sourceformat = "GBF"; + else sourceformat = ""; + } + + if (!stricmp(sourceformat.c_str(), "GBF")) { + module->AddStripFilter(gbfplain); + } + else if (!stricmp(sourceformat.c_str(), "ThML")) { + module->AddStripFilter(thmlplain); + } + else if (!stricmp(sourceformat.c_str(), "OSIS")) { + module->AddStripFilter(osisplain); + } + else if (!stricmp(sourceformat.c_str(), "TEI")) { + module->AddStripFilter(teiplain); + } + + if (filterMgr) + filterMgr->AddStripFilters(module, section); + +} + + +void SWMgr::CreateMods(bool multiMod) { + SectionMap::iterator it; + ConfigEntMap::iterator start; + ConfigEntMap::iterator end; + ConfigEntMap::iterator entry; + SWModule *newmod; + SWBuf driver, misc1; + for (it = config->Sections.begin(); it != config->Sections.end(); it++) { + ConfigEntMap §ion = (*it).second; + newmod = 0; + + driver = ((entry = section.find("ModDrv")) != section.end()) ? (*entry).second : (SWBuf)""; + if (driver.length()) { + newmod = CreateMod((*it).first, driver, section); + if (newmod) { + // Filters to add for this module and globally announce as an option to the user + // e.g. translit, strongs, redletterwords, etc, so users can turn these on and off globally + start = (*it).second.lower_bound("GlobalOptionFilter"); + end = (*it).second.upper_bound("GlobalOptionFilter"); + AddGlobalOptions(newmod, section, start, end); + + // Only add the option to the module, don't announce it's availability + // These are useful for like: filters that parse special entryAttribs in a text + // or whatever you might want to happen on entry lookup + start = (*it).second.lower_bound("LocalOptionFilter"); + end = (*it).second.upper_bound("LocalOptionFilter"); + AddLocalOptions(newmod, section, start, end); + + //STRIP FILTERS + + // add all basic ones for for the modtype + AddStripFilters(newmod, section); + + // Any special processing for this module when searching: + // e.g. for papyri, removed all [](). notation + start = (*it).second.lower_bound("LocalStripFilter"); + end = (*it).second.upper_bound("LocalStripFilter"); + AddStripFilters(newmod, section, start, end); + + AddRawFilters(newmod, section); + AddRenderFilters(newmod, section); + AddEncodingFilters(newmod, section); + + SWModule *oldmod = Modules[newmod->Name()]; + if (oldmod) { + delete oldmod; + } + + Modules[newmod->Name()] = newmod; + } + } + } +} + + +void SWMgr::DeleteMods() { + + ModMap::iterator it; + + for (it = Modules.begin(); it != Modules.end(); it++) + delete (*it).second; + + Modules.clear(); +} + + +void SWMgr::deleteModule(const char *modName) { + ModMap::iterator it = Modules.find(modName); + if (it != Modules.end()) { + delete (*it).second; + Modules.erase(it); + } +} + + +void SWMgr::InstallScan(const char *dirname) +{ + DIR *dir; + struct dirent *ent; + FileDesc *conffd = 0; + SWBuf newmodfile; + SWBuf targetName; + + if (FileMgr::existsDir(dirname)) { + if ((dir = opendir(dirname))) { + rewinddir(dir); + while ((ent = readdir(dir))) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + newmodfile = dirname; + if ((dirname[strlen(dirname)-1] != '\\') && (dirname[strlen(dirname)-1] != '/')) + newmodfile += "/"; + newmodfile += ent->d_name; + + // mods.d + if (configType) { + if (conffd) + FileMgr::getSystemFileMgr()->close(conffd); + targetName = configPath; + if ((configPath[strlen(configPath)-1] != '\\') && (configPath[strlen(configPath)-1] != '/')) + targetName += "/"; + targetName += ent->d_name; + conffd = FileMgr::getSystemFileMgr()->open(targetName.c_str(), FileMgr::WRONLY|FileMgr::CREAT, FileMgr::IREAD|FileMgr::IWRITE); + } + + // mods.conf + else { + if (!conffd) { + conffd = FileMgr::getSystemFileMgr()->open(config->filename.c_str(), FileMgr::WRONLY|FileMgr::APPEND); + if (conffd > 0) + conffd->seek(0L, SEEK_END); + else { + FileMgr::getSystemFileMgr()->close(conffd); + conffd = 0; + } + } + } + AddModToConfig(conffd, newmodfile.c_str()); + FileMgr::removeFile(newmodfile.c_str()); + } + } + if (conffd) + FileMgr::getSystemFileMgr()->close(conffd); + closedir(dir); + } + } +} + + +char SWMgr::AddModToConfig(FileDesc *conffd, const char *fname) +{ + FileDesc *modfd; + char ch; + + SWLog::getSystemLog()->logTimedInformation("Found new module [%s]. Installing...", fname); + modfd = FileMgr::getSystemFileMgr()->open(fname, FileMgr::RDONLY); + ch = '\n'; + conffd->write(&ch, 1); + while (modfd->read(&ch, 1) == 1) + conffd->write(&ch, 1); + ch = '\n'; + conffd->write(&ch, 1); + FileMgr::getSystemFileMgr()->close(modfd); + return 0; +} + + +void SWMgr::setGlobalOption(const char *option, const char *value) +{ + for (OptionFilterMap::iterator it = optionFilters.begin(); it != optionFilters.end(); it++) { + if ((*it).second->getOptionName()) { + if (!stricmp(option, (*it).second->getOptionName())) + (*it).second->setOptionValue(value); + } + } +} + + +const char *SWMgr::getGlobalOption(const char *option) +{ + for (OptionFilterMap::iterator it = optionFilters.begin(); it != optionFilters.end(); it++) { + if ((*it).second->getOptionName()) { + if (!stricmp(option, (*it).second->getOptionName())) + return (*it).second->getOptionValue(); + } + } + return 0; +} + + +const char *SWMgr::getGlobalOptionTip(const char *option) +{ + for (OptionFilterMap::iterator it = optionFilters.begin(); it != optionFilters.end(); it++) { + if ((*it).second->getOptionName()) { + if (!stricmp(option, (*it).second->getOptionName())) + return (*it).second->getOptionTip(); + } + } + return 0; +} + + +StringList SWMgr::getGlobalOptions() +{ + return options; +} + + +StringList SWMgr::getGlobalOptionValues(const char *option) +{ + StringList options; + for (OptionFilterMap::iterator it = optionFilters.begin(); it != optionFilters.end(); it++) { + if ((*it).second->getOptionName()) { + if (!stricmp(option, (*it).second->getOptionName())) { + options = (*it).second->getOptionValues(); + break; // just find the first one. All option filters with the same option name should expect the same values + } + } + } + return options; +} + + +signed char SWMgr::setCipherKey(const char *modName, const char *key) { + FilterMap::iterator it; + ModMap::iterator it2; + + // check for filter that already exists + it = cipherFilters.find(modName); + if (it != cipherFilters.end()) { + ((CipherFilter *)(*it).second)->getCipher()->setCipherKey(key); + return 0; + } + // check if module exists + else { + it2 = Modules.find(modName); + if (it2 != Modules.end()) { + SWFilter *cipherFilter = new CipherFilter(key); + cipherFilters.insert(FilterMap::value_type(modName, cipherFilter)); + cleanupFilters.push_back(cipherFilter); + (*it2).second->AddRawFilter(cipherFilter); + return 0; + } + } + return -1; +} + +SWORD_NAMESPACE_END diff --git a/src/mgr/swsearchable.cpp b/src/mgr/swsearchable.cpp new file mode 100644 index 0000000..48ae556 --- /dev/null +++ b/src/mgr/swsearchable.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * swsearchable.h - definition of class SWSearchable used to provide an + * interface for objects that be searched. + * + * $Id: swsearchable.cpp 1959 2006-08-28 00:39:56Z 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 <swsearchable.h> +#include <listkey.h> + +SWORD_NAMESPACE_START + +void SWSearchable::nullPercent(char percent, void *percentUserData) {} + +SWSearchable::SWSearchable() { +} + + +SWSearchable::~SWSearchable() { +} + + // special search framework +signed char SWSearchable::createSearchFramework(void (*percent)(char, void *), void *percentUserData) { + return 0; +} + + +void SWSearchable::deleteSearchFramework() { +} + + +bool SWSearchable::isSearchOptimallySupported(const char *istr, int searchType, int flags, SWKey *scope) { + bool retVal = false; + search(istr, searchType, flags, scope, &retVal); + return retVal; +} + +SWORD_NAMESPACE_END |