summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRoberto C. Sanchez <roberto@connexer.com>2014-03-29 10:53:59 -0400
committerRoberto C. Sanchez <roberto@connexer.com>2014-03-29 10:53:59 -0400
commit03134fa5f6f25d92724ce4c183f9bbe12a9e37dc (patch)
tree847326a4de82f0241ac87cbbc427a1b92a696a02 /src
parentd7469385b05b9510338407fa123e9ad090f80af6 (diff)
Imported Upstream version 1.5.11
Diffstat (limited to 'src')
-rw-r--r--src/Makefile5
-rw-r--r--src/frontend/Makefile5
-rw-r--r--src/frontend/Makefile.am6
-rw-r--r--src/frontend/X11/Makefile5
-rw-r--r--src/frontend/framework/Makefile4
-rw-r--r--src/frontend/framework/femain.cpp12
-rw-r--r--src/frontend/im/Makefile5
-rw-r--r--src/frontend/im/hebrewmcim.cpp653
-rw-r--r--src/frontend/im/nullim.cpp11
-rw-r--r--src/frontend/im/swinputmeth.cpp26
-rw-r--r--src/frontend/swdisp.cpp35
-rw-r--r--src/frontend/swlog.cpp114
-rw-r--r--src/frontend/windoze/Makefile5
-rw-r--r--src/keys/Makefile4
-rw-r--r--src/keys/Makefile.am10
-rw-r--r--src/keys/genarray.c33
-rw-r--r--src/keys/listkey.cpp332
-rw-r--r--src/keys/nt.bksbin0 -> 112 bytes
-rw-r--r--src/keys/nt.cpsbin0 -> 1152 bytes
-rw-r--r--src/keys/ot.bksbin0 -> 160 bytes
-rw-r--r--src/keys/ot.cpsbin0 -> 3876 bytes
-rw-r--r--src/keys/strkey.cpp42
-rw-r--r--src/keys/swkey.cpp216
-rw-r--r--src/keys/treekey.cpp96
-rw-r--r--src/keys/treekeyidx.cpp643
-rw-r--r--src/keys/versekey.cpp1664
-rw-r--r--src/keys/versetreekey.cpp72
-rw-r--r--src/mgr/Makefile4
-rw-r--r--src/mgr/Makefile.am33
-rw-r--r--src/mgr/curlftpt.cpp159
-rw-r--r--src/mgr/encfiltmgr.cpp154
-rw-r--r--src/mgr/filemgr.cpp566
-rw-r--r--src/mgr/ftplibftpt.cpp108
-rw-r--r--src/mgr/ftptrans.cpp170
-rw-r--r--src/mgr/installmgr.cpp579
-rw-r--r--src/mgr/localemgr.cpp257
-rw-r--r--src/mgr/markupfiltmgr.cpp295
-rw-r--r--src/mgr/stringmgr.cpp280
-rw-r--r--src/mgr/swcacher.cpp47
-rw-r--r--src/mgr/swconfig.cpp155
-rw-r--r--src/mgr/swfiltermgr.cpp93
-rw-r--r--src/mgr/swlocale.cpp204
-rw-r--r--src/mgr/swmgr.cpp1275
-rw-r--r--src/mgr/swsearchable.cpp53
-rw-r--r--src/modules/Makefile5
-rw-r--r--src/modules/Makefile.am10
-rw-r--r--src/modules/comments/Makefile5
-rw-r--r--src/modules/comments/Makefile.am9
-rw-r--r--src/modules/comments/hrefcom/Makefile5
-rw-r--r--src/modules/comments/hrefcom/Makefile.am2
-rw-r--r--src/modules/comments/hrefcom/hrefcom.cpp82
-rw-r--r--src/modules/comments/rawcom/Makefile5
-rw-r--r--src/modules/comments/rawcom/Makefile.am2
-rw-r--r--src/modules/comments/rawcom/rawcom.cpp153
-rw-r--r--src/modules/comments/rawcom4/Makefile.am2
-rw-r--r--src/modules/comments/rawcom4/rawcom4.cpp153
-rw-r--r--src/modules/comments/rawfiles/Makefile5
-rw-r--r--src/modules/comments/rawfiles/Makefile.am2
-rw-r--r--src/modules/comments/rawfiles/rawfiles.cpp257
-rw-r--r--src/modules/comments/swcom.cpp105
-rw-r--r--src/modules/comments/zcom/Makefile5
-rw-r--r--src/modules/comments/zcom/Makefile.am2
-rw-r--r--src/modules/comments/zcom/zcom.cpp188
-rw-r--r--src/modules/common/Makefile4
-rw-r--r--src/modules/common/Makefile.am21
-rw-r--r--src/modules/common/compress.cpp.txt767
-rw-r--r--src/modules/common/entriesblk.cpp172
-rw-r--r--src/modules/common/lzsscomprs.cpp668
-rw-r--r--src/modules/common/rawstr.cpp565
-rw-r--r--src/modules/common/rawstr4.cpp572
-rw-r--r--src/modules/common/rawverse.cpp350
-rw-r--r--src/modules/common/rawverse4.cpp350
-rw-r--r--src/modules/common/sapphire.cpp216
-rw-r--r--src/modules/common/swcipher.cpp128
-rw-r--r--src/modules/common/swcomprs.cpp193
-rw-r--r--src/modules/common/swcomprs.doc802
-rw-r--r--src/modules/common/zipcomprs.cpp164
-rw-r--r--src/modules/common/zstr.cpp731
-rw-r--r--src/modules/common/zverse.cpp538
-rw-r--r--src/modules/filters/Makefile5
-rw-r--r--src/modules/filters/Makefile.am105
-rw-r--r--src/modules/filters/cipherfil.cpp46
-rw-r--r--src/modules/filters/gbffootnotes.cpp193
-rw-r--r--src/modules/filters/gbfheadings.cpp87
-rw-r--r--src/modules/filters/gbfhtml.cpp181
-rw-r--r--src/modules/filters/gbfhtmlhref.cpp288
-rw-r--r--src/modules/filters/gbfmorph.cpp77
-rw-r--r--src/modules/filters/gbfosis.cpp420
-rw-r--r--src/modules/filters/gbfplain.cpp97
-rw-r--r--src/modules/filters/gbfredletterwords.cpp93
-rw-r--r--src/modules/filters/gbfrtf.cpp311
-rw-r--r--src/modules/filters/gbfstrongs.cpp126
-rw-r--r--src/modules/filters/gbfthml.cpp216
-rw-r--r--src/modules/filters/gbfwebif.cpp191
-rw-r--r--src/modules/filters/gbfwordjs.cpp282
-rw-r--r--src/modules/filters/greeklexattribs.cpp101
-rw-r--r--src/modules/filters/latin1utf16.cpp119
-rw-r--r--src/modules/filters/latin1utf8.cpp173
-rw-r--r--src/modules/filters/osisfootnotes.cpp157
-rw-r--r--src/modules/filters/osisheadings.cpp144
-rw-r--r--src/modules/filters/osishtmlhref.cpp561
-rw-r--r--src/modules/filters/osislemma.cpp85
-rw-r--r--src/modules/filters/osismorph.cpp85
-rw-r--r--src/modules/filters/osismorphsegmentation.cpp106
-rw-r--r--src/modules/filters/osisosis.cpp173
-rw-r--r--src/modules/filters/osisplain.cpp192
-rw-r--r--src/modules/filters/osisredletterwords.cpp85
-rw-r--r--src/modules/filters/osisrtf.cpp520
-rw-r--r--src/modules/filters/osisscripref.cpp100
-rw-r--r--src/modules/filters/osisstrongs.cpp257
-rw-r--r--src/modules/filters/osisvariants.cpp118
-rw-r--r--src/modules/filters/osiswebif.cpp198
-rw-r--r--src/modules/filters/osiswordjs.cpp178
-rw-r--r--src/modules/filters/papyriplain.cpp71
-rw-r--r--src/modules/filters/plainfootnotes.cpp79
-rw-r--r--src/modules/filters/plainhtml.cpp83
-rw-r--r--src/modules/filters/rtfhtml.cpp81
-rw-r--r--src/modules/filters/scsuutf8.cpp226
-rw-r--r--src/modules/filters/swbasicfilter.cpp406
-rw-r--r--src/modules/filters/swoptfilter.cpp47
-rw-r--r--src/modules/filters/teihtmlhref.cpp205
-rw-r--r--src/modules/filters/teiplain.cpp116
-rw-r--r--src/modules/filters/teirtf.cpp182
-rw-r--r--src/modules/filters/thmlfootnotes.cpp124
-rw-r--r--src/modules/filters/thmlgbf.cpp291
-rw-r--r--src/modules/filters/thmlheadings.cpp153
-rw-r--r--src/modules/filters/thmlhtml.cpp236
-rw-r--r--src/modules/filters/thmlhtmlhref.cpp357
-rw-r--r--src/modules/filters/thmllemma.cpp65
-rw-r--r--src/modules/filters/thmlmorph.cpp65
-rw-r--r--src/modules/filters/thmlosis.cpp575
-rw-r--r--src/modules/filters/thmlplain.cpp219
-rw-r--r--src/modules/filters/thmlrtf.cpp346
-rw-r--r--src/modules/filters/thmlscripref.cpp123
-rw-r--r--src/modules/filters/thmlstrongs.cpp146
-rw-r--r--src/modules/filters/thmlvariants.cpp118
-rw-r--r--src/modules/filters/thmlwebif.cpp103
-rw-r--r--src/modules/filters/thmlwordjs.cpp296
-rw-r--r--src/modules/filters/unicodertf.cpp87
-rw-r--r--src/modules/filters/utf16utf8.cpp90
-rw-r--r--src/modules/filters/utf8arshaping.cpp51
-rw-r--r--src/modules/filters/utf8bidireorder.cpp60
-rw-r--r--src/modules/filters/utf8cantillation.cpp55
-rw-r--r--src/modules/filters/utf8greekaccents.cpp261
-rw-r--r--src/modules/filters/utf8hebrewpoints.cpp44
-rw-r--r--src/modules/filters/utf8html.cpp70
-rw-r--r--src/modules/filters/utf8latin1.cpp75
-rw-r--r--src/modules/filters/utf8nfc.cpp50
-rw-r--r--src/modules/filters/utf8nfkd.cpp52
-rw-r--r--src/modules/filters/utf8transliterator.cpp888
-rw-r--r--src/modules/filters/utf8utf16.cpp78
-rw-r--r--src/modules/genbook/Makefile5
-rw-r--r--src/modules/genbook/Makefile.am5
-rw-r--r--src/modules/genbook/rawgenbook/Makefile4
-rw-r--r--src/modules/genbook/rawgenbook/Makefile.am4
-rw-r--r--src/modules/genbook/rawgenbook/rawgenbook.cpp219
-rw-r--r--src/modules/genbook/swgenbook.cpp29
-rw-r--r--src/modules/lexdict/Makefile5
-rw-r--r--src/modules/lexdict/Makefile.am7
-rw-r--r--src/modules/lexdict/rawld/Makefile5
-rw-r--r--src/modules/lexdict/rawld/Makefile.am4
-rw-r--r--src/modules/lexdict/rawld/rawld.cpp194
-rw-r--r--src/modules/lexdict/rawld4/Makefile5
-rw-r--r--src/modules/lexdict/rawld4/Makefile.am4
-rw-r--r--src/modules/lexdict/rawld4/rawld4.cpp192
-rw-r--r--src/modules/lexdict/swld.cpp82
-rw-r--r--src/modules/lexdict/zld/Makefile5
-rw-r--r--src/modules/lexdict/zld/Makefile.am4
-rw-r--r--src/modules/lexdict/zld/zld.cpp189
-rw-r--r--src/modules/readme9
-rw-r--r--src/modules/swmodule.cpp1285
-rw-r--r--src/modules/tests/Makefile4
-rw-r--r--src/modules/tests/echomod.cpp27
-rw-r--r--src/modules/texts/Makefile5
-rw-r--r--src/modules/texts/Makefile.am7
-rw-r--r--src/modules/texts/rawtext/Makefile5
-rw-r--r--src/modules/texts/rawtext/Makefile.am2
-rw-r--r--src/modules/texts/rawtext/rawtext.cpp548
-rw-r--r--src/modules/texts/rawtext4/Makefile.am2
-rw-r--r--src/modules/texts/rawtext4/rawtext4.cpp548
-rw-r--r--src/modules/texts/swtext.cpp113
-rw-r--r--src/modules/texts/ztext/Makefile5
-rw-r--r--src/modules/texts/ztext/Makefile.am3
-rw-r--r--src/modules/texts/ztext/ztext.cpp195
-rw-r--r--src/utilfuns/Makefile4
-rw-r--r--src/utilfuns/Makefile.am40
-rw-r--r--src/utilfuns/ftplib.c1355
-rw-r--r--src/utilfuns/ftpparse.c447
-rw-r--r--src/utilfuns/regex.c5721
-rw-r--r--src/utilfuns/roman.cpp87
-rw-r--r--src/utilfuns/sub.c36
-rw-r--r--src/utilfuns/swbuf.cpp167
-rw-r--r--src/utilfuns/swobject.cpp46
-rw-r--r--src/utilfuns/swunicod.cpp138
-rw-r--r--src/utilfuns/swversion.cpp83
-rw-r--r--src/utilfuns/url.cpp269
-rw-r--r--src/utilfuns/utilstr.cpp241
-rw-r--r--src/utilfuns/utilxml.cpp296
-rw-r--r--src/utilfuns/win32/dirent.cpp131
-rw-r--r--src/utilfuns/win32/dirent.h32
-rw-r--r--src/utilfuns/zlib/adler32.c48
-rw-r--r--src/utilfuns/zlib/compress.c68
-rw-r--r--src/utilfuns/zlib/crc32.c162
-rw-r--r--src/utilfuns/zlib/deflate.c1350
-rw-r--r--src/utilfuns/zlib/deflate.h318
-rw-r--r--src/utilfuns/zlib/gzio.c875
-rw-r--r--src/utilfuns/zlib/infblock.c403
-rw-r--r--src/utilfuns/zlib/infblock.h39
-rw-r--r--src/utilfuns/zlib/infcodes.c251
-rw-r--r--src/utilfuns/zlib/infcodes.h27
-rw-r--r--src/utilfuns/zlib/inffast.c183
-rw-r--r--src/utilfuns/zlib/inffast.h17
-rw-r--r--src/utilfuns/zlib/inffixed.h151
-rw-r--r--src/utilfuns/zlib/inflate.c366
-rw-r--r--src/utilfuns/zlib/inftrees.c454
-rw-r--r--src/utilfuns/zlib/inftrees.h58
-rw-r--r--src/utilfuns/zlib/infutil.c87
-rw-r--r--src/utilfuns/zlib/infutil.h98
-rw-r--r--src/utilfuns/zlib/maketree.c85
-rw-r--r--src/utilfuns/zlib/trees.c1214
-rw-r--r--src/utilfuns/zlib/trees.h128
-rw-r--r--src/utilfuns/zlib/uncompr.c58
-rw-r--r--src/utilfuns/zlib/untgz.c436
-rw-r--r--src/utilfuns/zlib/zutil.c225
-rw-r--r--src/utilfuns/zlib/zutil.h220
225 files changed, 48446 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..fc04b62
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,5 @@
+
+root := ..
+
+all:
+ make -C ${root}
diff --git a/src/frontend/Makefile b/src/frontend/Makefile
new file mode 100644
index 0000000..ef8eccd
--- /dev/null
+++ b/src/frontend/Makefile
@@ -0,0 +1,5 @@
+
+root := ../..
+
+all:
+ make -C ${root}
diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am
new file mode 100644
index 0000000..df82518
--- /dev/null
+++ b/src/frontend/Makefile.am
@@ -0,0 +1,6 @@
+frontenddir = $(top_srcdir)/src/frontend
+
+libsword_la_SOURCES += $(frontenddir)/swdisp.cpp
+libsword_la_SOURCES += $(frontenddir)/swlog.cpp
+
+
diff --git a/src/frontend/X11/Makefile b/src/frontend/X11/Makefile
new file mode 100644
index 0000000..1a2d00d
--- /dev/null
+++ b/src/frontend/X11/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/frontend/framework/Makefile b/src/frontend/framework/Makefile
new file mode 100644
index 0000000..81f7721
--- /dev/null
+++ b/src/frontend/framework/Makefile
@@ -0,0 +1,4 @@
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/frontend/framework/femain.cpp b/src/frontend/framework/femain.cpp
new file mode 100644
index 0000000..415ab6f
--- /dev/null
+++ b/src/frontend/framework/femain.cpp
@@ -0,0 +1,12 @@
+#include <femain.h>
+
+FEMain::FEMain() {
+}
+
+FEMain::~FEMain() {
+ list <SWDisplay *>::iterator it;
+
+ for (it = displays.begin(); it != displays.end(); it++)
+ delete *it;
+
+}
diff --git a/src/frontend/im/Makefile b/src/frontend/im/Makefile
new file mode 100644
index 0000000..1a2d00d
--- /dev/null
+++ b/src/frontend/im/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/frontend/im/hebrewmcim.cpp b/src/frontend/im/hebrewmcim.cpp
new file mode 100644
index 0000000..9ec55a9
--- /dev/null
+++ b/src/frontend/im/hebrewmcim.cpp
@@ -0,0 +1,653 @@
+
+/**
+ * Title: Keyboard mapping for Michigan-Claremont Hebrew input
+ * Description:
+ * Copyright: Copyright (c) 2001 CrossWire Bible Society under the terms of the GNU GPL
+ * Company:
+ * @author Troy A. Griffitts
+ * @version 1.0
+ */
+
+#include <hebrewmcim.h>
+
+HebrewMCIM::HebrewMCIM()
+ :SWInputMethod() {
+
+ init();
+}
+
+
+int *HebrewMCIM::translate(char in) {
+ int retVal = 0;
+ static int retString[5];
+ int retStringIndex = 0;
+
+ memset(retString, 0, 5);
+
+ if (getState() > 1) {
+ if (getState() >= 12) { // serious issue with internal structure
+ setState(0);
+ retString[retStringIndex++] = in;
+ return retString;
+ }
+ map<int, int>::iterator find = subst2[getState()].find(in);
+ if (find != subst2[getState()].end())
+ retVal = find->second;
+ else retVal = in;
+
+ setState(0);
+ retString[retStringIndex++] = retVal;
+ return retString;
+ }
+ else {
+ retVal = subst[in];
+
+ if (retVal == 0) {
+ setState(0);
+ retString[retStringIndex++] = in;
+ return retString;
+ }
+ if (retVal > 100) {
+ setState(1);
+ retString[retStringIndex++] = retVal;
+ return retString;
+ }
+ if (retVal == 50) { // multiChar
+ setState(1);
+ int *chars = multiChars[in];
+ if (chars != 0) {
+ retString[retStringIndex++] = chars[0];
+ retString[retStringIndex++] = chars[1];
+ return retString;
+ }
+ }
+ }
+ setState(retVal);
+ return 0;
+}
+
+
+void HebrewMCIM::init() {
+ memset(subst, 0, 255);
+
+ subst[')'] = 1488;
+ subst['B'] = 1489;
+ subst['G'] = 1490;
+ subst['D'] = 1491;
+ subst['H'] = 1492;
+ subst['W'] = 1493;
+ subst['Z'] = 1494;
+ subst['X'] = 1495;
+ subst['+'] = 1496;
+ subst['Y'] = 1497;
+
+ subst['k'] = 1498; // finals
+ subst['m'] = 1501;
+ subst['n'] = 1503;
+ subst['c'] = 1509;
+
+ subst['P'] = 1508;
+ subst['K'] = 1499;
+ subst['L'] = 1500;
+ subst['M'] = 1502;
+ subst['N'] = 1504;
+ subst['S'] = 1505;
+ subst['('] = 1506;
+ subst['p'] = 1507;
+ subst['C'] = 1510;
+ subst['Q'] = 1511;
+ subst['R'] = 1512;
+ subst['#'] = 1513;
+
+ // special multiChars
+ subst['&'] = 50;
+ subst['$'] = 50;
+
+ static int x[] = {1513, 1474};
+ multiChars['&'] = x;
+ static int y[] = {1513, 1473};
+ multiChars['$'] = y;
+
+ subst['T'] = 1514;
+
+ // VOWELS
+ subst['A'] = 1463;
+ subst['F'] = 1464;
+ subst['E'] = 1462;
+ subst['"'] = 1461;
+ subst['I'] = 1460;
+ subst['O'] = 1465;
+ subst['U'] = 1467;
+
+
+
+ // OTHER DIACRITICS
+ subst['.'] = 1468;
+ subst['-'] = 1470;
+ subst[','] = 1471;
+
+ // Compound input
+
+ // CANTILLATION
+
+ subst[':'] = 2;
+ subst2[2]['A'] = 1458;
+ subst2[2]['E'] = 1457;
+ subst2[2]['F'] = 1459;
+
+
+ /* Telisha qetana is postpositive as in '04' above. However, Michigan
+# code '24' is for a medial telisha. Graphically, there is no
+# difference.
+ */
+ subst['2'] = 5;
+ subst2[5]['4'] = 1449;
+
+
+ /* Note Michigan encoding distinguishes between medial metheg '35' (occuring
+# on the left of the vowel), and the ordinary meteg '95' (occuring on the
+# right of the vowel). It is also used for silluq.
+ */
+ subst['3'] = 6;
+ subst2[6]['3'] = 1433;
+ subst2[6]['5'] = 1469;
+
+
+ /* The Michigan code of telisha gedola in medial position. Graphically,
+# there is no difference.
+ */
+ subst['4'] = 7;
+ subst2[7]['4'] = 1440;
+
+ subst['6'] = 8;
+ subst2[8]['0'] = 1451;
+ subst2[8]['1'] = 1436;
+
+ subst['1'] = 4;
+ subst2[4]['0'] = 1434;
+
+ /* In the poetic books, prepositive dehi occurs; it's unclear whether
+# tipeha also occurs in the poetic books. Otherwise, we could simply
+# check for what book in the Tanach we are in. Michigan uses the same
+# code for each.
+ */
+
+ subst2[4]['3'] = 1430;
+
+ /* This is the poetic accent mugrash, which also includes rebia, but is
+# encoded separately as '81' in the Michigan text.
+ */
+ subst2[4]['1'] = 1437;
+ subst2[4]['4'] = 1440;
+
+
+ subst['0'] = 3;
+ subst2[3]['0'] = 1475;
+ subst2[3]['1'] = 1426;
+
+ /* According to BHS, zarqa and sinnor are both postpositive. However,
+# the Michigan encoding uses one code for both. The Unicode zarqa
+# (0x0598) is definitely NOT postpositive. And further, the shape of
+# the symbol is different in BHS and Uniocde. This needs further
+# research to determine what's going on here. For now, we follow BHS
+# and use the postpositive Unicode zinor or both accents.
+ */
+
+ subst2[3]['2'] = 1454;
+
+ /* Pashta is postpositive, and the Unicode equivalent reflects
+# this. However, there is a poetic equivalent -- azla legarmeh --
+# which is not postpositive, but no equivalent code point exists in
+# Unicode. The Michigan encoding does not distinguish between the two,
+# although it could be algorithmically determined.
+ */
+
+ subst2[3]['3'] = 1433;
+ subst2[3]['4'] = 1449;
+ subst2[3]['5'] = 1472;
+
+
+ /* This is the Unicode Hebrew *accent*; there is also another Hebrew
+# *punctuation* called GERSHAYIM 0x05F4. I'm using the more
+# traditional rounded marks, rather than the alternate straight
+# marks.
+ */
+
+ subst2[8]['2'] = 1438;
+
+ // Also known as azla
+ subst2[8]['3'] = 1448;
+ subst2[8]['4'] = 1452;
+ subst2[8]['5'] = 1427;
+
+
+ subst['8'] = 9;
+ subst2[9]['0'] = 1428;
+ subst2[9]['1'] = 1431;
+
+ /* Note, this accent is actually sinnorit, but it does not exist as a
+# separate glyph in the Unicode standard. The 'ZINOR' Unicode accent
+# is postpositive, while sinnorit is not. ZARQA is as close as I can
+# get to this.
+ */
+ subst2[9]['2'] = 1432;
+
+ /* The Unicode form does not match the form used by BHS, but the names
+# are the same.
+ */
+ subst2[9]['3'] = 1441;
+ subst2[9]['4'] = 1439;
+ subst2[9]['5'] = 1429;
+
+ subst['7'] = 10;
+ subst2[10]['0'] = 1444;
+ subst2[10]['1'] = 1445;
+ subst2[10]['2'] = 1446;
+ subst2[10]['3'] = 1430; // also '13', '73' also is used for majela
+ subst2[10]['4'] = 1443;
+ subst2[10]['5'] = 1469; // this is silluq; should appear to the left of the vowel
+
+ subst['9'] = 11;
+ subst2[11]['1'] = 1435;
+ subst2[11]['2'] = 1425;
+ subst2[11]['3'] = 1450;
+ subst2[11]['4'] = 1447;
+ subst2[11]['5'] = 1469; // should appear to the right of the vowel
+
+}
+
+ /*
+
+
+# CANTILLION MARKS
+
+ my $ETNAHTA = '&#1425;';
+# officially the Unicode name for this symbol was "SEGOL." However, that is
+# not a unique name, conflicting with the vowel of the same name. Further,
+# the position of the symbol is different. I have changed the name of the
+# accent to "SEGOLTA," the traditional name for this accent.
+ my $SEGOLTA = '&#1426;';
+ my $SHALSHELET = '&#1427;';
+ my $ZAQEF_QATAN = '&#1428;';
+ my $ZAQEF_GADOL = '&#1429;';
+ my $TIPEHA = '&#1430;';
+ my $REVIA = '&#1431;';
+ my $ZARQA = '&#1432;';
+ my $PASHTA = '&#1433;';
+ my $YETIV = '&#1434;';
+ my $TEVIR = '&#1435;';
+ my $GERESH = '&#1436;';
+ my $GERESH_MUQDAM = '&#1437;';
+ my $GERSHAYIM = '&#1438;';
+ my $QARNEY_PARA = '&#1439;';
+ my $TELISHA_GEDOLA = '&#1440;';
+ my $PAZER = '&#1441;';
+ my $MUNAH = '&#1443;';
+ my $MAHAPAKH = '&#1444;';
+ my $MERKHA = '&#1445;';
+ my $MERKHA_KEFULA = '&#1446;';
+ my $DARGA = '&#1447;';
+ my $QADMA = '&#1448;';
+ my $TELISHA_QETANA = '&#1449;';
+ my $YERAH_BEN_YOMO = '&#1450;';
+ my $OLE = '&#1451;';
+ my $ILUY = '&#1452;';
+ my $DEHI = '&#1453;';
+ my $ZINOR = '&#1454;';
+# HEBREW MARK
+ my $MASORA_CIRCLE = '&#1455;';
+# HEBREW EXTENDED-A points and punctuation
+ my $SHEVA = '&#1456;';
+ my $HATAF_SEGOL = '&#1457;';
+ my $HATAF_PATAH = '&#1458;';
+ my $HATAF_QAMATS = '&#1459;';
+ my $HIRIQ = '&#1460;';
+ my $TSERE = '&#1461;';
+ my $SEGOL = '&#1462;';
+# furtive Patah is not a distinct character
+ my $PATAH = '&#1463;';
+ my $QAMATS = '&#1464;';
+ my $HOLAM = '&#1465;';
+ my $QUBUTS = '&#1467;';
+# also used as shuruq
+# falls within the base letter
+ my $DAGESH_OR_MAPIQ = '&#1468;';
+# also used as siluq
+ my $METAG = '&#1469;';
+ my $MAQAF = '&#1470;';
+ my $RAFE = '&#1471;';
+# Also used for legarmeh
+# may be treated as spacing punctuation, not as a point
+ my $PASEQ = '&#1472;';
+ my $SHIN_DOT = '&#1473;';
+ my $SIN_DOT = '&#1474;';
+ my $SOF_PASUQ = '&#1475;';
+# HEBREW MARK
+ my $UPPER_DOT = '&#1476;';
+# HEBREW LETTERS based on ISO 8859-8
+# aleph
+# x (alef symbol - 2135)
+ my $ALEF = '&#1488;';
+# x (bet symbol - 2136)
+ my $BET = '&#1489;';
+# x (gimel symbol - 2137)
+ my $GIMEL = '&#1490;';
+# x (dalet symbol - 2138)
+ my $DALET = '&#1491;';
+ my $HE = '&#1492;';
+ my $VAV = '&#1493;';
+ my $ZAYIN = '&#1494;';
+ my $HET = '&#1495;';
+ my $TET = '&#1496;';
+ my $YOD = '&#1497;';
+ my $FINAL_KAF = '&#1498;';
+ my $KAF = '&#1499;';
+ my $LAMED = '&#1500;';
+ my $FINAL_MEM = '&#1501;';
+ my $MEM = '&#1502;';
+ my $FINAL_NUN = '&#1503;';
+ my $NUN = '&#1504;';
+ my $SAMEKH = '&#1505;';
+ my $AYIN = '&#1506;';
+ my $FINAL_PE = '&#1507;';
+ my $PE = '&#1508;';
+ my $FINAL_TSADI = '&#1509;';
+# also known as zade
+ my $TSADI = '&#1510;';
+ my $QOF = '&#1511;';
+ my $RESH = '&#1512;';
+ my $SHIN = '&#1513;';
+ my $TAV = '&#1514;';
+# Yiddish digraphs
+# Hebrew Ligature
+# tsvey vovn
+ my $DOUBLE_VAV = '&#1520;';
+ my $VAV_YOD = '&#1521;';
+# tsvey yudn
+ my $DOUBLE_YOD = '&#1522;';
+
+# Additional punctuation
+ my $PUNCT_GERESH = '&#1523;';
+ my $PUNCT_GERSHAYIM = '&#1524;';
+# Reserved: 0x05F5"
+# x (hebrew point judeo-spanish varika - FB1E)
+#my $JUDEO_SPANISH_VARIKA = pack("U",0xFB1E); # UTF-8 OxFB1E
+
+#############################
+# End of Unicode 2.0 Hebrew #
+#############################
+
+# A hash whose key is a Michagan code, and whose value is a Unicode
+# equvalent
+
+ char subst[] = new char [255];
+ subst[')'] = 1488;
+ 'B' => $BET,
+ 'G' => $GIMEL,
+ 'D' => $DALET,
+ 'H' => $HE,
+ 'W' => $VAV,
+ 'Z' => $ZAYIN,
+ 'X' => $HET,
+ '+' => $TET,
+ 'Y' => $YOD,
+ 'K' => $KAF,
+ 'L' => $LAMED,
+ 'M' => $MEM,
+ 'N' => $NUN,
+ 'S' => $SAMEKH,
+ '(' => $AYIN,
+ 'P' => $PE,
+ 'C' => $TSADI,
+ 'Q' => $QOF,
+ 'R' => $RESH,
+ '#' => $SHIN, # the letter shin without a point
+ '&' => ($SHIN . $SIN_DOT),
+ '$' => ($SHIN . $SHIN_DOT), # '
+ 'T' => $TAV,
+# VOWELS
+ 'A' => $PATAH,
+ 'F' => $QAMATS,
+ 'E' => $SEGOL,
+ '"' => $TSERE,
+ 'I' => $HIRIQ,
+ 'O' => $HOLAM,
+ 'U' => $QUBUTS,
+ ':' => $SHEVA,
+ ':A' => $HATAF_PATAH,
+ ':E' => $HATAF_SEGOL,
+ ':F' => $HATAF_QAMATS,
+# OTHER DIACRITICS
+ '.' => $DAGESH_OR_MAPIQ,
+ '-' => $MAQAF,
+ ',' => $RAFE,
+# CANTILLATION
+ '00' => $SOF_PASUQ,
+ '01' => $SEGOLTA,
+# According to BHS, zarqa and sinnor are both postpositive. However,
+# the Michigan encoding uses one code for both. The Unicode zarqa
+# (0x0598) is definitely NOT postpositive. And further, the shape of
+# the symbol is different in BHS and Uniocde. This needs further
+# research to determine what's going on here. For now, we follow BHS
+# and use the postpositive Unicode zinor or both accents.
+ '02' => $ZINOR,
+# Pashta is postpositive, and the Unicode equivalent reflects
+# this. However, there is a poetic equivalent -- azla legarmeh --
+# which is not postpositive, but no equivalent code point exists in
+# Unicode. The Michigan encoding does not distinguish between the two,
+# although it could be algorithmically determined.
+ '03' => $PASHTA,
+ '04' => $TELISHA_QETANA,
+ '05' => $PASEQ,
+ '10' => $YETIV,
+# In the poetic books, prepositive dehi occurs; it's unclear whether
+# tipeha also occurs in the poetic books. Otherwise, we could simply
+# check for what book in the Tanach we are in. Michigan uses the same
+# code for each.
+ '13' => $TIPEHA, # also $DEHI
+# This is the poetic accent mugrash, which also includes rebia, but is
+# encoded separately as '81' in the Michigan text.
+ '11' => $GERESH_MUQDAM,
+ '14' => $TELISHA_GEDOLA,
+# Telisha qetana is postpositive as in '04' above. However, Michigan
+# code '24' is for a medial telisha. Graphically, there is no
+# difference.
+ '24' => $TELISHA_QETANA,
+ '33' => $PASHTA,
+# The Michigan code of telisha gedola in medial position. Graphically,
+# there is no difference.
+ '44' => $TELISHA_GEDOLA,
+ '60' => $OLE,
+ '61' => $GERESH,
+# This is the Unicode Hebrew *accent*; there is also another Hebrew
+# *punctuation* called GERSHAYIM 0x05F4. I'm using the more
+# traditional rounded marks, rather than the alternate straight
+# marks.
+ '62' => $GERSHAYIM,
+# Also known as azla
+ '63' => $QADMA,
+ '64' => $ILUY,
+ '65' => $SHALSHELET,
+ '80' => $ZAQEF_QATAN,
+ '81' => $REVIA,
+# Note, this accent is actually sinnorit, but it does not exist as a
+# separate glyph in the Unicode standard. The 'ZINOR' Unicode accent
+# is postpositive, while sinnorit is not. ZARQA is as close as I can
+# get to this.
+ '82' => $ZARQA,
+# The Unicode form does not match the form used by BHS, but the names
+# are the same.
+ '83' => $PAZER,
+ '84' => $QARNEY_PARA,
+ '85' => $ZAQEF_GADOL,
+# Note Michigan encoding distinguishes between medial metheg '35' (occuring
+# on the left of the vowel), and the ordinary meteg '95' (occuring on the
+# right of the vowel). It is also used for silluq.
+ '35' => $METAG,
+ '70' => $MAHAPAKH,
+ '71' => $MERKHA,
+ '72' => $MERKHA_KEFULA,
+ '73' => $TIPEHA, # also '13', '73' also is used for majela
+ '74' => $MUNAH,
+ '75' => $METAG, # this is silluq; should appear to the left of the vowel
+ '91' => $TEVIR,
+ '92' => $ETNAHTA,
+ '93' => $YERAH_BEN_YOMO,
+ '94' => $DARGA,
+ '95' => $METAG, # should appear to the right of the vowel
+
+# Not used by the Michigan Encoding
+# $UPPER_DOT = '05C4';
+ );
+
+# declare other variables
+ my (@bhsLines,
+ @bhsVerse,
+ @entity_line) = ();
+
+ my ($i,
+ $verse,
+ $word,
+ $character) = 0;
+
+ my ($element,
+ $saveGuttural) = "";
+
+# read in a line
+ while (<>) {
+# Process one verse
+# iterate over every character and change to XML decimal entity
+ CHAR: for ( $i = 0; ($i < scalar(@bhsVerse)); $i++) {
+ # find and convert final kaf, mem, nun, pe, tsade
+ ( # if final form
+ $bhsVerse[$i] =~ /[KMNPC]/
+ )
+ &&
+ (
+ ( # whitespace or
+ $bhsVerse[$i+1] =~ /[ \-?]/
+ )
+ ||
+ ( # EOL or
+ $i == ( scalar(@bhsVerse) - 1 )
+ )
+ ||
+ ( # sof pasuq or
+ ( $bhsVerse[$i+1] =~ /0/ ) &&
+ ( $bhsVerse[$i+2] =~ /0/ )
+ )
+ ||
+ ( # one accent followed by white, eol or
+ (
+ ( $bhsVerse[$i+1] =~ /\d/ ) &&
+ ( $bhsVerse[$i+2] =~ /\d/ )
+ ) &&
+ (
+ ( $bhsVerse[$i+3] =~ /[ \-?]/ ) ||
+ ( $i == ( scalar(@bhsVerse) - 1 ) )
+ )
+ )
+ ||
+ ( # two accents followed by white, eol
+ (
+ ( $bhsVerse[$i+1] =~ /\d/ ) &&
+ ( $bhsVerse[$i+2] =~ /\d/ ) &&
+ ( $bhsVerse[$i+3] =~ /\d/ ) &&
+ ( $bhsVerse[$i+4] =~ /\d/ )
+ ) &&
+ (
+ ( $bhsVerse[$i+5] =~ /[ \-?]/ ) ||
+ ( $i == ( scalar(@bhsVerse) - 1 ) )
+ )
+ )
+ ||
+ ( # followed by a vowel and white, eol, sof pasuq
+ ( $bhsVerse[$i+1] =~ /[:F]/ ) &&
+ ( # followed by
+ ( $bhsVerse[$i+2] =~ /[ \-?]/ ) || # whitespace or
+ ( $i == ( scalar(@bhsVerse) - 1 ) ) || # eol or
+ ( # sof pasuq
+ ( $bhsVerse[$i+2] =~ /0/ ) &&
+ ( $bhsVerse[$i+3] =~ /0/ )
+ )
+ )
+ )
+ ) # end of what follows after final letter
+ &&
+ do {
+ $bhsVerse[$i] =~ /K/ && eval { push @entity_line,$FINAL_KAF; }
+ && next CHAR;
+ $bhsVerse[$i] =~ /M/ && eval { push @entity_line,$FINAL_MEM; }
+ && next CHAR;
+ $bhsVerse[$i] =~ /N/ && eval { push @entity_line,$FINAL_NUN; }
+ && next CHAR;
+ $bhsVerse[$i] =~ /P/ && eval { push @entity_line,$FINAL_PE; }
+ && next CHAR;
+ $bhsVerse[$i] =~ /C/ && eval { push @entity_line,$FINAL_TSADI; }
+ && next CHAR;
+ };
+ # find and convert "furtive patach"
+ ( $bhsVerse[$i] =~ /A/ ) && # If the letter is a patach
+ ( $bhsVerse[$i-1] =~ /[)HX(]/ ) && # and is preceeded by a guttural
+ ( ( $bhsVerse[$i-2] =~ /[AEFOU]/ ) || # and is preceeded by a vowel
+ ( ( $bhsVerse[$i-2] =~ /\./ ) && # or by suruq
+ ( $bhsVerse[$i-3] =~ /W/ ) ) || #
+ ( ( $bhsVerse[$i-2] =~ /W/ ) && # or by holem (written plene)
+ ( $bhsVerse[$i-3] =~ /O/ ) ) || #
+ ( ( $bhsVerse[$i-2] =~ /Y/ ) && # or by hiriq-yod
+ ( $bhsVerse[$i-3] =~ /I/ ) ) ) &&
+ do {
+ $saveGuttural = pop @entity_line; # snip off the gutteral
+ push @entity_line,$PATAH; # push on the patach
+ push @entity_line,$saveGuttural; # push back on the gutteral
+ next CHAR;
+ };
+ # convert cantillation
+ # since we have previously dealt with all other cases of
+ # numbers, two digit patterns are all we have to search for
+ $bhsVerse[$i] =~ /\d/ && $bhsVerse[$i+1] =~ /\d/ && do {
+ push @entity_line,$Michigan2XMLentity{"$bhsVerse[$i]$bhsVerse[$i+1]"};
+ $i++; # accents are two digits long, so advance past the 2nd digit
+ next CHAR;
+ };
+ # convert katef vowels, which are two characters long
+ $bhsVerse[$i] =~ /:/ && $bhsVerse[$i+1] =~ /[AEF]/ && do {
+ push @entity_line,$Michigan2XMLentity{"$bhsVerse[$i]$bhsVerse[$i+1]"};
+ $i++;
+ next CHAR;
+ };
+ # convert everything else
+ push @entity_line,$Michigan2XMLentity{"$bhsVerse[$i]"};
+ } # end CHAR
+# print the line to standard output with XML character-level encoding
+# each character has the following format:
+# <c id="1kg1.verse#.word#.character#">&#1234;</c>
+
+# set up the verse element
+ $word = 1;
+ $character = 1;
+ print "<verse>\n<word>\n";
+# print each character element
+# if there is a space, then close the word entity, open a new word
+# entity, increment the word number, reset the character number to
+# zero.
+ foreach $element (@entity_line) {
+ if ( $element =~ " " ) {
+ $word++;
+ $character = 1;
+ print "</word>\n<word>\n";
+ next;
+ }
+ print "<c id=\"1kg1.$verse.$word.$character\">$element</c>\n";
+ $character++;
+ }
+# close the verse element
+ print "</word></verse>\n";
+# reinitialize variables
+ @bhsVerse = ();
+ @entity_line = ();
+ @bhsLines = ();
+ } # end while
+# close the XML document
+ print "</body>\n";
+ */
diff --git a/src/frontend/im/nullim.cpp b/src/frontend/im/nullim.cpp
new file mode 100644
index 0000000..a4f4aad
--- /dev/null
+++ b/src/frontend/im/nullim.cpp
@@ -0,0 +1,11 @@
+#include <nullim.h>
+
+
+NullIM::NullIM() {
+}
+
+int *NullIM::translate(char ch) {
+ static int retVal[1];
+ *retVal = ch;
+ return retVal;
+}
diff --git a/src/frontend/im/swinputmeth.cpp b/src/frontend/im/swinputmeth.cpp
new file mode 100644
index 0000000..7f64865
--- /dev/null
+++ b/src/frontend/im/swinputmeth.cpp
@@ -0,0 +1,26 @@
+/**
+ * Title:
+ * Description:
+ * Copyright: Copyright (c) 2001 CrossWire Bible Society under the terms of the GNU GPL
+ * Company:
+ * @author Troy A. Griffitts
+ * @version 1.0
+ */
+
+#include <swinputmeth.h>
+
+SWInputMethod::SWInputMethod() {
+ state = 0;
+}
+
+void SWInputMethod::setState(int state) {
+ this->state = state;
+}
+
+int SWInputMethod::getState() {
+ return state;
+}
+
+void SWInputMethod::clearState() {
+ state = 0;
+}
diff --git a/src/frontend/swdisp.cpp b/src/frontend/swdisp.cpp
new file mode 100644
index 0000000..e7208e0
--- /dev/null
+++ b/src/frontend/swdisp.cpp
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * swdisp.cpp - code for base class 'swdisp'. swdisp is the basis for all
+ * types of displays (e.g. raw textout, curses, xwindow, etc.)
+ */
+
+#ifndef _WIN32_WCE
+#include <iostream>
+#endif
+#include <swmodule.h>
+#include <swdisp.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"SWDisplay", "SWObject", 0};
+SWClass SWDisplay::classdef(classes);
+
+/******************************************************************************
+ * SWDisplay::Display - casts a module to a character pointer and displays it to
+ * raw output (overriden for different display types and
+ * module types if necessary)
+ *
+ * ENT: imodule - module to display
+ *
+ * RET: error status
+ */
+
+char SWDisplay::Display(SWModule &imodule)
+{
+#ifndef _WIN32_WCE
+ std::cout << (const char *)imodule;
+#endif
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/frontend/swlog.cpp b/src/frontend/swlog.cpp
new file mode 100644
index 0000000..13b025b
--- /dev/null
+++ b/src/frontend/swlog.cpp
@@ -0,0 +1,114 @@
+//---------------------------------------------------------------------------
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <iostream>
+#if defined(_ICU_) && !defined(_ICUSWORD_)
+#define _USTDIO_
+#include <unicode/ustdio.h>
+#include <unicode/ustream.h>
+#endif
+#include "swlog.h"
+//---------------------------------------------------------------------------
+
+SWORD_NAMESPACE_START
+
+
+SWLog *SWLog::systemLog = 0;
+
+const int SWLog::LOG_ERROR = 1;
+const int SWLog::LOG_WARN = 2;
+const int SWLog::LOG_INFO = 3;
+const int SWLog::LOG_TIMEDINFO = 4;
+const int SWLog::LOG_DEBUG = 5;
+
+SWLog *SWLog::getSystemLog() {
+ static class __staticSystemLog {
+ SWLog **clear;
+ public:
+ __staticSystemLog(SWLog **clear) { this->clear = clear; }
+ ~__staticSystemLog() { delete *clear; *clear = 0; }
+ } _staticSystemLog(&SWLog::systemLog);
+
+ if (!systemLog)
+ systemLog = new SWLog();
+
+ return systemLog;
+}
+
+
+void SWLog::setSystemLog(SWLog *newLog) {
+ delete getSystemLog();
+ systemLog = newLog;
+}
+
+
+void SWLog::logWarning(const char *fmt, ...) const {
+ char msg[2048];
+ va_list argptr;
+
+ if (logLevel >= LOG_WARN) {
+ va_start(argptr, fmt);
+ vsprintf(msg, fmt, argptr);
+ va_end(argptr);
+ logMessage(msg, LOG_WARN);
+ }
+}
+
+
+void SWLog::logError(const char *fmt, ...) const {
+ char msg[2048];
+ va_list argptr;
+
+ if (logLevel) {
+ va_start(argptr, fmt);
+ vsprintf(msg, fmt, argptr);
+ va_end(argptr);
+ logMessage(msg, LOG_ERROR);
+ }
+}
+
+
+void SWLog::logInformation(const char *fmt, ...) const {
+ char msg[2048];
+ va_list argptr;
+
+ if (logLevel >= LOG_INFO) {
+ va_start(argptr, fmt);
+ vsprintf(msg, fmt, argptr);
+ va_end(argptr);
+ logMessage(msg, LOG_INFO);
+ }
+}
+
+
+void SWLog::logTimedInformation(const char *fmt, ...) const {
+ char msg[2048];
+ va_list argptr;
+
+ if (logLevel >= LOG_TIMEDINFO) {
+ va_start(argptr, fmt);
+ vsprintf(msg, fmt, argptr);
+ va_end(argptr);
+ logMessage(msg, LOG_TIMEDINFO);
+ }
+}
+
+
+void SWLog::logDebug(const char *fmt, ...) const {
+ char msg[2048];
+ va_list argptr;
+
+ if (logLevel >= LOG_DEBUG) {
+ va_start(argptr, fmt);
+ vsprintf(msg, fmt, argptr);
+ va_end(argptr);
+ logMessage(msg, LOG_DEBUG);
+ }
+}
+
+void SWLog::logMessage(const char *message, int level) const {
+ std::cerr << message;
+ std::cerr << std::endl;
+}
+SWORD_NAMESPACE_END
diff --git a/src/frontend/windoze/Makefile b/src/frontend/windoze/Makefile
new file mode 100644
index 0000000..1a2d00d
--- /dev/null
+++ b/src/frontend/windoze/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/keys/Makefile b/src/keys/Makefile
new file mode 100644
index 0000000..339f87a
--- /dev/null
+++ b/src/keys/Makefile
@@ -0,0 +1,4 @@
+root := ../..
+
+all:
+ make -C ${root}
diff --git a/src/keys/Makefile.am b/src/keys/Makefile.am
new file mode 100644
index 0000000..1eced21
--- /dev/null
+++ b/src/keys/Makefile.am
@@ -0,0 +1,10 @@
+keysdir = $(top_srcdir)/src/keys
+
+libsword_la_SOURCES += $(keysdir)/swkey.cpp
+libsword_la_SOURCES += $(keysdir)/listkey.cpp
+libsword_la_SOURCES += $(keysdir)/strkey.cpp
+libsword_la_SOURCES += $(keysdir)/treekey.cpp
+libsword_la_SOURCES += $(keysdir)/treekeyidx.cpp
+libsword_la_SOURCES += $(keysdir)/versekey.cpp
+libsword_la_SOURCES += $(keysdir)/versetreekey.cpp
+
diff --git a/src/keys/genarray.c b/src/keys/genarray.c
new file mode 100644
index 0000000..331d4af
--- /dev/null
+++ b/src/keys/genarray.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+main()
+{
+ int fd, l1, l2, l3;
+ char *fnames[] = {"ot.bks", "ot.cps", "nt.bks", "nt.cps"};
+ long val;
+ char buf[64];
+
+#ifndef O_BINARY // O_BINARY is needed in Borland C++ 4.53
+#define O_BINARY 0 // If it hasn't been defined than we probably
+#endif // don't need it.
+
+
+ for (l1 = 0; l1 < 2; l1++) {
+ for (l2 = 0; l2 < 2; l2++) {
+ l3 = 1;
+ sprintf(buf, "%s", fnames[(l1*2)+l2]);
+ printf(" // %s\n", fnames[(l1*2)+l2]);
+ fd = open(buf, O_RDONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH);
+ while (read(fd, &val, 4) == 4) {
+ l3++;
+ printf("%ld, ", val/(4 + (l2*2)));
+ if (!(l3%7))
+ printf("\n");
+ }
+ close(fd);
+ printf("}, \n");
+ }
+ }
+}
diff --git a/src/keys/listkey.cpp b/src/keys/listkey.cpp
new file mode 100644
index 0000000..ffafdd4
--- /dev/null
+++ b/src/keys/listkey.cpp
@@ -0,0 +1,332 @@
+/******************************************************************************
+ * listkey.cpp - code for base class 'ListKey'. ListKey is the basis for all
+ * types of keys that have lists of specified indexes
+ * (e.g. a list of verses, place, etc.)
+ */
+
+#include <utilstr.h>
+#include <stdlib.h>
+#include <swkey.h>
+#include <listkey.h>
+#include <string.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"ListKey", "SWKey", "SWObject", 0};
+SWClass ListKey::classdef(classes);
+
+/******************************************************************************
+ * ListKey Constructor - initializes instance of ListKey
+ *
+ * ENT: ikey - text key
+ */
+
+ListKey::ListKey(const char *ikey): SWKey(ikey) {
+ arraymax = 0;
+ ClearList();
+ init();
+}
+
+
+ListKey::ListKey(ListKey const &k) : SWKey(k.keytext) {
+ arraymax = k.arraymax;
+ arraypos = k.arraypos;
+ arraycnt = k.arraycnt;
+ array = (arraymax)?(SWKey **)malloc(k.arraymax * sizeof(SWKey *)):0;
+ for (int i = 0; i < arraycnt; i++)
+ array[i] = k.array[i]->clone();
+ init();
+}
+
+
+void ListKey::init() {
+ myclass = &classdef;
+}
+
+
+SWKey *ListKey::clone() const
+{
+ return new ListKey(*this);
+}
+
+/******************************************************************************
+ * ListKey Destructor - cleans up instance of ListKey
+ */
+
+ListKey::~ListKey()
+{
+ ClearList();
+}
+
+
+/******************************************************************************
+ * ListKey::ClearList - Clears out elements of list
+ */
+
+void ListKey::clear()
+{
+ int loop;
+
+ if (arraymax) {
+ for (loop = 0; loop < arraycnt; loop++)
+ delete array[loop];
+
+ free(array);
+ arraymax = 0;
+ }
+ arraycnt = 0;
+ arraypos = 0;
+ array = 0;
+}
+
+
+/******************************************************************************
+ * ListKey::copyFrom Equates this ListKey to another ListKey object
+ *
+ * ENT: ikey - other ListKey object
+ */
+
+void ListKey::copyFrom(const ListKey &ikey) {
+ ClearList();
+
+ arraymax = ikey.arraymax;
+ arraypos = ikey.arraypos;
+ arraycnt = ikey.arraycnt;
+ array = (arraymax)?(SWKey **)malloc(ikey.arraymax * sizeof(SWKey *)):0;
+ for (int i = 0; i < arraycnt; i++)
+ array[i] = ikey.array[i]->clone();
+
+ SetToElement(0);
+}
+
+
+/******************************************************************************
+ * ListKey::add - Adds an element to the list
+ */
+
+void ListKey::add(const SWKey &ikey) {
+ if (++arraycnt > arraymax) {
+ array = (SWKey **) ((array) ? realloc(array, (arraycnt + 32) * sizeof(SWKey *)) : calloc(arraycnt + 32, sizeof(SWKey *)));
+ arraymax = arraycnt + 32;
+ }
+ array[arraycnt-1] = ikey.clone();
+ SetToElement(arraycnt-1);
+}
+
+
+
+/******************************************************************************
+ * ListKey::setPosition(SW_POSITION) - Positions this key
+ *
+ * ENT: p - position
+ *
+ * RET: *this
+ */
+
+void ListKey::setPosition(SW_POSITION p) {
+ switch (p) {
+ case 1: // GCC won't compile P_TOP
+ SetToElement(0, p);
+ break;
+ case 2: // GCC won't compile P_BOTTOM
+ SetToElement(arraycnt-1, p);
+ break;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::increment - Increments a number of elements
+ */
+
+void ListKey::increment(int step) {
+ if (step < 0) {
+ decrement(step*-1);
+ return;
+ }
+ Error(); // clear error
+ for(; step && !Error(); step--) {
+ if (arraypos < arraycnt) {
+ if (array[arraypos]->isBoundSet())
+ (*(array[arraypos]))++;
+ if ((array[arraypos]->Error()) || (!array[arraypos]->isBoundSet())) {
+ SetToElement(arraypos+1);
+ }
+ else SWKey::setText((const char *)(*array[arraypos]));
+ }
+ else error = KEYERR_OUTOFBOUNDS;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::decrement - Decrements a number of elements
+ */
+
+void ListKey::decrement(int step) {
+ if (step < 0) {
+ increment(step*-1);
+ return;
+ }
+ Error(); // clear error
+ for(; step && !Error(); step--) {
+ if (arraypos > -1) {
+ if (array[arraypos]->isBoundSet())
+ (*(array[arraypos]))--;
+ if ((array[arraypos]->Error()) || (!array[arraypos]->isBoundSet())) {
+ SetToElement(arraypos-1, BOTTOM);
+ }
+ else SWKey::setText((const char *)(*array[arraypos]));
+ }
+ else error = KEYERR_OUTOFBOUNDS;
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::Count - Returns number of elements in list
+ */
+
+int ListKey::Count() {
+ return arraycnt;
+}
+
+
+/******************************************************************************
+ * ListKey::SetToElement - Sets key to element number
+ *
+ * ENT: ielement - element number to set to
+ *
+ * RET: error status
+ */
+
+char ListKey::SetToElement(int ielement, SW_POSITION pos) {
+ arraypos = ielement;
+ if (arraypos >= arraycnt) {
+ arraypos = (arraycnt>0)?arraycnt - 1:0;
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else {
+ if (arraypos < 0) {
+ arraypos = 0;
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else {
+ error = 0;
+ }
+ }
+
+ if (arraycnt) {
+ if (array[arraypos]->isBoundSet())
+ (*array[arraypos]) = pos;
+ SWKey::setText((const char *)(*array[arraypos]));
+ }
+ else SWKey::setText("");
+
+ return error;
+}
+
+
+/******************************************************************************
+ * ListKey::GetElement - Gets a key element number
+ *
+ * ENT: pos - element number to get (or default current)
+ *
+ * RET: Key or null on error
+ */
+
+SWKey *ListKey::getElement(int pos) {
+ if (pos < 0)
+ pos = arraypos;
+
+ if (pos >=arraycnt)
+ error = KEYERR_OUTOFBOUNDS;
+
+ return (error) ? 0:array[pos];
+}
+
+
+/******************************************************************************
+ * ListKey::Remove - Removes current element from list
+ */
+
+void ListKey::Remove() {
+ if ((arraypos > -1) && (arraypos < arraycnt)) {
+ delete array[arraypos];
+ if (arraypos < arraycnt - 1)
+ memmove(&array[arraypos], &array[arraypos+1], (arraycnt - arraypos - 1) * sizeof(SWKey *));
+ arraycnt--;
+
+ SetToElement((arraypos)?arraypos-1:0);
+ }
+}
+
+
+/******************************************************************************
+ * ListKey::getRangeText - returns parsable range text for this key
+ */
+
+const char *ListKey::getRangeText() const {
+ char *buf = new char[(arraycnt + 1) * 255];
+ buf[0] = 0;
+ for (int i = 0; i < arraycnt; i++) {
+ strcat(buf, array[i]->getRangeText());
+ if (i < arraycnt-1)
+ strcat(buf, "; ");
+ }
+ stdstr(&rangeText, buf);
+ delete [] buf;
+ return rangeText;
+}
+
+
+/******************************************************************************
+ * ListKey::getText - returns text key if (const char *) cast is requested
+ */
+
+const char *ListKey::getText() const {
+ int pos = arraypos;
+ SWKey *key = (pos >= arraycnt) ? 0:array[pos];
+ return (key) ? key->getText() : keytext;
+}
+
+
+void ListKey::setText(const char *ikey) {
+ // at least try to set the current element to this text
+ for (arraypos = 0; arraypos < arraycnt; arraypos++) {
+ SWKey *key = array[arraypos];
+ if (key) {
+ if (key->isTraversable() && key->isBoundSet()) {
+ key->setText(ikey);
+ if (!key->Error())
+ break;
+ }
+ else {
+ if (!strcmp(key->getText(), ikey))
+ break;
+ }
+ }
+ }
+ if (arraypos >= arraycnt) {
+ error = 1;
+ arraypos = arraycnt-1;
+ }
+
+ SWKey::setText(ikey);
+}
+
+// This sort impl sucks. Let's change it to a quicksort or some other efficient algol
+void ListKey::sort() {
+ for (int i = 0; i < arraycnt; i++) {
+ for (int j = i; j < arraycnt; j++) {
+ if (*array[j] < *array[i]) {
+ SWKey *tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+ }
+}
+
+SWORD_NAMESPACE_END
+
diff --git a/src/keys/nt.bks b/src/keys/nt.bks
new file mode 100644
index 0000000..6a3cf92
--- /dev/null
+++ b/src/keys/nt.bks
Binary files differ
diff --git a/src/keys/nt.cps b/src/keys/nt.cps
new file mode 100644
index 0000000..fdaa6f4
--- /dev/null
+++ b/src/keys/nt.cps
Binary files differ
diff --git a/src/keys/ot.bks b/src/keys/ot.bks
new file mode 100644
index 0000000..512f064
--- /dev/null
+++ b/src/keys/ot.bks
Binary files differ
diff --git a/src/keys/ot.cps b/src/keys/ot.cps
new file mode 100644
index 0000000..f4bf66b
--- /dev/null
+++ b/src/keys/ot.cps
Binary files differ
diff --git a/src/keys/strkey.cpp b/src/keys/strkey.cpp
new file mode 100644
index 0000000..1d84650
--- /dev/null
+++ b/src/keys/strkey.cpp
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * StrKey.cpp - code for class 'StrKey'- a standard string key class (used
+ * for modules that index on single strings (eg. cities,
+ * names, words, etc.)
+ */
+
+#include <swmacs.h>
+#include <strkey.h>
+#include <stdio.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"StrKey", "SWKey", "SWObject", 0};
+SWClass StrKey::classdef(classes);
+
+/******************************************************************************
+ * StrKey Constructor - initializes instance of StrKey
+ *
+ * ENT: ikey - text key (word, city, name, etc.)
+ */
+
+StrKey::StrKey(const char *ikey) : SWKey(ikey)
+{
+ init();
+}
+
+
+void StrKey::init() {
+ myclass = &classdef;
+}
+
+
+/******************************************************************************
+ * StrKey Destructor - cleans up instance of StrKey
+ *
+ * ENT: ikey - text key
+ */
+
+StrKey::~StrKey() {
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/keys/swkey.cpp b/src/keys/swkey.cpp
new file mode 100644
index 0000000..0557c40
--- /dev/null
+++ b/src/keys/swkey.cpp
@@ -0,0 +1,216 @@
+/******************************************************************************
+ * swkey.cpp - code for base class 'SWKey'. SWKey is the basis for all
+ * types of keys for indexing into modules (e.g. verse, word,
+ * place, etc.)
+ */
+
+#include <swkey.h>
+#include <utilstr.h>
+#include <string.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"SWKey", "SWObject", 0};
+SWClass SWKey::classdef(classes);
+
+/******************************************************************************
+ * SWKey Constructor - initializes instance of SWKey
+ *
+ * ENT: ikey - text key
+ */
+
+SWKey::SWKey(const char *ikey)
+{
+ index = 0;
+ persist = 0;
+ keytext = 0;
+ rangeText = 0;
+ error = 0;
+ userData = 0;
+ stdstr(&keytext, ikey);
+ init();
+}
+
+SWKey::SWKey(SWKey const &k)
+{
+ index = k.index;
+ persist = k.persist;
+ userData = k.userData;
+ keytext = 0;
+ rangeText = 0;
+ error = k.error;
+ setText(k.getText());
+ init();
+}
+
+void SWKey::init() {
+ myclass = &classdef;
+ boundSet = false;
+}
+
+SWKey *SWKey::clone() const
+{
+ return new SWKey(*this);
+}
+
+/******************************************************************************
+ * SWKey Destructor - cleans up instance of SWKey
+ */
+
+SWKey::~SWKey() {
+ if (keytext)
+ delete [] keytext;
+ if (rangeText)
+ delete [] rangeText;
+}
+
+
+/******************************************************************************
+ * SWKey::Persist - Gets whether this object itself persists within a
+ * module that it was used to setKey or just a copy.
+ * (1 - persists in module; 0 - a copy is attempted
+ *
+ * RET: value of persist
+ */
+
+char SWKey::Persist() const
+{
+ return persist;
+}
+
+
+/******************************************************************************
+ * SWKey::Persist - Set/gets whether this object itself persists within a
+ * module that it was used to setKey or just a copy.
+ * (1 - persists in module; 0 - a copy is attempted
+ *
+ * ENT: ipersist - value which to set persist
+ * [-1] - only get
+ *
+ * RET: value of persist
+ */
+
+char SWKey::Persist(signed char ipersist)
+{
+ if (ipersist != -1)
+ persist = ipersist;
+
+ return persist;
+}
+
+
+/******************************************************************************
+ * SWKey::Error - Gets and clears error status
+ *
+ * RET: error status
+ */
+
+char SWKey::Error()
+{
+ char retval = error;
+
+ error = 0;
+ return retval;
+}
+
+
+/******************************************************************************
+ * SWKey::setText Equates this SWKey to a character string
+ *
+ * ENT: ikey - other swkey object
+ */
+
+void SWKey::setText(const char *ikey) {
+ stdstr(&keytext, ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::copyFrom Equates this SWKey to another SWKey object
+ *
+ * ENT: ikey - other swkey object
+ */
+
+void SWKey::copyFrom(const SWKey &ikey) {
+// not desirable Persist(ikey.Persist());
+ setText((const char *)ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::getText - returns text key if (const char *) cast is requested
+ */
+
+const char *SWKey::getText() const {
+ return keytext;
+}
+
+
+/******************************************************************************
+ * SWKey::getRangeText - returns parsable range text for this key
+ */
+
+const char *SWKey::getRangeText() const {
+ stdstr(&rangeText, keytext);
+ return rangeText;
+}
+
+
+/******************************************************************************
+ * SWKey::compare - Compares another VerseKey object
+ *
+ * ENT: ikey - key to compare with this one
+ *
+ * RET: > 0 if this key is greater than compare key
+ * < 0
+ * 0
+ */
+
+int SWKey::compare(const SWKey &ikey)
+{
+ return strcmp((const char *)*this, (const char *)ikey);
+}
+
+
+/******************************************************************************
+ * SWKey::setPosition(SW_POSITION) - Positions this key if applicable
+ */
+
+void SWKey::setPosition(SW_POSITION p) {
+ switch (p) {
+ case POS_TOP:
+// *this = "";
+ break;
+ case POS_BOTTOM:
+// *this = "zzzzzzzzz";
+ break;
+ }
+}
+
+
+/******************************************************************************
+ * SWKey::increment - Increments key a number of entries
+ *
+ * ENT: increment - Number of entries to jump forward
+ *
+ * RET: *this
+ */
+
+void SWKey::increment(int) {
+ error = KEYERR_OUTOFBOUNDS;
+}
+
+
+/******************************************************************************
+ * SWKey::decrement - Decrements key a number of entries
+ *
+ * ENT: decrement - Number of entries to jump backward
+ *
+ * RET: *this
+ */
+
+void SWKey::decrement(int) {
+ error = KEYERR_OUTOFBOUNDS;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/keys/treekey.cpp b/src/keys/treekey.cpp
new file mode 100644
index 0000000..2b217ee
--- /dev/null
+++ b/src/keys/treekey.cpp
@@ -0,0 +1,96 @@
+/******************************************************************************
+ * versekey.h - code for class 'versekey'- a standard Biblical verse key
+ *
+ * $Id: treekey.cpp 1994 2006-11-20 20:24:06Z 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 <treekey.h>
+#include <utilstr.h>
+#include <string.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"TreeKey", "SWKey", "SWObject", 0};
+SWClass TreeKey::classdef(classes);
+
+void TreeKey::init() {
+ myclass = &classdef;
+ unsnappedKeyText = "";
+}
+
+
+void TreeKey::assureKeyPath(const char *keyBuffer) {
+
+ if (!keyBuffer) {
+ keyBuffer = unsnappedKeyText;
+ //assert we have something to do before setting root
+ if (!*keyBuffer)
+ return;
+ }
+
+ char *keybuf = 0;
+ stdstr(&keybuf, keyBuffer);
+
+ root();
+
+ // TODO: change to NOT use strtok. strtok is dangerous.
+ SWBuf tok = strtok(keybuf, "/");
+ tok.trim();
+ while (tok.size()) {
+ bool foundkey = false;
+ if (hasChildren()) {
+ firstChild();
+ if (tok == getLocalName()) {
+ foundkey = true;
+ }
+ else {
+ while (nextSibling()) {
+ if (getLocalName()) {
+ if (tok == getLocalName()) {
+ foundkey = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!foundkey) {
+ append();
+ setLocalName(tok);
+ save();
+ }
+ }
+ else {
+ appendChild();
+ setLocalName(tok);
+ save();
+ }
+
+#ifdef DEBUG
+// std::cout << getLocalName() << " : " << tok << std::endl;
+#endif
+
+ tok = strtok(0, "/");
+ tok.trim();
+
+ }
+ delete [] keybuf;
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/keys/treekeyidx.cpp b/src/keys/treekeyidx.cpp
new file mode 100644
index 0000000..738d17a
--- /dev/null
+++ b/src/keys/treekeyidx.cpp
@@ -0,0 +1,643 @@
+/******************************************************************************
+ * versekey.h - code for class 'versekey'- a standard Biblical verse key
+ *
+ * $Id: treekeyidx.cpp 2147 2008-03-14 06:54:18Z scribe $
+ *
+ * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org)
+ * CrossWire Bible Society
+ * P. O. Box 2528
+ * Tempe, AZ 85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <treekeyidx.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <swlog.h>
+#include <utilstr.h>
+#include <filemgr.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+static const char nl = '\n';
+static const char *classes[] = {"TreeKeyIdx", "TreeKey", "SWKey", "SWObject", 0};
+SWClass TreeKeyIdx::classdef(classes);
+
+
+TreeKeyIdx::TreeKeyIdx(const TreeKeyIdx &ikey) : currentNode() {
+ init();
+ path = 0;
+ idxfd = 0;
+ datfd = 0;
+ copyFrom(ikey);
+}
+
+TreeKeyIdx::TreeKeyIdx(const char *idxPath, int fileMode) : currentNode() {
+ SWBuf buf;
+
+ init();
+ path = 0;
+ stdstr(&path, idxPath);
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s.idx", path);
+ idxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+ buf.setFormatted("%s.dat", path);
+ datfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ if (datfd <= 0) {
+ SWLog::getSystemLog()->logError("%d", errno);
+ error = errno;
+ }
+ else {
+ root();
+ }
+}
+
+
+void TreeKeyIdx::init() {
+ myclass = &classdef;
+}
+
+
+TreeKeyIdx::~TreeKeyIdx () {
+ if (path)
+ delete [] path;
+
+ FileMgr::getSystemFileMgr()->close(idxfd);
+ FileMgr::getSystemFileMgr()->close(datfd);
+}
+
+
+const char *TreeKeyIdx::getLocalName() {
+ unsnappedKeyText = "";
+ return currentNode.name;
+}
+
+
+const char *TreeKeyIdx::getUserData(int *size) {
+ unsnappedKeyText = "";
+ if (size)
+ *size = (int)currentNode.dsize;
+ return currentNode.userData;
+}
+
+
+void TreeKeyIdx::setUserData(const char *userData, int size) {
+ // this makes sure any unsnapped path exists
+ assureKeyPath();
+ if (currentNode.userData)
+ delete currentNode.userData;
+
+ if (!size)
+ size = strlen(userData) + 1;
+
+ currentNode.userData = new char [ size ];
+ memcpy(currentNode.userData, userData, size);
+ currentNode.dsize = size;
+}
+
+const char *TreeKeyIdx::setLocalName(const char *newName) {
+ unsnappedKeyText = "";
+ stdstr(&(currentNode.name), newName);
+ return currentNode.name;
+}
+
+
+void TreeKeyIdx::save() {
+ saveTreeNode(&currentNode);
+}
+
+
+void TreeKeyIdx::root() {
+ error = getTreeNodeFromIdxOffset(0, &currentNode);
+}
+
+
+bool TreeKeyIdx::parent() {
+ if (currentNode.parent > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.parent, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::firstChild() {
+ if (currentNode.firstChild > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.firstChild, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::nextSibling() {
+ if (currentNode.next > -1) {
+ error = getTreeNodeFromIdxOffset(currentNode.next, &currentNode);
+ return true;
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::previousSibling() {
+ TreeNode iterator;
+ __s32 target = currentNode.offset;
+ if (currentNode.parent > -1) {
+ getTreeNodeFromIdxOffset(currentNode.parent, &iterator);
+ getTreeNodeFromIdxOffset(iterator.firstChild, &iterator);
+ if (iterator.offset != target) {
+ while ((iterator.next != target) && (iterator.next > -1))
+ getTreeNodeFromIdxOffset(iterator.next, &iterator);
+ if (iterator.next > -1) {
+ error = getTreeNodeFromIdxOffset(iterator.offset, &currentNode);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+bool TreeKeyIdx::hasChildren() {
+ return (currentNode.firstChild > -1);
+}
+
+
+void TreeKeyIdx::append() {
+ TreeNode lastSib;
+ if (currentNode.offset) {
+ getTreeNodeFromIdxOffset(currentNode.offset, &lastSib);
+ while (lastSib.next > -1) {
+ getTreeNodeFromIdxOffset(lastSib.next, &lastSib);
+ }
+ __u32 idxOffset = idxfd->seek(0, SEEK_END);
+ lastSib.next = idxOffset;
+ saveTreeNodeOffsets(&lastSib);
+ __u32 parent = currentNode.parent;
+ currentNode.clear();
+ currentNode.offset = idxOffset;
+ currentNode.parent = parent;
+ }
+}
+
+
+void TreeKeyIdx::appendChild() {
+ if (firstChild()) {
+ append();
+ }
+ else {
+ __u32 idxOffset = idxfd->seek(0, SEEK_END);
+ currentNode.firstChild = idxOffset;
+ saveTreeNodeOffsets(&currentNode);
+ __u32 parent = currentNode.offset;
+ currentNode.clear();
+ currentNode.offset = idxOffset;
+ currentNode.parent = parent;
+ }
+}
+
+
+void TreeKeyIdx::insertBefore() {
+}
+
+
+void TreeKeyIdx::remove() {
+ TreeNode node;
+ bool done = false;
+ if (currentNode.offset) {
+ getTreeNodeFromIdxOffset(currentNode.offset, &node);
+ if (node.parent > -1) {
+ TreeNode parent;
+ getTreeNodeFromIdxOffset(node.parent, &parent);
+ if (parent.firstChild == node.offset) {
+ parent.firstChild = node.next;
+ saveTreeNodeOffsets(&parent);
+ getTreeNodeFromIdxOffset(parent.offset, &currentNode);
+ done = true;
+ }
+ }
+ if (!done) {
+ TreeNode iterator;
+ __s32 target = currentNode.offset;
+ if (currentNode.parent > -1) {
+ getTreeNodeFromIdxOffset(currentNode.parent, &iterator);
+ getTreeNodeFromIdxOffset(iterator.firstChild, &iterator);
+ if (iterator.offset != target) {
+ while ((iterator.next != target) && (iterator.next > -1))
+ getTreeNodeFromIdxOffset(iterator.next, &iterator);
+ if (iterator.next > -1) {
+ TreeNode prev;
+ getTreeNodeFromIdxOffset(iterator.offset, &prev);
+ prev.next = node.next;
+ saveTreeNodeOffsets(&prev);
+ getTreeNodeFromIdxOffset(prev.offset, &currentNode);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/******************************************************************************
+ * TreeKeyIdx::Create - Creates new key idx/dat files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+signed char TreeKeyIdx::create(const char *ipath) {
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s.dat", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s.idx", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ TreeKeyIdx newTree(path);
+ TreeKeyIdx::TreeNode root;
+ stdstr(&(root.name), "");
+ newTree.saveTreeNode(&root);
+
+ delete [] path;
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * zStr::getidxbufdat - Gets the index string at the given dat offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd and must
+ * be free'd by calling function
+ *
+ * ENT: ioffset - offset in dat file to lookup
+ * node - address of pointer to allocate for storage of string
+ */
+
+void TreeKeyIdx::getTreeNodeFromDatOffset(long ioffset, TreeNode *node) const {
+ unsnappedKeyText = "";
+ char ch;
+ __s32 tmp;
+ __u16 tmp2;
+
+ if (datfd > 0) {
+
+ datfd->seek(ioffset, SEEK_SET);
+
+ datfd->read(&tmp, 4);
+ node->parent = swordtoarch32(tmp);
+
+ datfd->read(&tmp, 4);
+ node->next = swordtoarch32(tmp);
+
+ datfd->read(&tmp, 4);
+ node->firstChild = swordtoarch32(tmp);
+
+ SWBuf name;
+ do {
+ datfd->read(&ch, 1);
+ name += ch;
+ } while (ch);
+
+ stdstr(&(node->name), name.c_str());
+
+ datfd->read(&tmp2, 2);
+ node->dsize = swordtoarch16(tmp2);
+
+ if (node->dsize) {
+ if (node->userData)
+ delete [] node->userData;
+ node->userData = new char [node->dsize];
+ datfd->read(node->userData, node->dsize);
+ }
+ }
+}
+
+
+/******************************************************************************
+ * zStr::getidxbuf - Gets the index string at the given idx offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd
+ * and must be freed by calling function
+ *
+ * ENT: ioffset - offset in idx file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+char TreeKeyIdx::getTreeNodeFromIdxOffset(long ioffset, TreeNode *node) const {
+ unsnappedKeyText = "";
+ __u32 offset;
+ char error = KEYERR_OUTOFBOUNDS;
+
+ if (ioffset < 0) {
+ ioffset = 0;
+ error = 77; // out of bounds but still position to 0;
+ }
+
+ node->offset = ioffset;
+ if (idxfd > 0) {
+ if (idxfd->getFd() > 0) {
+ idxfd->seek(ioffset, SEEK_SET);
+ if (idxfd->read(&offset, 4) == 4) {
+ offset = swordtoarch32(offset);
+ error = (error == 77) ? KEYERR_OUTOFBOUNDS : 0;
+ getTreeNodeFromDatOffset(offset, node);
+ }
+ else {
+ idxfd->seek(-4, SEEK_END);
+ if (idxfd->read(&offset, 4) == 4) {
+ offset = swordtoarch32(offset);
+ getTreeNodeFromDatOffset(offset, node);
+ }
+ }
+ }
+ }
+ return error;
+}
+
+
+unsigned long TreeKeyIdx::getOffset() const {
+ unsnappedKeyText = "";
+ return currentNode.offset;
+}
+
+void TreeKeyIdx::setOffset(unsigned long offset) {
+ error = getTreeNodeFromIdxOffset(offset, &currentNode);
+}
+
+
+void TreeKeyIdx::saveTreeNodeOffsets(TreeNode *node) {
+ unsnappedKeyText = "";
+ long datOffset = 0;
+ __s32 tmp;
+
+ if (idxfd > 0) {
+ idxfd->seek(node->offset, SEEK_SET);
+ if (idxfd->read(&tmp, 4) != 4) {
+ datOffset = datfd->seek(0, SEEK_END);
+ tmp = archtosword32(datOffset);
+ idxfd->write(&tmp, 4);
+ }
+ else {
+ datOffset = swordtoarch32(tmp);
+ datfd->seek(datOffset, SEEK_SET);
+ }
+
+ tmp = archtosword32(node->parent);
+ datfd->write(&tmp, 4);
+
+ tmp = archtosword32(node->next);
+ datfd->write(&tmp, 4);
+
+ tmp = archtosword32(node->firstChild);
+ datfd->write(&tmp, 4);
+ }
+}
+
+
+void TreeKeyIdx::copyFrom(const TreeKeyIdx &ikey) {
+ unsnappedKeyText = "";
+
+ SWKey::copyFrom(ikey);
+
+ currentNode.offset = ikey.currentNode.offset;
+ currentNode.parent = ikey.currentNode.parent;
+ currentNode.next = ikey.currentNode.next;
+ currentNode.firstChild = ikey.currentNode.firstChild;
+ stdstr(&(currentNode.name), ikey.currentNode.name);
+ currentNode.dsize = ikey.currentNode.dsize;
+
+ if (currentNode.userData)
+ delete [] currentNode.userData;
+ if (currentNode.dsize) {
+ currentNode.userData = new char [ currentNode.dsize ];
+ memcpy(currentNode.userData, ikey.currentNode.userData, currentNode.dsize);
+ }
+ else currentNode.userData = 0;
+
+ bool newFiles = true;
+
+ if (path && ikey.path)
+ newFiles = strcmp(path, ikey.path);
+
+ if (newFiles) {
+ stdstr(&path, ikey.path);
+
+ if (idxfd) {
+ FileMgr::getSystemFileMgr()->close(idxfd);
+ FileMgr::getSystemFileMgr()->close(datfd);
+ }
+ idxfd = FileMgr::getSystemFileMgr()->open(ikey.idxfd->path, ikey.idxfd->mode, ikey.idxfd->perms);
+ datfd = FileMgr::getSystemFileMgr()->open(ikey.datfd->path, ikey.datfd->mode, ikey.datfd->perms);
+ }
+}
+
+
+void TreeKeyIdx::saveTreeNode(TreeNode *node) {
+ long datOffset = 0;
+ __s32 tmp;
+ if (idxfd > 0) {
+
+ idxfd->seek(node->offset, SEEK_SET);
+ datOffset = datfd->seek(0, SEEK_END);
+ tmp = archtosword32(datOffset);
+ idxfd->write(&tmp, 4);
+
+ saveTreeNodeOffsets(node);
+
+ datfd->write(node->name, strlen(node->name));
+ char null = 0;
+ datfd->write(&null, 1);
+
+ __u16 tmp2 = archtosword16(node->dsize);
+ datfd->write(&tmp2, 2);
+
+ if (node->dsize) {
+ datfd->write(node->userData, node->dsize);
+ }
+ }
+}
+
+
+void TreeKeyIdx::setText(const char *ikey) {
+ char *buf = 0;
+ stdstr(&buf, ikey);
+ SWBuf leaf = strtok(buf, "/");
+ leaf.trim();
+ root();
+ while ((leaf.size()) && (!Error())) {
+ bool ok, inChild = false;
+ error = KEYERR_OUTOFBOUNDS;
+ for (ok = firstChild(); ok; ok = nextSibling()) {
+ inChild = true;
+ if (leaf == getLocalName()) {
+ error = 0;
+ break;
+ }
+ }
+ leaf = strtok(0, "/");
+ leaf.trim();
+ if (!ok) {
+ if (inChild) { // if we didn't find a matching child node, default to first child
+ parent();
+ firstChild();
+ }
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ }
+ if (leaf.size())
+ error = KEYERR_OUTOFBOUNDS;
+ delete [] buf;
+ unsnappedKeyText = ikey;
+}
+
+
+
+void TreeKeyIdx::copyFrom(const SWKey &ikey) {
+ unsnappedKeyText = ikey;
+ SWKey::copyFrom(ikey);
+}
+
+void TreeKeyIdx::setPosition(SW_POSITION p) {
+ switch (p) {
+ case POS_TOP:
+ root();
+ break;
+ case POS_BOTTOM:
+ error = getTreeNodeFromIdxOffset(idxfd->seek(-4, SEEK_END), &currentNode);
+ break;
+ }
+ Error(); // clear error from normalize
+}
+
+
+int TreeKeyIdx::_compare (const TreeKeyIdx & ikey) {
+ return (getOffset() - ikey.getOffset());
+}
+
+
+int TreeKeyIdx::compare(const SWKey &ikey) {
+ TreeKeyIdx *treeKey = SWDYNAMIC_CAST(TreeKeyIdx, (&ikey));
+ if (treeKey)
+ return _compare(*treeKey);
+ return SWKey::compare(ikey);
+}
+
+
+void TreeKeyIdx::decrement(int steps) {
+ error = getTreeNodeFromIdxOffset(currentNode.offset - (4*steps), &currentNode);
+}
+
+void TreeKeyIdx::increment(int steps) {
+ error = getTreeNodeFromIdxOffset(currentNode.offset + (4*steps), &currentNode);
+
+/*
+ // assert positive
+ if (steps < 0) {
+ decrement(steps * -1);
+ return;
+ }
+
+ while (steps > 0) {
+ if (!firstChild()) {
+ if (!nextSibbling() {
+ error = KEYERR_OUTOFBOUNDS;
+ return;
+ }
+ }
+ steps--;
+ }
+*/
+}
+
+
+
+const char *TreeKeyIdx::getText() const {
+ TreeNode parent;
+ static SWBuf fullPath;
+ fullPath = currentNode.name;
+ parent.parent = currentNode.parent;
+ while (parent.parent > -1) {
+ getTreeNodeFromIdxOffset(parent.parent, &parent);
+ fullPath = ((SWBuf)parent.name) + (SWBuf) "/" + fullPath;
+ }
+ // we've snapped; clear our unsnapped text holder
+ unsnappedKeyText = "";
+ return fullPath.c_str();
+}
+
+
+TreeKeyIdx::TreeNode::TreeNode() {
+
+ name = 0;
+ stdstr(&name, "");
+ userData = 0;
+
+ clear();
+}
+
+
+void TreeKeyIdx::TreeNode::clear() {
+ offset = 0;
+ parent = -1;
+ next = -1;
+ firstChild = -1;
+ dsize = 0;
+
+ if (name)
+ delete [] name;
+ name = 0;
+ stdstr(&name, "");
+
+ if (userData)
+ delete [] userData;
+ userData = 0;
+}
+
+
+TreeKeyIdx::TreeNode::~TreeNode() {
+ if (name)
+ delete [] name;
+
+ if (userData)
+ delete [] userData;
+}
+
+
+SWKey *TreeKeyIdx::clone() const
+{
+ return new TreeKeyIdx(*this);
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/keys/versekey.cpp b/src/keys/versekey.cpp
new file mode 100644
index 0000000..cb7bbfc
--- /dev/null
+++ b/src/keys/versekey.cpp
@@ -0,0 +1,1664 @@
+/******************************************************************************
+ * VerseKey.cpp - code for class 'VerseKey'- a standard Biblical verse key
+ */
+
+#include <swmacs.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <utilstr.h>
+#include <stringmgr.h>
+#include <swkey.h>
+#include <swlog.h>
+#include <versekey.h>
+#include <localemgr.h>
+#include <swlocale.h>
+#include <roman.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0};
+SWClass VerseKey::classdef(classes);
+
+/******************************************************************************
+ * Initialize static members of VerseKey
+ */
+
+#include <canon.h> // Initialize static members of canonical books structure
+
+struct sbook *VerseKey::builtin_books[2] = {0,0};
+const char VerseKey::builtin_BMAX[2] = {39, 27};
+long *VerseKey::offsets[2][2] = {{VerseKey::otbks, VerseKey::otcps}, {VerseKey::ntbks, VerseKey::ntcps}};
+int VerseKey::instance = 0;
+VerseKey::LocaleCache VerseKey::localeCache;
+
+
+/******************************************************************************
+ * VerseKey::init - initializes instance of VerseKey
+ */
+
+void VerseKey::init() {
+ myclass = &classdef;
+ if (!instance)
+ initstatics();
+
+ instance++;
+ autonorm = 1; // default auto normalization to true
+ headings = 0; // default display headings option is false
+ upperBound = 0;
+ lowerBound = 0;
+ boundSet = false;
+ testament = 0;
+ book = 0;
+ chapter = 0;
+ verse = 0;
+ locale = 0;
+
+ setLocale(LocaleMgr::getSystemLocaleMgr()->getDefaultLocaleName());
+}
+
+/******************************************************************************
+ * VerseKey Constructor - initializes instance of VerseKey
+ *
+ * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'. See
+ * VerseKey::parse for more detailed information)
+ */
+
+VerseKey::VerseKey(const SWKey &ikey) : SWKey(ikey)
+{
+ init();
+ copyFrom(ikey);
+}
+
+
+VerseKey::VerseKey(const SWKey *ikey) : SWKey(*ikey)
+{
+ init();
+ if (ikey)
+ copyFrom(*ikey);
+}
+
+
+/******************************************************************************
+ * VerseKey Constructor - initializes instance of VerseKey
+ *
+ * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'. See
+ * VerseKey::parse for more detailed information)
+ */
+
+VerseKey::VerseKey(const char *ikeyText) : SWKey(ikeyText)
+{
+ init();
+ if (ikeyText)
+ parse();
+}
+
+
+VerseKey::VerseKey(VerseKey const &k) : SWKey(k)
+{
+ init();
+ copyFrom(k);
+}
+
+
+/******************************************************************************
+ * VerseKey::copyFrom - Equates this VerseKey to another VerseKey
+ */
+
+void VerseKey::copyFrom(const VerseKey &ikey) {
+ autonorm = ikey.autonorm;
+ headings = ikey.headings;
+ testament = ikey.Testament();
+ book = ikey.Book();
+ chapter = ikey.Chapter();
+ verse = ikey.Verse();
+ if (ikey.isBoundSet()) {
+ LowerBound(ikey.LowerBound());
+ UpperBound(ikey.UpperBound());
+ }
+}
+
+
+/******************************************************************************
+ * VerseKey::copyFrom - Equates this VerseKey to another SWKey
+ */
+
+void VerseKey::copyFrom(const SWKey &ikey) {
+ // check to see if we can do a more specific copy
+ // plus some optimizations
+ const SWKey *fromKey = &ikey;
+ ListKey *tryList = SWDYNAMIC_CAST(ListKey, fromKey);
+ if (tryList) {
+ SWKey *k = tryList->getElement();
+ if (k) fromKey = k;
+ }
+ VerseKey *tryVerse = SWDYNAMIC_CAST(VerseKey, fromKey);
+ if (tryVerse) {
+ copyFrom(*tryVerse);
+ }
+ else {
+ SWKey::copyFrom(*fromKey);
+ parse();
+ }
+}
+
+
+VerseKey::VerseKey(const char *min, const char *max) : SWKey()
+{
+ init();
+ LowerBound(min);
+ UpperBound(max);
+ setPosition(TOP);
+}
+
+
+SWKey *VerseKey::clone() const
+{
+ return new VerseKey(*this);
+}
+
+
+/******************************************************************************
+ * VerseKey Destructor - cleans up instance of VerseKey
+ *
+ * ENT: ikey - text key
+ */
+
+VerseKey::~VerseKey() {
+ if (upperBound)
+ delete upperBound;
+ if (lowerBound)
+ delete lowerBound;
+ if (locale)
+ delete [] locale;
+
+ --instance;
+}
+
+
+void VerseKey::setLocale(const char *name) {
+ char *BMAX;
+ struct sbook **lbooks;
+ bool useCache = false;
+
+ if (localeCache.name)
+ useCache = (!strcmp(localeCache.name, name));
+
+ if (!useCache) { // if we're setting params for a new locale
+ stdstr(&(localeCache.name), name);
+ localeCache.abbrevsCnt = 0;
+ }
+
+ SWLocale *locale = (useCache) ? localeCache.locale : LocaleMgr::getSystemLocaleMgr()->getLocale(name);
+ localeCache.locale = locale;
+
+ if (locale) {
+ locale->getBooks(&BMAX, &lbooks);
+ setBooks(BMAX, lbooks);
+ setBookAbbrevs(locale->getBookAbbrevs(), localeCache.abbrevsCnt);
+ localeCache.abbrevsCnt = abbrevsCnt;
+ }
+ else {
+ setBooks(builtin_BMAX, builtin_books);
+ setBookAbbrevs(builtin_abbrevs, localeCache.abbrevsCnt);
+ localeCache.abbrevsCnt = abbrevsCnt;
+ }
+ stdstr(&(this->locale), localeCache.name);
+
+ if (lowerBound)
+ LowerBound().setLocale(name);
+ if (upperBound)
+ UpperBound().setLocale(name);
+}
+
+void VerseKey::setBooks(const char *iBMAX, struct sbook **ibooks) {
+ BMAX = iBMAX;
+ books = ibooks;
+}
+
+
+void VerseKey::setBookAbbrevs(const struct abbrev *bookAbbrevs, unsigned int size) {
+ abbrevs = bookAbbrevs;
+ if (!size) {
+ for (abbrevsCnt = 0; *abbrevs[abbrevsCnt].ab; abbrevsCnt++) {
+ /*
+ if (strcmp(abbrevs[abbrevsCnt-1].ab, abbrevs[abbrevsCnt].ab) > 0) {
+ fprintf(stderr, "ERROR: book abbreviation (canon.h or locale) misordered at entry: %s\n", abbrevs[abbrevsCnt].ab);
+ exit(-1);
+ }
+ */
+ }
+
+ if (SWLog::getSystemLog()->getLogLevel() > 0) { //make sure log is wanted, this loop stuff costs a lot of time
+ for (int t = 0; t < 2; t++) {
+ for (int i = 0; i < BMAX[t]; i++) {
+ const int bn = getBookAbbrev(books[t][i].name);
+ if ((bn-1)%39 != i) {
+ SWLog::getSystemLog()->logError("VerseKey::Book: %s does not have a matching toupper abbrevs entry! book number returned was: %d(%d). Required entry should be:",
+ books[t][i].name, bn, i);
+ char *abbr = 0;
+ stdstr(&abbr, books[t][i].name, 2);
+ strstrip(abbr);
+
+ StringMgr* stringMgr = StringMgr::getSystemStringMgr();
+ const bool hasUTF8Support = StringMgr::hasUTF8Support();
+ if (hasUTF8Support) { //we have support for UTF-8 handling; we expect UTF-8 encoded locales
+ stringMgr->upperUTF8(abbr, strlen(abbr)*2);
+ }
+ else {
+ stringMgr->upperLatin1(abbr);
+ }
+ SWLog::getSystemLog()->logError("%s=%d", abbr, (t*39)+i+1);
+ }
+ }
+ }
+ }
+ }
+ else abbrevsCnt = size;
+}
+
+
+/******************************************************************************
+ * VerseKey::initstatics - initializes statics. Performed only when first
+ * instance on VerseKey (or descendent) is created.
+ */
+
+void VerseKey::initstatics() {
+ int l1, l2, chaptmp = 0;
+
+ builtin_books[0] = otbooks;
+ builtin_books[1] = ntbooks;
+
+ for (l1 = 0; l1 < 2; l1++) {
+ for (l2 = 0; l2 < builtin_BMAX[l1]; l2++) {
+ builtin_books[l1][l2].versemax = &vm[chaptmp];
+ chaptmp += builtin_books[l1][l2].chapmax;
+ }
+ }
+}
+
+
+/******************************************************************************
+ * VerseKey::parse - parses keytext into testament|book|chapter|verse
+ *
+ * RET: error status
+ */
+
+char VerseKey::parse(bool checkAutoNormalize)
+{
+
+
+ testament = 2;
+ book = BMAX[1];
+ chapter = 1;
+ verse = 1;
+ int booklen = 0;
+
+ int error = 0;
+
+ if (keytext) {
+ ListKey tmpListKey = VerseKey::ParseVerseList(keytext);
+ if (tmpListKey.Count()) {
+ SWKey::setText((const char *)tmpListKey);
+ for (int i = 1; i < 3; i++) {
+ for (int j = 1; j <= BMAX[i-1]; j++) {
+ int matchlen = strlen(books[i-1][j-1].name);
+ if (!strncmp(keytext, books[i-1][j-1].name, matchlen)) {
+ if (matchlen > booklen) {
+ booklen = matchlen;
+ testament = i;
+ book = j;
+ }
+ }
+ }
+ }
+
+ if (booklen) {
+ sscanf(&keytext[booklen], "%d:%d", &chapter, &verse);
+ }
+ else error = 1;
+ } else error = 1;
+ }
+ if (checkAutoNormalize) {
+ Normalize(1);
+ }
+ freshtext();
+
+ return (this->error) ? this->error : (this->error = error);
+}
+
+
+/******************************************************************************
+ * VerseKey::freshtext - refreshes keytext based on
+ * testament|book|chapter|verse
+ */
+
+void VerseKey::freshtext() const
+{
+ char buf[2024];
+ int realtest = testament;
+ int realbook = book;
+
+ if (book < 1) {
+ if (testament < 1)
+ sprintf(buf, "[ Module Heading ]");
+ else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
+ }
+ else {
+ if (realbook > BMAX[realtest-1]) {
+ realbook -= BMAX[realtest-1];
+ if (realtest < 2)
+ realtest++;
+ if (realbook > BMAX[realtest-1])
+ realbook = BMAX[realtest-1];
+ }
+ sprintf(buf, "%s %d:%d", books[realtest-1][realbook-1].name, chapter, verse);
+ }
+
+ stdstr((char **)&keytext, buf);
+}
+
+
+
+/******************************************************************************
+ * VerseKey::getBookAbbrev - Attempts to find a book abbreviation for a buffer
+ *
+ * ENT: abbr - key for which to search;
+ * RET: book number or < 0 = not valid
+ */
+
+int VerseKey::getBookAbbrev(const char *iabbr)
+{
+ int diff, abLen, min, max, target, retVal = -1;
+
+ char *abbr = 0;
+
+ StringMgr* stringMgr = StringMgr::getSystemStringMgr();
+ const bool hasUTF8Support = StringMgr::hasUTF8Support();
+
+ // The first iteration of this loop tries to uppercase
+ // the string. If we still fail to match, we try
+ // matching the string without any toupper logic.
+ // This is useful for, say, Chinese user input
+ // on a system that doesn't properly support
+ // a true Unicode-toupper function (!hasUTF8Support)
+ for (int i = 0; i < 2; i++) {
+ stdstr(&abbr, iabbr, 2);
+ strstrip(abbr);
+
+ if (!i) {
+ if (hasUTF8Support) { //we have support for UTF-8 handling; we expect UTF-8 encoded locales
+ stringMgr->upperUTF8(abbr, strlen(abbr)*2);
+ }
+ else {
+ stringMgr->upperLatin1(abbr);
+ }
+ }
+
+ abLen = strlen(abbr);
+
+ if (abLen) {
+ min = 0;
+ max = abbrevsCnt;
+
+ // binary search for a match
+ while(1) {
+ target = min + ((max - min) / 2);
+ diff = strncmp(abbr, abbrevs[target].ab, abLen);
+ if ((!diff)||(target >= max)||(target <= min))
+ break;
+ if (diff > 0)
+ min = target;
+ else max = target;
+ }
+
+ // lets keep backing up till we find the 'first' valid match
+ for (; target > 0; target--) {
+ if (strncmp(abbr, abbrevs[target-1].ab, abLen))
+ break;
+ }
+
+ retVal = (!diff) ? abbrevs[target].book : -1;
+ }
+ if (retVal > 0)
+ break;
+ }
+ delete [] abbr;
+ return retVal;
+}
+
+
+/******************************************************************************
+ * VerseKey::ParseVerseList - Attempts to parse a buffer into separate
+ * verse entries returned in a ListKey
+ *
+ * ENT: buf - buffer to parse;
+ * defaultKey - if verse, chap, book, or testament is left off,
+ * pull info from this key (ie. Gen 2:3; 4:5;
+ * Gen would be used when parsing the 4:5 section)
+ * expandRange - whether or not to expand eg. John 1:10-12 or just
+ * save John 1:10
+ *
+ * RET: ListKey reference filled with verse entries contained in buf
+ *
+ * COMMENT: This code works but wreaks. Rewrite to make more maintainable.
+ */
+
+ListKey VerseKey::ParseVerseList(const char *buf, const char *defaultKey, bool expandRange) {
+ char book[2048];
+ char number[2048];
+ *book = 0;
+ *number = 0;
+ int tobook = 0;
+ int tonumber = 0;
+ int chap = -1, verse = -1;
+ int bookno = 0;
+ VerseKey curKey, lBound, lastKey;
+ curKey.setLocale(getLocale());
+ lBound.setLocale(getLocale());
+ lastKey.setLocale(getLocale());
+ int loop;
+ char comma = 0;
+ char dash = 0;
+ const char *orig = buf;
+ int q;
+ ListKey tmpListKey;
+ ListKey internalListKey;
+ char lastPartial = 0;
+ bool inTerm = true;
+ int notAllDigits = 0;
+
+ curKey.AutoNormalize(0);
+ if (defaultKey) lastKey = defaultKey;
+
+ while (*buf) {
+ switch (*buf) {
+ case ':':
+ if (buf[1] != ' ') { // for silly Mat 1:1: this verse....
+ number[tonumber] = 0;
+ tonumber = 0;
+ if (*number)
+ chap = atoi(number);
+ *number = 0;
+ comma = 0;
+ break;
+ }
+ // otherwise drop down to next case
+ case ' ':
+ inTerm = true;
+ while (true) {
+ if ((!*number) || (chap < 0))
+ break;
+ for (q = 1; ((buf[q]) && (buf[q] != ' ')); q++);
+ if (buf[q] == ':')
+ break;
+ inTerm = false;
+ break;
+ }
+ if (inTerm) {
+ book[tobook++] = ' ';
+ break;
+ }
+
+ case '-':
+ case ',': // on number new verse
+ case ';': // on number new chapter
+ number[tonumber] = 0;
+ tonumber = 0;
+ if (*number) {
+ if (chap >= 0)
+ verse = atoi(number);
+ else chap = atoi(number);
+ }
+ *number = 0;
+ book[tobook] = 0;
+ tobook = 0;
+ bookno = -1;
+ if (*book) {
+ for (loop = strlen(book) - 1; loop+1; loop--) {
+ if ((isdigit(book[loop])) || (book[loop] == ' ')) {
+ book[loop] = 0;
+ continue;
+ }
+ else {
+ if ((SW_toupper(book[loop])=='F')&&(loop)) {
+ if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
+ book[loop] = 0;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+
+ for (loop = strlen(book) - 1; loop+1; loop--) {
+ if (book[loop] == ' ') {
+ if (isroman(&book[loop+1])) {
+ if (verse == -1) {
+ verse = chap;
+ chap = from_rom(&book[loop+1]);
+ book[loop] = 0;
+ }
+ }
+ break;
+ }
+ }
+
+ if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev
+ if (verse == -1) {
+ verse = chap;
+ chap = lastKey.Chapter();
+ *book = 0;
+ }
+ }
+ if ((!stricmp(book, "ch")) || (!stricmp(book, "chap"))) { // Verse abbrev
+ strcpy(book, lastKey.getBookName());
+ }
+ bookno = getBookAbbrev(book);
+ }
+ if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
+ char partial = 0;
+ curKey.Verse(1);
+ curKey.Chapter(1);
+ curKey.Book(1);
+
+ if (bookno < 0) {
+ curKey.Testament(lastKey.Testament());
+ curKey.Book(lastKey.Book());
+ }
+ else {
+ curKey.Testament(1);
+ curKey.Book(bookno);
+ }
+
+ if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
+// if (comma) {
+ curKey.Chapter(lastKey.Chapter());
+ curKey.Verse(chap); // chap because this is the first number captured
+ }
+ else {
+ if (chap >= 0) {
+ curKey.Chapter(chap);
+ }
+ else {
+ partial++;
+ curKey.Chapter(1);
+ }
+ if (verse >= 0) {
+ curKey.Verse(verse);
+ }
+ else {
+ partial++;
+ curKey.Verse(1);
+ }
+ }
+
+ // check for '-'
+ for (q = 0; ((buf[q]) && (buf[q] == ' ')); q++);
+ if ((buf[q] == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper
+ buf+=q;
+ lastKey.LowerBound(curKey);
+ lastKey.setPosition(TOP);
+ tmpListKey << lastKey;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ else {
+ if (!dash) { // if last separator was not a dash just add
+ if (expandRange && partial) {
+ lastKey.LowerBound(curKey);
+ if (partial > 1)
+ curKey.setPosition(MAXCHAPTER);
+ if (partial > 0)
+ curKey = MAXVERSE;
+ lastKey.UpperBound(curKey);
+ lastKey = TOP;
+ tmpListKey << lastKey;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ else {
+ // we store non-range entries as strings so we don't traverse
+ // maybe we should consider just setting
+ // lowerBound and upperBound to the same value
+ tmpListKey << curKey.getText();
+ tmpListKey.GetElement()->userData = (void *)buf;
+ lastKey = curKey;
+ }
+ }
+ else if (expandRange) {
+ VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
+ if (newElement) {
+ if (partial > 1)
+ curKey = MAXCHAPTER;
+ if (partial > 0)
+ curKey = MAXVERSE;
+ newElement->UpperBound(curKey);
+ *newElement = TOP;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ }
+ }
+ lastPartial = partial;
+ }
+ *book = 0;
+ chap = -1;
+ verse = -1;
+ if (*buf == ',')
+ comma = 1;
+ else comma = 0;
+ if (*buf == '-')
+ dash = 1;
+ else dash = 0;
+ break;
+ case 10: // ignore these
+ case 13:
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ break;
+ case '.':
+ if (buf > orig) // ignore (break) if preceeding char is not a digit
+ for (notAllDigits = tobook; notAllDigits; notAllDigits--) {
+ if ((!isdigit(book[notAllDigits-1])) && (!strchr(" .", book[notAllDigits-1])))
+ break;
+ }
+ if (!notAllDigits)
+ break;
+
+ number[tonumber] = 0;
+ tonumber = 0;
+ if (*number)
+ chap = atoi(number);
+ *number = 0;
+ break;
+
+ default:
+ if (isdigit(*buf)) {
+ number[tonumber++] = *buf;
+ }
+ else {
+ switch (*buf) {
+ case ' ': // ignore these and don't reset number
+ case 'f':
+ case 'F':
+ break;
+ default:
+ number[tonumber] = 0;
+ tonumber = 0;
+ break;
+ }
+ }
+ if (chap == -1)
+ book[tobook++] = *buf;
+ }
+ buf++;
+ }
+ number[tonumber] = 0;
+ tonumber = 0;
+ if (*number) {
+ if (chap >= 0)
+ verse = atoi(number);
+ else chap = atoi(number);
+ }
+ *number = 0;
+ book[tobook] = 0;
+ tobook = 0;
+ if (*book) {
+ for (loop = strlen(book) - 1; loop+1; loop--) {
+ if ((isdigit(book[loop])) || (book[loop] == ' ')) {
+ book[loop] = 0;
+ continue;
+ }
+ else {
+ if ((SW_toupper(book[loop])=='F')&&(loop)) {
+ if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
+ book[loop] = 0;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+
+ for (loop = strlen(book) - 1; loop+1; loop--) {
+ if (book[loop] == ' ') {
+ if (isroman(&book[loop+1])) {
+ if (verse == -1) {
+ verse = chap;
+ chap = from_rom(&book[loop+1]);
+ book[loop] = 0;
+ }
+ }
+ break;
+ }
+ }
+
+ if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev.
+ if (verse == -1) {
+ verse = chap;
+ chap = lastKey.Chapter();
+ *book = 0;
+ }
+ }
+
+ if ((!stricmp(book, "ch")) || (!stricmp(book, "chap"))) { // Verse abbrev
+ strcpy(book, lastKey.getBookName());
+ }
+ bookno = getBookAbbrev(book);
+ }
+ if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
+ char partial = 0;
+ curKey.Verse(1);
+ curKey.Chapter(1);
+ curKey.Book(1);
+
+ if (bookno < 0) {
+ curKey.Testament(lastKey.Testament());
+ curKey.Book(lastKey.Book());
+ }
+ else {
+ curKey.Testament(1);
+ curKey.Book(bookno);
+ }
+
+ if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
+ curKey.Chapter(lastKey.Chapter());
+ curKey.Verse(chap); // chap because this is the first number captured
+ }
+ else {
+ if (chap >= 0) {
+ curKey.Chapter(chap);
+ }
+ else {
+ partial++;
+ curKey.Chapter(1);
+ }
+ if (verse >= 0) {
+ curKey.Verse(verse);
+ }
+ else {
+ partial++;
+ curKey.Verse(1);
+ }
+ }
+
+ if ((*buf == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper
+ lastKey.LowerBound(curKey);
+ lastKey = TOP;
+ tmpListKey << lastKey;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ else {
+ if (!dash) { // if last separator was not a dash just add
+ if (expandRange && partial) {
+ lastKey.LowerBound(curKey);
+ if (partial > 1)
+ curKey = MAXCHAPTER;
+ if (partial > 0)
+ curKey = MAXVERSE;
+ lastKey.UpperBound(curKey);
+ lastKey = TOP;
+ tmpListKey << lastKey;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ else {
+ tmpListKey << curKey.getText();
+ tmpListKey.GetElement()->userData = (void *)buf;
+ lastKey = curKey;
+ }
+ }
+ else if (expandRange) {
+ VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
+ if (newElement) {
+ if (partial > 1)
+ curKey = MAXCHAPTER;
+ if (partial > 0)
+ curKey = MAXVERSE;
+ newElement->UpperBound(curKey);
+ *newElement = TOP;
+ tmpListKey.GetElement()->userData = (void *)buf;
+ }
+ }
+ }
+ }
+ *book = 0;
+ tmpListKey = TOP;
+ internalListKey = tmpListKey;
+ internalListKey = TOP; // Align internalListKey to first element before passing back;
+
+ return internalListKey;
+}
+
+
+/******************************************************************************
+ * VerseKey::LowerBound - sets / gets the lower boundary for this key
+ */
+
+VerseKey &VerseKey::LowerBound(const char *lb)
+{
+ if (!lowerBound)
+ initBounds();
+
+ (*lowerBound) = lb;
+ lowerBound->Normalize();
+ lowerBound->setLocale( this->getLocale() );
+ boundSet = true;
+ return (*lowerBound);
+}
+
+
+/******************************************************************************
+ * VerseKey::UpperBound - sets / gets the upper boundary for this key
+ */
+
+VerseKey &VerseKey::UpperBound(const char *ub)
+{
+ if (!upperBound)
+ initBounds();
+
+// need to set upperbound parsing to resolve to max verse/chap if not specified
+ (*upperBound) = ub;
+ if (*upperBound < *lowerBound)
+ *upperBound = *lowerBound;
+ upperBound->Normalize();
+ upperBound->setLocale( this->getLocale() );
+
+// until we have a proper method to resolve max verse/chap use this kludge
+ int len = strlen(ub);
+ bool alpha = false;
+ bool versespec = false;
+ bool chapspec = false;
+ for (int i = 0; i < len; i++) {
+ if (isalpha(ub[i]))
+ alpha = true;
+ if (ub[i] == ':') // if we have a : we assume verse spec
+ versespec = true;
+ if ((isdigit(ub[i])) && (alpha)) // if digit after alpha assume chap spec
+ chapspec = true;
+ }
+ if (!chapspec)
+ *upperBound = MAXCHAPTER;
+ if (!versespec)
+ *upperBound = MAXVERSE;
+
+
+// -- end kludge
+ boundSet = true;
+ return (*upperBound);
+}
+
+
+/******************************************************************************
+ * VerseKey::LowerBound - sets / gets the lower boundary for this key
+ */
+
+VerseKey &VerseKey::LowerBound() const
+{
+ if (!lowerBound)
+ initBounds();
+
+ return (*lowerBound);
+}
+
+
+/******************************************************************************
+ * VerseKey::UpperBound - sets / gets the upper boundary for this key
+ */
+
+VerseKey &VerseKey::UpperBound() const
+{
+ if (!upperBound)
+ initBounds();
+
+ return (*upperBound);
+}
+
+
+/******************************************************************************
+ * VerseKey::ClearBounds - clears bounds for this VerseKey
+ */
+
+void VerseKey::ClearBounds()
+{
+ initBounds();
+}
+
+
+void VerseKey::initBounds() const
+{
+ if (!upperBound) {
+ upperBound = new VerseKey();
+ upperBound->AutoNormalize(0);
+ upperBound->Headings(1);
+ }
+ if (!lowerBound) {
+ lowerBound = new VerseKey();
+ lowerBound->AutoNormalize(0);
+ lowerBound->Headings(1);
+ }
+
+ lowerBound->Testament(0);
+ lowerBound->Book(0);
+ lowerBound->Chapter(0);
+ lowerBound->Verse(0);
+
+ upperBound->Testament(2);
+ upperBound->Book(BMAX[1]);
+ upperBound->Chapter(books[1][BMAX[1]-1].chapmax);
+ upperBound->Verse(books[1][BMAX[1]-1].versemax[upperBound->Chapter()-1]);
+ boundSet = false;
+}
+
+
+/******************************************************************************
+ * VerseKey::getText - refreshes keytext before returning if cast to
+ * a (char *) is requested
+ */
+
+const char *VerseKey::getText() const {
+ freshtext();
+ return keytext;
+}
+
+
+const char *VerseKey::getShortText() const {
+ static char *stext = 0;
+ char buf[2047];
+ freshtext();
+ if (book < 1) {
+ if (testament < 1)
+ sprintf(buf, "[ Module Heading ]");
+ else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
+ }
+ else {
+ sprintf(buf, "%s %d:%d", books[testament-1][book-1].prefAbbrev, chapter, verse);
+ }
+ stdstr(&stext, buf);
+ return stext;
+}
+
+
+const char *VerseKey::getBookName() const {
+ return books[testament-1][book-1].name;
+}
+
+
+const char *VerseKey::getBookAbbrev() const {
+ return books[testament-1][book-1].prefAbbrev;
+}
+/******************************************************************************
+ * VerseKey::setPosition(SW_POSITION) - Positions this key
+ *
+ * ENT: p - position
+ *
+ * RET: *this
+ */
+
+void VerseKey::setPosition(SW_POSITION p) {
+ switch (p) {
+ case POS_TOP:
+ testament = LowerBound().Testament();
+ book = LowerBound().Book();
+ chapter = LowerBound().Chapter();
+ verse = LowerBound().Verse();
+ break;
+ case POS_BOTTOM:
+ testament = UpperBound().Testament();
+ book = UpperBound().Book();
+ chapter = UpperBound().Chapter();
+ verse = UpperBound().Verse();
+ break;
+ case POS_MAXVERSE:
+ Normalize();
+ verse = books[testament-1][book-1].versemax[chapter-1];
+ break;
+ case POS_MAXCHAPTER:
+ verse = 1;
+ Normalize();
+ chapter = books[testament-1][book-1].chapmax;
+ break;
+ }
+ Normalize(1);
+ Error(); // clear error from normalize
+}
+
+
+/******************************************************************************
+ * VerseKey::increment - Increments key a number of verses
+ *
+ * ENT: step - Number of verses to jump forward
+ *
+ * RET: *this
+ */
+
+void VerseKey::increment(int step) {
+ char ierror = 0;
+ Index(Index() + step);
+ while ((!verse) && (!headings) && (!ierror)) {
+ Index(Index() + 1);
+ ierror = Error();
+ }
+
+ error = (ierror) ? ierror : error;
+}
+
+
+/******************************************************************************
+ * VerseKey::decrement - Decrements key a number of verses
+ *
+ * ENT: step - Number of verses to jump backward
+ *
+ * RET: *this
+ */
+
+void VerseKey::decrement(int step) {
+ char ierror = 0;
+
+ Index(Index() - step);
+ while ((!verse) && (!headings) && (!ierror)) {
+ Index(Index() - 1);
+ ierror = Error();
+ }
+ if ((ierror) && (!headings))
+ (*this)++;
+
+ error = (ierror) ? ierror : error;
+}
+
+
+/******************************************************************************
+ * VerseKey::Normalize - checks limits and normalizes if necessary (e.g.
+ * Matthew 29:47 = Mark 2:2). If last verse is
+ * exceeded, key is set to last Book CH:VS
+ * RET: *this
+ */
+
+void VerseKey::Normalize(char autocheck)
+{
+ error = 0;
+
+ if (((!autocheck) || (autonorm)) // only normalize if we were explicitely called or if autonorm is turned on
+ &&
+ ((!headings) || ((verse) && (chapter)))) { // this is cheeze and temporary until deciding what actions should be taken; so headings should only be turned on when positioning with Index() or incrementors
+
+ while ((testament < 3) && (testament > 0)) {
+
+ if (book > BMAX[testament-1]) {
+ book -= BMAX[testament-1];
+ testament++;
+ continue;
+ }
+
+ if (book < 1) {
+ if (--testament > 0) {
+ book += BMAX[testament-1];
+ }
+ continue;
+ }
+
+ if (chapter > books[testament-1][book-1].chapmax) {
+ chapter -= books[testament-1][book-1].chapmax;
+ book++;
+ continue;
+ }
+
+ if (chapter < 1) {
+ if (--book > 0) {
+ chapter += books[testament-1][book-1].chapmax;
+ }
+ else {
+ if (testament > 1) {
+ chapter += books[0][BMAX[0]-1].chapmax;
+ }
+ }
+ continue;
+ }
+
+ if (verse > books[testament-1][book-1].versemax[chapter-1]) { // -1 because e.g chapter 1 of Matthew is books[1][0].versemax[0]
+ verse -= books[testament-1][book-1].versemax[chapter++ - 1];
+ continue;
+ }
+
+ if (verse < 1) {
+ if (--chapter > 0) {
+ verse += books[testament-1][book-1].versemax[chapter-1];
+ }
+ else {
+ if (book > 1) {
+ verse += books[testament-1][book-2].versemax[books[testament-1][book-2].chapmax-1];
+ }
+ else {
+ if (testament > 1) {
+ verse += books[0][BMAX[0]-1].versemax[books[0][BMAX[0]-1].chapmax-1];
+ }
+ }
+ }
+ continue;
+ }
+
+ break; // If we've made it this far (all failure checks continue) we're ok
+ }
+
+ if (testament > 2) {
+ testament = 2;
+ book = BMAX[testament-1];
+ chapter = books[testament-1][book-1].chapmax;
+ verse = books[testament-1][book-1].versemax[chapter-1];
+ error = KEYERR_OUTOFBOUNDS;
+ }
+
+ if (testament < 1) {
+ error = ((!headings) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0;
+ testament = ((headings) ? 0 : 1);
+ book = ((headings) ? 0 : 1);
+ chapter = ((headings) ? 0 : 1);
+ verse = ((headings) ? 0 : 1);
+ }
+
+ // should we always perform bounds checks? Tried but seems to cause infinite recursion
+ if (_compare(UpperBound()) > 0) {
+ setText(UpperBound(), false);
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ if (_compare(LowerBound()) < 0) {
+ setText(LowerBound(), false);
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ }
+}
+
+
+/******************************************************************************
+ * VerseKey::Testament - Gets testament
+ *
+ * RET: value of testament
+ */
+
+char VerseKey::Testament() const
+{
+ return testament;
+}
+
+
+/******************************************************************************
+ * VerseKey::Book - Gets book
+ *
+ * RET: value of book
+ */
+
+char VerseKey::Book() const
+{
+ return book;
+}
+
+
+/******************************************************************************
+ * VerseKey::Chapter - Gets chapter
+ *
+ * RET: value of chapter
+ */
+
+int VerseKey::Chapter() const
+{
+ return chapter;
+}
+
+
+/******************************************************************************
+ * VerseKey::Verse - Gets verse
+ *
+ * RET: value of verse
+ */
+
+int VerseKey::Verse() const
+{
+ return verse;
+}
+
+
+/******************************************************************************
+ * VerseKey::Testament - Sets/gets testament
+ *
+ * ENT: itestament - value which to set testament
+ * [MAXPOS(char)] - only get
+ *
+ * RET: if unchanged -> value of testament
+ * if changed -> previous value of testament
+ */
+
+char VerseKey::Testament(char itestament)
+{
+ char retval = testament;
+
+ if (itestament != MAXPOS(char)) {
+ testament = itestament;
+ Normalize(1);
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::Book - Sets/gets book
+ *
+ * ENT: ibook - value which to set book
+ * [MAXPOS(char)] - only get
+ *
+ * RET: if unchanged -> value of book
+ * if changed -> previous value of book
+ */
+
+char VerseKey::Book(char ibook)
+{
+ char retval = book;
+
+ Chapter(1);
+ book = ibook;
+ Normalize(1);
+
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::Chapter - Sets/gets chapter
+ *
+ * ENT: ichapter - value which to set chapter
+ * [MAXPOS(int)] - only get
+ *
+ * RET: if unchanged -> value of chapter
+ * if changed -> previous value of chapter
+ */
+
+int VerseKey::Chapter(int ichapter)
+{
+ int retval = chapter;
+
+ Verse(1);
+ chapter = ichapter;
+ Normalize(1);
+
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::Verse - Sets/gets verse
+ *
+ * ENT: iverse - value which to set verse
+ * [MAXPOS(int)] - only get
+ *
+ * RET: if unchanged -> value of verse
+ * if changed -> previous value of verse
+ */
+
+int VerseKey::Verse(int iverse)
+{
+ int retval = verse;
+
+ verse = iverse;
+ Normalize(1);
+
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::AutoNormalize - Sets/gets flag that tells VerseKey to auto-
+ * matically normalize itself when modified
+ *
+ * ENT: iautonorm - value which to set autonorm
+ * [MAXPOS(char)] - only get
+ *
+ * RET: if unchanged -> value of autonorm
+ * if changed -> previous value of autonorm
+ */
+
+char VerseKey::AutoNormalize(char iautonorm)
+{
+ char retval = autonorm;
+
+ if (iautonorm != MAXPOS(char)) {
+ autonorm = iautonorm;
+ Normalize(1);
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::Headings - Sets/gets flag that tells VerseKey to include
+ * chap/book/testmnt/module headings
+ *
+ * ENT: iheadings - value which to set headings
+ * [MAXPOS(char)] - only get
+ *
+ * RET: if unchanged -> value of headings
+ * if changed -> previous value of headings
+ */
+
+char VerseKey::Headings(char iheadings)
+{
+ char retval = headings;
+
+ if (iheadings != MAXPOS(char)) {
+ headings = iheadings;
+ Normalize(1);
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * VerseKey::findindex - binary search to find the index closest, but less
+ * than the given value.
+ *
+ * ENT: array - long * to array to search
+ * size - number of elements in the array
+ * value - value to find
+ *
+ * RET: the index into the array that is less than but closest to value
+ */
+
+int VerseKey::findindex(long *array, int size, long value)
+{
+ int lbound, ubound, tval;
+
+ lbound = 0;
+ ubound = size - 1;
+ while ((ubound - lbound) > 1) {
+ tval = lbound + (ubound-lbound)/2;
+ if (array[tval] <= value)
+ lbound = tval;
+ else ubound = tval;
+ }
+ return (array[ubound] <= value) ? ubound : lbound;
+}
+
+
+/******************************************************************************
+ * VerseKey::Index - Gets index based upon current verse
+ *
+ * RET: offset
+ */
+
+long VerseKey::Index() const
+{
+ long offset;
+
+ if (!testament) { // if we want module heading
+ offset = 0;
+ verse = 0;
+ }
+ else {
+ if (!book)
+ chapter = 0;
+ if (!chapter)
+ verse = 0;
+
+ offset = offsets[testament-1][0][book];
+ offset = offsets[testament-1][1][(int)offset + chapter];
+ if (!(offset|verse)) // if we have a testament but nothing else.
+ offset = 1;
+ }
+ return (offset + verse);
+}
+
+
+/******************************************************************************
+ * VerseKey::Index - Gets index based upon current verse
+ *
+ * RET: offset
+ */
+
+long VerseKey::NewIndex() const
+{
+ static long otMaxIndex = 32300 - 8245; // total positions - new testament positions
+// static long otMaxIndex = offsets[0][1][(int)offsets[0][0][BMAX[0]] + books[0][BMAX[0]].chapmax];
+ return ((testament-1) * otMaxIndex) + Index();
+}
+
+
+/******************************************************************************
+ * VerseKey::Index - Sets index based upon current verse
+ *
+ * ENT: iindex - value to set index to
+ *
+ * RET: offset
+ */
+
+long VerseKey::Index(long iindex)
+{
+ long offset;
+
+// This is the dirty stuff --------------------------------------------
+
+ if (!testament)
+ testament = 1;
+
+ if (iindex < 1) { // if (-) or module heading
+ if (testament < 2) {
+ if (iindex < 0) {
+ testament = 0; // previously we changed 0 -> 1
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else testament = 0; // we want module heading
+ }
+ else {
+ testament--;
+ iindex = (offsets[testament-1][1][offsize[testament-1][1]-1] + books[testament-1][BMAX[testament-1]-1].versemax[books[testament-1][BMAX[testament-1]-1].chapmax-1]) + iindex; // What a doozy! ((offset of last chapter + number of verses in the last chapter) + iindex)
+ }
+ }
+
+// --------------------------------------------------------------------
+
+
+ if (testament) {
+ if ((!error) && (iindex)) {
+ offset = findindex(offsets[testament-1][1], offsize[testament-1][1], iindex);
+ verse = iindex - offsets[testament-1][1][offset];
+ book = findindex(offsets[testament-1][0], offsize[testament-1][0], offset);
+ chapter = offset - offsets[testament-1][0][VerseKey::book];
+ verse = (chapter) ? verse : 0; // funny check. if we are index=1 (testmt header) all gets set to 0 exept verse. Don't know why. Fix if you figure out. Think its in the offsets table.
+ if (verse) { // only check if -1 won't give negative
+ if (verse > books[testament-1][book-1].versemax[chapter-1]) {
+ if (testament > 1) {
+ verse = books[testament-1][book-1].versemax[chapter-1];
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ else {
+ testament++;
+ Index(verse - books[testament-2][book-1].versemax[chapter-1]);
+ }
+ }
+ }
+ }
+ }
+ if (_compare(UpperBound()) > 0) {
+ *this = UpperBound();
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ if (_compare(LowerBound()) < 0) {
+ *this = LowerBound();
+ error = KEYERR_OUTOFBOUNDS;
+ }
+ return Index();
+}
+
+
+/******************************************************************************
+ * VerseKey::compare - Compares another SWKey object
+ *
+ * ENT: ikey - key to compare with this one
+ *
+ * RET: >0 if this versekey is greater than compare versekey
+ * <0 <
+ * 0 =
+ */
+
+int VerseKey::compare(const SWKey &ikey)
+{
+ const SWKey *testKey = &ikey;
+ const VerseKey *vkey = (const VerseKey *)SWDYNAMIC_CAST(VerseKey, testKey);
+ if (vkey) {
+ return _compare(*vkey);
+ }
+ const VerseKey ivkey = (const char *)ikey;
+ return _compare(ivkey);
+}
+
+
+/******************************************************************************
+ * VerseKey::_compare - Compares another VerseKey object
+ *
+ * ENT: ikey - key to compare with this one
+ *
+ * RET: >0 if this versekey is greater than compare versekey
+ * <0 <
+ * 0 =
+ */
+
+int VerseKey::_compare(const VerseKey &ivkey)
+{
+ long keyval1 = 0;
+ long keyval2 = 0;
+
+ keyval1 += Testament() * 1000000000;
+ keyval2 += ivkey.Testament() * 1000000000;
+ keyval1 += Book() * 1000000;
+ keyval2 += ivkey.Book() * 1000000;
+ keyval1 += Chapter() * 1000;
+ keyval2 += ivkey.Chapter() * 1000;
+ keyval1 += Verse();
+ keyval2 += ivkey.Verse();
+ keyval1 -= keyval2;
+ keyval1 = (keyval1) ? ((keyval1 > 0) ? 1 : -1) /*keyval1/labs(keyval1)*/:0; // -1 | 0 | 1
+ return keyval1;
+}
+
+const char *VerseKey::osisotbooks[] = {
+ "Gen","Exod","Lev","Num","Deut","Josh","Judg","Ruth","1Sam","2Sam",
+ "1Kgs","2Kgs","1Chr","2Chr","Ezra","Neh","Esth","Job","Ps",
+ "Prov", // added this. Was not in OSIS spec
+ "Eccl",
+ "Song","Isa","Jer","Lam","Ezek","Dan","Hos","Joel","Amos","Obad",
+ "Jonah","Mic","Nah","Hab","Zeph","Hag","Zech","Mal","Bar","PrAzar",
+ "Bel","Sus","1Esd","2Esd","AddEsth","EpJer","Jdt","1Macc","2Macc","3Macc",
+ "4Macc","PrMan","Ps151","Sir","Tob","Wis"};
+const char *VerseKey::osisntbooks[] = {
+ "Matt","Mark","Luke","John","Acts","Rom","1Cor","2Cor","Gal","Eph",
+ "Phil","Col","1Thess","2Thess","1Tim","2Tim","Titus","Phlm","Heb","Jas",
+ "1Pet","2Pet","1John","2John","3John","Jude","Rev"};
+const char **VerseKey::osisbooks[] = { osisotbooks, osisntbooks };
+
+
+const char *VerseKey::getOSISRef() const {
+ static char buf[5][254];
+ static int loop = 0;
+
+ if (loop > 4)
+ loop = 0;
+
+ if (Verse())
+ sprintf(buf[loop], "%s.%d.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter(), (int)Verse());
+ else if (Chapter())
+ sprintf(buf[loop], "%s.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter());
+ else if (Book())
+ sprintf(buf[loop], "%s", osisbooks[Testament()-1][Book()-1]);
+ else buf[loop][0] = 0;
+ return buf[loop++];
+}
+
+const int VerseKey::getOSISBookNum(const char *bookab) {
+ int i;
+ for (i=0; i < 39; i++)
+ {
+ if (!strncmp(bookab, osisotbooks[i], strlen(osisotbooks[i])))
+ {
+ //printf("VerseKey::getOSISBookNum %s is OT %d\n", bookab, i+1);
+ return i+1;
+ }
+ }
+ for (i=0; i < 27; i++)
+ {
+ if (!strncmp(bookab, osisntbooks[i], strlen(osisotbooks[i])))
+ {
+ //printf("VerseKey::getOSISBookNum %s is NT %d\n", bookab, i+1);
+ return i+1;
+ }
+ }
+ return -1;
+}
+
+
+
+/******************************************************************************
+ * VerseKey::getRangeText - returns parsable range text for this key
+ */
+
+const char *VerseKey::getRangeText() const {
+ if (isBoundSet()) {
+ char buf[1023];
+ sprintf(buf, "%s-%s", (const char *)LowerBound(), (const char *)UpperBound());
+ stdstr(&rangeText, buf);
+ }
+ else stdstr(&rangeText, getText());
+ return rangeText;
+}
+
+
+const char *VerseKey::convertToOSIS(const char *inRef, const SWKey *lastKnownKey) {
+ static SWBuf outRef;
+
+ outRef = "";
+
+ VerseKey defLanguage;
+ ListKey verses = defLanguage.ParseVerseList(inRef, (*lastKnownKey), true);
+ const char *startFrag = inRef;
+ for (int i = 0; i < verses.Count(); i++) {
+ VerseKey *element = SWDYNAMIC_CAST(VerseKey, verses.GetElement(i));
+ char buf[5120];
+ char frag[800];
+ char preJunk[800];
+ char postJunk[800];
+ memset(buf, 0, 5120);
+ memset(frag, 0, 800);
+ memset(preJunk, 0, 800);
+ memset(postJunk, 0, 800);
+ while ((*startFrag) && (strchr(" {};,()[].", *startFrag))) {
+ outRef += *startFrag;
+ startFrag++;
+ }
+ if (element) {
+ memmove(frag, startFrag, ((const char *)element->userData - startFrag) + 1);
+ frag[((const char *)element->userData - startFrag) + 1] = 0;
+ int j;
+ for (j = strlen(frag)-1; j && (strchr(" {};,()[].", frag[j])); j--);
+ if (frag[j+1])
+ strcpy(postJunk, frag+j+1);
+ frag[j+1]=0;
+ startFrag += ((const char *)element->userData - startFrag) + 1;
+ sprintf(buf, "<reference osisRef=\"%s-%s\">%s</reference>%s", element->LowerBound().getOSISRef(), element->UpperBound().getOSISRef(), frag, postJunk);
+ }
+ else {
+ memmove(frag, startFrag, ((const char *)verses.GetElement(i)->userData - startFrag) + 1);
+ frag[((const char *)verses.GetElement(i)->userData - startFrag) + 1] = 0;
+ int j;
+ for (j = strlen(frag)-1; j && (strchr(" {};,()[].", frag[j])); j--);
+ if (frag[j+1])
+ strcpy(postJunk, frag+j+1);
+ frag[j+1]=0;
+ startFrag += ((const char *)verses.GetElement(i)->userData - startFrag) + 1;
+ sprintf(buf, "<reference osisRef=\"%s\">%s</reference>%s", VerseKey(*verses.GetElement(i)).getOSISRef(), frag, postJunk);
+ }
+ outRef+=buf;
+ }
+ if (startFrag < (inRef + strlen(inRef)))
+ outRef+=startFrag;
+ return outRef.c_str();
+}
+SWORD_NAMESPACE_END
diff --git a/src/keys/versetreekey.cpp b/src/keys/versetreekey.cpp
new file mode 100644
index 0000000..b73672d
--- /dev/null
+++ b/src/keys/versetreekey.cpp
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * VerseTreeKey.cpp - code for class 'VerseTreeKey'- versekey using treekey
+ * for data retrieval
+ */
+
+#include <versetreekey.h>
+
+SWORD_NAMESPACE_START
+
+static const char *classes[] = {"VerseTreeKey", "VerseKey", "SWKey", "SWObject", 0};
+SWClass VerseTreeKey::classdef(classes);
+
+
+/******************************************************************************
+ * VerseTreeKey Constructor - initializes instance of VerseTreeKey
+ *
+ * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'. See
+ * VerseTreeKey::parse for more detailed information)
+ */
+
+VerseTreeKey::VerseTreeKey(TreeKey *treeKey, const SWKey *ikey) : VerseKey(ikey)
+{
+ this->treeKey = treeKey;
+ if (ikey)
+ parse();
+}
+
+
+/******************************************************************************
+ * VerseTreeKey Constructor - initializes instance of VerseTreeKey
+ *
+ * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'. See
+ * VerseTreeKey::parse for more detailed information)
+ */
+
+VerseTreeKey::VerseTreeKey(TreeKey *treeKey, const char *ikey) : VerseKey(ikey)
+{
+ this->treeKey = treeKey;
+ if (ikey)
+ parse();
+}
+
+
+VerseTreeKey::VerseTreeKey(VerseTreeKey const &k) : VerseKey(k)
+{
+ treeKey = k.treeKey;
+}
+
+
+VerseTreeKey::VerseTreeKey(TreeKey *treeKey, const char *min, const char *max) : VerseKey(min, max)
+{
+ this->treeKey = treeKey;
+}
+
+
+SWKey *VerseTreeKey::clone() const
+{
+ return new VerseTreeKey(*this);
+}
+
+
+/******************************************************************************
+ * VerseTreeKey Destructor - cleans up instance of VerseTreeKey
+ *
+ * ENT: ikey - text key
+ */
+
+VerseTreeKey::~VerseTreeKey() {
+}
+
+
+SWORD_NAMESPACE_END
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 &section) {
+
+ 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 &section) {
+ 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 &section) {
+ 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 &section)
+{
+ 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(&section);
+ }
+
+ return newmod;
+}
+
+
+void SWMgr::AddGlobalOptions(SWModule *module, ConfigEntMap &section, 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 &section, 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 &section, 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 &section) {
+ 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 &section) {
+ if (filterMgr)
+ filterMgr->AddEncodingFilters(module, section);
+}
+
+
+void SWMgr::AddRenderFilters(SWModule *module, ConfigEntMap &section) {
+ 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 &section)
+{
+ 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 &section = (*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
diff --git a/src/modules/Makefile b/src/modules/Makefile
new file mode 100644
index 0000000..ef8eccd
--- /dev/null
+++ b/src/modules/Makefile
@@ -0,0 +1,5 @@
+
+root := ../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
new file mode 100644
index 0000000..944dc18
--- /dev/null
+++ b/src/modules/Makefile.am
@@ -0,0 +1,10 @@
+modulesdir = $(top_srcdir)/src/modules
+
+libsword_la_SOURCES += $(modulesdir)/swmodule.cpp
+
+include ../src/modules/common/Makefile.am
+include ../src/modules/filters/Makefile.am
+include ../src/modules/genbook/Makefile.am
+include ../src/modules/texts/Makefile.am
+include ../src/modules/comments/Makefile.am
+include ../src/modules/lexdict/Makefile.am
diff --git a/src/modules/comments/Makefile b/src/modules/comments/Makefile
new file mode 100644
index 0000000..1a2d00d
--- /dev/null
+++ b/src/modules/comments/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/comments/Makefile.am b/src/modules/comments/Makefile.am
new file mode 100644
index 0000000..7139cf8
--- /dev/null
+++ b/src/modules/comments/Makefile.am
@@ -0,0 +1,9 @@
+commentsdir = $(top_srcdir)/src/modules/comments
+
+libsword_la_SOURCES += $(commentsdir)/swcom.cpp
+
+include ../src/modules/comments/rawcom/Makefile.am
+include ../src/modules/comments/rawcom4/Makefile.am
+include ../src/modules/comments/rawfiles/Makefile.am
+include ../src/modules/comments/zcom/Makefile.am
+include ../src/modules/comments/hrefcom/Makefile.am
diff --git a/src/modules/comments/hrefcom/Makefile b/src/modules/comments/hrefcom/Makefile
new file mode 100644
index 0000000..35d6648
--- /dev/null
+++ b/src/modules/comments/hrefcom/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/comments/hrefcom/Makefile.am b/src/modules/comments/hrefcom/Makefile.am
new file mode 100644
index 0000000..dba4294
--- /dev/null
+++ b/src/modules/comments/hrefcom/Makefile.am
@@ -0,0 +1,2 @@
+hrefcomdir = $(top_srcdir)/src/modules/comments/hrefcom
+libsword_la_SOURCES += $(hrefcomdir)/hrefcom.cpp
diff --git a/src/modules/comments/hrefcom/hrefcom.cpp b/src/modules/comments/hrefcom/hrefcom.cpp
new file mode 100644
index 0000000..7791da2
--- /dev/null
+++ b/src/modules/comments/hrefcom/hrefcom.cpp
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * hrefcom.cpp - code for class 'HREFCom'- a module that produces HTML HREFs
+ * pointing to actual text desired. Uses standard
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <utilstr.h>
+#include <rawverse.h>
+#include <hrefcom.h>
+#include <swbuf.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+ /******************************************************************************
+ * HREFCom Constructor - Initializes data for instance of HREFCom
+ *
+ * ENT: iname - Internal name for module
+ * iprefix - string to prepend to each HREF (e.g. "file://mods/com/jfb/")
+ * idesc - Name to display to user for module
+ * idisp - Display object to use for displaying
+ */
+
+HREFCom::HREFCom(const char *ipath, const char *iprefix, const char *iname, const char *idesc, SWDisplay *idisp) : RawVerse(ipath), SWCom(iname, idesc, idisp)
+{
+ prefix = 0;
+ stdstr(&prefix, iprefix);
+}
+
+
+/******************************************************************************
+ * HREFCom Destructor - Cleans up instance of HREFCom
+ */
+
+HREFCom::~HREFCom()
+{
+ if (prefix)
+ delete [] prefix;
+}
+
+
+/******************************************************************************
+ * HREFCom::operator char * - Returns the correct verse when char * cast
+ * is requested
+ *
+ * RET: string buffer with verse
+ */
+
+SWBuf &HREFCom::getRawEntryBuf() {
+ long start;
+ unsigned short size;
+ VerseKey *key = 0;
+
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+ entrySize = size; // support getEntrySize call
+
+ SWBuf tmpbuf;
+
+ readText(key->Testament(), start, size, tmpbuf);
+ entryBuf = prefix;
+ entryBuf += tmpbuf.c_str();
+ prepText(entryBuf);
+
+ if (key != this->key)
+ delete key;
+
+ return entryBuf;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/comments/rawcom/Makefile b/src/modules/comments/rawcom/Makefile
new file mode 100644
index 0000000..35d6648
--- /dev/null
+++ b/src/modules/comments/rawcom/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/comments/rawcom/Makefile.am b/src/modules/comments/rawcom/Makefile.am
new file mode 100644
index 0000000..116d706
--- /dev/null
+++ b/src/modules/comments/rawcom/Makefile.am
@@ -0,0 +1,2 @@
+rawcomdir = $(top_srcdir)/src/modules/comments/rawcom
+libsword_la_SOURCES += $(rawcomdir)/rawcom.cpp
diff --git a/src/modules/comments/rawcom/rawcom.cpp b/src/modules/comments/rawcom/rawcom.cpp
new file mode 100644
index 0000000..fd01c24
--- /dev/null
+++ b/src/modules/comments/rawcom/rawcom.cpp
@@ -0,0 +1,153 @@
+/******************************************************************************
+ * rawcom.cpp - code for class 'RawCom'- a module that reads raw commentary
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <filemgr.h>
+#include <rawverse.h>
+#include <rawcom.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+ /******************************************************************************
+ * RawCom Constructor - Initializes data for instance of RawCom
+ *
+ * ENT: iname - Internal name for module
+ * idesc - Name to display to user for module
+ * idisp - Display object to use for displaying
+ */
+
+RawCom::RawCom(const char *ipath, const char *iname, const char *idesc, SWDisplay *idisp, SWTextEncoding encoding, SWTextDirection dir, SWTextMarkup markup, const char* ilang)
+ : RawVerse(ipath),
+ SWCom(iname, idesc, idisp, encoding, dir, markup, ilang){
+}
+
+
+/******************************************************************************
+ * RawCom Destructor - Cleans up instance of RawCom
+ */
+
+RawCom::~RawCom()
+{
+}
+
+
+bool RawCom::isWritable() {
+ return ((idxfp[0]->getFd() > 0) && ((idxfp[0]->mode & FileMgr::RDWR) == FileMgr::RDWR));
+}
+/******************************************************************************
+ * RawCom::getRawEntry() - Returns the correct verse when char * cast
+ * is requested
+ *
+ * RET: string buffer with verse
+ */
+
+SWBuf &RawCom::getRawEntryBuf() {
+ long start = 0;
+ unsigned short size = 0;
+ VerseKey *key = &getVerseKey();
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+ entrySize = size; // support getEntrySize call
+
+ entryBuf = "";
+ readText(key->Testament(), start, size, entryBuf);
+
+ rawFilter(entryBuf, 0); // hack, decipher
+ rawFilter(entryBuf, key);
+
+// if (!isUnicode())
+ prepText(entryBuf);
+
+ return entryBuf;
+}
+
+
+/******************************************************************************
+ * RawCom::increment - Increments module key a number of entries
+ *
+ * ENT: steps - Number of entries to jump forward
+ *
+ * RET: *this
+ */
+
+void RawCom::increment(int steps) {
+ long start;
+ unsigned short size;
+ VerseKey *tmpkey = &getVerseKey();
+
+ findOffset(tmpkey->Testament(), tmpkey->Index(), &start, &size);
+
+ SWKey lastgood = *tmpkey;
+ while (steps) {
+ long laststart = start;
+ unsigned short lastsize = size;
+ SWKey lasttry = *tmpkey;
+ (steps > 0) ? (*key)++ : (*key)--;
+ tmpkey = &getVerseKey();
+
+ if ((error = key->Error())) {
+ *key = lastgood;
+ break;
+ }
+ long index = tmpkey->Index();
+ findOffset(tmpkey->Testament(), index, &start, &size);
+ if (
+ (((laststart != start) || (lastsize != size)) // we're a different entry
+// && (start > 0)
+ && (size)) // and we actually have a size
+ ||(!skipConsecutiveLinks)) { // or we don't want to skip consecutive links
+ steps += (steps < 0) ? 1 : -1;
+ lastgood = *tmpkey;
+ }
+ }
+ error = (error) ? KEYERR_OUTOFBOUNDS : 0;
+}
+
+
+void RawCom::setEntry(const char *inbuf, long len) {
+ VerseKey *key = &getVerseKey();
+ doSetText(key->Testament(), key->Index(), inbuf, len);
+}
+
+
+void RawCom::linkEntry(const SWKey *inkey) {
+ VerseKey *destkey = &getVerseKey();
+ const VerseKey *srckey = 0;
+
+ // see if we have a VerseKey * or decendant
+ SWTRY {
+ srckey = SWDYNAMIC_CAST(VerseKey, inkey);
+ }
+ SWCATCH ( ... ) {}
+ // if we don't have a VerseKey * decendant, create our own
+ if (!srckey)
+ srckey = new VerseKey(inkey);
+
+ doLinkEntry(destkey->Testament(), destkey->Index(), srckey->Index());
+
+ if (inkey != srckey) // free our key if we created a VerseKey
+ delete srckey;
+}
+
+
+/******************************************************************************
+ * RawCom::deleteEntry - deletes this entry
+ *
+ * RET: *this
+ */
+
+void RawCom::deleteEntry() {
+
+ VerseKey *key = &getVerseKey();
+ doSetText(key->Testament(), key->Index(), "");
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/comments/rawcom4/Makefile.am b/src/modules/comments/rawcom4/Makefile.am
new file mode 100644
index 0000000..346dbc5
--- /dev/null
+++ b/src/modules/comments/rawcom4/Makefile.am
@@ -0,0 +1,2 @@
+rawcom4dir = $(top_srcdir)/src/modules/comments/rawcom4
+libsword_la_SOURCES += $(rawcom4dir)/rawcom4.cpp
diff --git a/src/modules/comments/rawcom4/rawcom4.cpp b/src/modules/comments/rawcom4/rawcom4.cpp
new file mode 100644
index 0000000..e59ee39
--- /dev/null
+++ b/src/modules/comments/rawcom4/rawcom4.cpp
@@ -0,0 +1,153 @@
+/******************************************************************************
+ * rawcom4.cpp - code for class 'RawCom4'- a module that reads raw commentary
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <filemgr.h>
+#include <rawverse4.h>
+#include <rawcom4.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+ /******************************************************************************
+ * RawCom4 Constructor - Initializes data for instance of RawCom4
+ *
+ * ENT: iname - Internal name for module
+ * idesc - Name to display to user for module
+ * idisp - Display object to use for displaying
+ */
+
+RawCom4::RawCom4(const char *ipath, const char *iname, const char *idesc, SWDisplay *idisp, SWTextEncoding encoding, SWTextDirection dir, SWTextMarkup markup, const char* ilang)
+ : RawVerse4(ipath),
+ SWCom(iname, idesc, idisp, encoding, dir, markup, ilang){
+}
+
+
+/******************************************************************************
+ * RawCom4 Destructor - Cleans up instance of RawCom4
+ */
+
+RawCom4::~RawCom4()
+{
+}
+
+
+bool RawCom4::isWritable() {
+ return ((idxfp[0]->getFd() > 0) && ((idxfp[0]->mode & FileMgr::RDWR) == FileMgr::RDWR));
+}
+/******************************************************************************
+ * RawCom4::getRawEntry() - Returns the correct verse when char * cast
+ * is requested
+ *
+ * RET: string buffer with verse
+ */
+
+SWBuf &RawCom4::getRawEntryBuf() {
+ long start = 0;
+ unsigned long size = 0;
+ VerseKey *key = &getVerseKey();
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+ entrySize = size; // support getEntrySize call
+
+ entryBuf = "";
+ readText(key->Testament(), start, size, entryBuf);
+
+ rawFilter(entryBuf, 0); // hack, decipher
+ rawFilter(entryBuf, key);
+
+// if (!isUnicode())
+ prepText(entryBuf);
+
+ return entryBuf;
+}
+
+
+/******************************************************************************
+ * RawCom4::increment - Increments module key a number of entries
+ *
+ * ENT: steps - Number of entries to jump forward
+ *
+ * RET: *this
+ */
+
+void RawCom4::increment(int steps) {
+ long start;
+ unsigned long size;
+ VerseKey *tmpkey = &getVerseKey();
+
+ findOffset(tmpkey->Testament(), tmpkey->Index(), &start, &size);
+
+ SWKey lastgood = *tmpkey;
+ while (steps) {
+ long laststart = start;
+ unsigned long lastsize = size;
+ SWKey lasttry = *tmpkey;
+ (steps > 0) ? (*key)++ : (*key)--;
+ tmpkey = &getVerseKey();
+
+ if ((error = key->Error())) {
+ *key = lastgood;
+ break;
+ }
+ long index = tmpkey->Index();
+ findOffset(tmpkey->Testament(), index, &start, &size);
+ if (
+ (((laststart != start) || (lastsize != size)) // we're a different entry
+// && (start > 0)
+ && (size)) // and we actually have a size
+ ||(!skipConsecutiveLinks)) { // or we don't want to skip consecutive links
+ steps += (steps < 0) ? 1 : -1;
+ lastgood = *tmpkey;
+ }
+ }
+ error = (error) ? KEYERR_OUTOFBOUNDS : 0;
+}
+
+
+void RawCom4::setEntry(const char *inbuf, long len) {
+ VerseKey *key = &getVerseKey();
+ doSetText(key->Testament(), key->Index(), inbuf, len);
+}
+
+
+void RawCom4::linkEntry(const SWKey *inkey) {
+ VerseKey *destkey = &getVerseKey();
+ const VerseKey *srckey = 0;
+
+ // see if we have a VerseKey * or decendant
+ SWTRY {
+ srckey = SWDYNAMIC_CAST(VerseKey, inkey);
+ }
+ SWCATCH ( ... ) {}
+ // if we don't have a VerseKey * decendant, create our own
+ if (!srckey)
+ srckey = new VerseKey(inkey);
+
+ doLinkEntry(destkey->Testament(), destkey->Index(), srckey->Index());
+
+ if (inkey != srckey) // free our key if we created a VerseKey
+ delete srckey;
+}
+
+
+/******************************************************************************
+ * RawCom4::deleteEntry - deletes this entry
+ *
+ * RET: *this
+ */
+
+void RawCom4::deleteEntry() {
+
+ VerseKey *key = &getVerseKey();
+ doSetText(key->Testament(), key->Index(), "");
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/comments/rawfiles/Makefile b/src/modules/comments/rawfiles/Makefile
new file mode 100644
index 0000000..35d6648
--- /dev/null
+++ b/src/modules/comments/rawfiles/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/comments/rawfiles/Makefile.am b/src/modules/comments/rawfiles/Makefile.am
new file mode 100644
index 0000000..a9b7dbe
--- /dev/null
+++ b/src/modules/comments/rawfiles/Makefile.am
@@ -0,0 +1,2 @@
+rawfilesdir = $(top_srcdir)/src/modules/comments/rawfiles
+libsword_la_SOURCES += $(rawfilesdir)/rawfiles.cpp
diff --git a/src/modules/comments/rawfiles/rawfiles.cpp b/src/modules/comments/rawfiles/rawfiles.cpp
new file mode 100644
index 0000000..3b614d9
--- /dev/null
+++ b/src/modules/comments/rawfiles/rawfiles.cpp
@@ -0,0 +1,257 @@
+/******************************************************************************
+ * rawfiles.cpp - code for class 'RawFiles'- a module that produces HTML HREFs
+ * pointing to actual text desired. Uses standard
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <rawverse.h>
+#include <rawfiles.h>
+#include <filemgr.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+ /******************************************************************************
+ * RawFiles Constructor - Initializes data for instance of RawFiles
+ *
+ * ENT: iname - Internal name for module
+ * idesc - Name to display to user for module
+ * idisp - Display object to use for displaying
+ */
+
+RawFiles::RawFiles(const char *ipath, const char *iname, const char *idesc, SWDisplay *idisp, SWTextEncoding enc, SWTextDirection dir, SWTextMarkup mark, const char* ilang) : RawVerse(ipath, FileMgr::RDWR), SWCom(iname, idesc, idisp, enc, dir, mark, ilang)
+{
+}
+
+
+/******************************************************************************
+ * RawFiles Destructor - Cleans up instance of RawFiles
+ */
+
+RawFiles::~RawFiles()
+{
+}
+
+
+/** Is the module writable? :)
+* @return yes or no
+*/
+bool RawFiles::isWritable() {
+ return ((idxfp[0]->getFd() > 0) && ((idxfp[0]->mode & FileMgr::RDWR) == FileMgr::RDWR));
+}
+
+
+/******************************************************************************
+ * RawFiles::getRawEntry - Returns the correct verse when char * cast
+ * is requested
+ *
+ * RET: string buffer with verse
+ */
+
+SWBuf &RawFiles::getRawEntryBuf() {
+ FileDesc *datafile;
+ long start = 0;
+ unsigned short size = 0;
+ VerseKey *key = 0;
+
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+
+ entryBuf = "";
+ if (size) {
+ SWBuf tmpbuf = path;
+ tmpbuf += '/';
+ readText(key->Testament(), start, size, entryBuf);
+ tmpbuf += entryBuf;
+ entryBuf = "";
+ datafile = FileMgr::getSystemFileMgr()->open(tmpbuf.c_str(), FileMgr::RDONLY);
+ if (datafile->getFd() > 0) {
+ size = datafile->seek(0, SEEK_END);
+ char *tmpBuf = new char [ size + 1 ];
+ memset(tmpBuf, 0, size + 1);
+ datafile->seek(0, SEEK_SET);
+ datafile->read(tmpBuf, size);
+ entryBuf = tmpBuf;
+ delete [] tmpBuf;
+// preptext(entrybuf);
+ }
+ FileMgr::getSystemFileMgr()->close(datafile);
+ }
+
+ if (key != this->key)
+ delete key;
+
+ return entryBuf;
+}
+
+
+/******************************************************************************
+ * RawFiles::setEntry(char *)- Update the modules current key entry with
+ * provided text
+ */
+
+void RawFiles::setEntry(const char *inbuf, long len) {
+ FileDesc *datafile;
+ long start;
+ unsigned short size;
+ VerseKey *key = 0;
+
+ len = (len<0)?strlen(inbuf):len;
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+
+ if (size) {
+ SWBuf tmpbuf;
+ entryBuf = path;
+ entryBuf += '/';
+ readText(key->Testament(), start, size, tmpbuf);
+ entryBuf += tmpbuf;
+ }
+ else {
+ SWBuf tmpbuf;
+ entryBuf = path;
+ entryBuf += '/';
+ tmpbuf = getNextFilename();
+ doSetText(key->Testament(), key->Index(), tmpbuf);
+ entryBuf += tmpbuf;
+ }
+ datafile = FileMgr::getSystemFileMgr()->open(entryBuf, FileMgr::CREAT|FileMgr::WRONLY|FileMgr::TRUNC);
+ if (datafile->getFd() > 0) {
+ datafile->write(inbuf, len);
+ }
+ FileMgr::getSystemFileMgr()->close(datafile);
+
+ if (key != this->key)
+ delete key;
+}
+
+
+/******************************************************************************
+ * RawFiles::linkEntry(SWKey *)- Link the modules current key entry with
+ * another module entry
+ *
+ * RET: *this
+ */
+
+void RawFiles::linkEntry(const SWKey *inkey) {
+
+ long start;
+ unsigned short size;
+ const VerseKey *key = 0;
+
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, inkey);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+
+ if (size) {
+ SWBuf tmpbuf;
+ readText(key->Testament(), start, size + 2, tmpbuf);
+
+ if (key != inkey)
+ delete key;
+ key = 0;
+
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, inkey);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+ doSetText(key->Testament(), key->Index(), tmpbuf.c_str());
+ }
+
+ if (key != inkey)
+ delete key;
+}
+
+
+/******************************************************************************
+ * RawFiles::deleteEntry - deletes this entry
+ *
+ * RET: *this
+ */
+
+void RawFiles::deleteEntry() {
+
+ VerseKey *key = 0;
+
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ doSetText(key->Testament(), key->Index(), "");
+
+ if (key != this->key)
+ delete key;
+}
+
+
+/******************************************************************************
+ * RawFiles::getNextfilename - generates a valid filename in which to store
+ * an entry
+ *
+ * RET: filename
+ */
+
+char *RawFiles::getNextFilename() {
+ static char incfile[255];
+ long number;
+ FileDesc *datafile;
+
+ sprintf(incfile, "%s/incfile", path);
+ datafile = FileMgr::getSystemFileMgr()->open(incfile, FileMgr::RDONLY);
+ if (datafile->read(&number, 4) != 4)
+ number = 0;
+ number++;
+ FileMgr::getSystemFileMgr()->close(datafile);
+
+ datafile = FileMgr::getSystemFileMgr()->open(incfile, FileMgr::CREAT|FileMgr::WRONLY|FileMgr::TRUNC);
+ datafile->write(&number, 4);
+ FileMgr::getSystemFileMgr()->close(datafile);
+ sprintf(incfile, "%.7ld", number-1);
+ return incfile;
+}
+
+
+char RawFiles::createModule (const char *path) {
+ char *incfile = new char [ strlen (path) + 16 ];
+ static long zero = 0;
+ FileDesc *datafile;
+
+ sprintf(incfile, "%s/incfile", path);
+ datafile = FileMgr::getSystemFileMgr()->open(incfile, FileMgr::CREAT|FileMgr::WRONLY|FileMgr::TRUNC);
+ delete [] incfile;
+ datafile->write(&zero, 4);
+ FileMgr::getSystemFileMgr()->close(datafile);
+
+ return RawVerse::createModule (path);
+}
+
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/comments/swcom.cpp b/src/modules/comments/swcom.cpp
new file mode 100644
index 0000000..94f92c9
--- /dev/null
+++ b/src/modules/comments/swcom.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ * swcom.cpp - code for base class 'SWCom'- The basis for all commentary
+ * modules
+ */
+
+#include <swcom.h>
+#include <localemgr.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * SWCom Constructor - Initializes data for instance of SWCom
+ *
+ * ENT: imodname - Internal name for module
+ * imoddesc - Name to display to user for module
+ * idisp - Display object to use for displaying
+ */
+
+SWCom::SWCom(const char *imodname, const char *imoddesc, SWDisplay *idisp, SWTextEncoding enc, SWTextDirection dir, SWTextMarkup mark, const char* ilang): SWModule(imodname, imoddesc, idisp, (char *)"Commentaries", enc, dir, mark, ilang) {
+ delete key;
+ key = CreateKey();
+ tmpVK = new VerseKey();
+}
+
+
+/******************************************************************************
+ * SWCom Destructor - Cleans up instance of SWCom
+ */
+
+SWCom::~SWCom() {
+ delete tmpVK;
+}
+
+
+SWKey *SWCom::CreateKey() { return new VerseKey(); }
+
+
+long SWCom::Index() const {
+ VerseKey *key = 0;
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ entryIndex = key->NewIndex();
+
+ if (key != this->key)
+ delete key;
+
+ return entryIndex;
+}
+
+long SWCom::Index(long iindex) {
+ VerseKey *key = 0;
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) {}
+ if (!key)
+ key = new VerseKey(this->key);
+
+ key->Testament(1);
+ key->Index(iindex);
+
+ if (key != this->key) {
+ this->key->copyFrom(*key);
+ delete key;
+ }
+
+ return Index();
+}
+
+
+VerseKey &SWCom::getVerseKey() const {
+ VerseKey *key = NULL;
+ // see if we have a VerseKey * or decendant
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, this->key);
+ }
+ SWCATCH ( ... ) { }
+ if (!key) {
+ ListKey *lkTest = 0;
+ SWTRY {
+ lkTest = SWDYNAMIC_CAST(ListKey, this->key);
+ }
+ SWCATCH ( ... ) { }
+ if (lkTest) {
+ SWTRY {
+ key = SWDYNAMIC_CAST(VerseKey, lkTest->GetElement());
+ }
+ SWCATCH ( ... ) { }
+ }
+ }
+ if (!key) {
+ tmpVK->setLocale(LocaleMgr::getSystemLocaleMgr()->getDefaultLocaleName());
+ (*tmpVK) = *(this->key);
+ return (*tmpVK);
+ }
+ else return *key;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/comments/zcom/Makefile b/src/modules/comments/zcom/Makefile
new file mode 100644
index 0000000..35d6648
--- /dev/null
+++ b/src/modules/comments/zcom/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/comments/zcom/Makefile.am b/src/modules/comments/zcom/Makefile.am
new file mode 100644
index 0000000..f22700c
--- /dev/null
+++ b/src/modules/comments/zcom/Makefile.am
@@ -0,0 +1,2 @@
+zcomdir = $(top_srcdir)/src/modules/comments/zcom
+libsword_la_SOURCES += $(zcomdir)/zcom.cpp
diff --git a/src/modules/comments/zcom/zcom.cpp b/src/modules/comments/zcom/zcom.cpp
new file mode 100644
index 0000000..cdaea02
--- /dev/null
+++ b/src/modules/comments/zcom/zcom.cpp
@@ -0,0 +1,188 @@
+/******************************************************************************
+ * rawcom.cpp - code for class 'zCom'- a module that reads raw commentary
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <zverse.h>
+#include <versekey.h>
+#include <zcom.h>
+#include <filemgr.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * zCom Constructor - Initializes data for instance of zCom
+ *
+ * ENT: ipath - path to data files
+ * iname - Internal name for module
+ * idesc - Name to display to user for module
+ * iblockType - verse, chapter, book, etc. of index chunks
+ * icomp - Compressor object
+ * idisp - Display object to use for displaying
+ */
+
+zCom::zCom(const char *ipath, const char *iname, const char *idesc, int iblockType, SWCompress *icomp, SWDisplay *idisp, SWTextEncoding enc, SWTextDirection dir, SWTextMarkup mark, const char* ilang) : zVerse(ipath, -1, iblockType, icomp), SWCom(iname, idesc, idisp, enc, dir, mark, ilang)/*, SWCompress()*/
+{
+ blockType = iblockType;
+ lastWriteKey = 0;
+}
+
+/******************************************************************************
+ * zCom Destructor - Cleans up instance of zCom
+ */
+
+zCom::~zCom() {
+ flushCache();
+
+ if (lastWriteKey)
+ delete lastWriteKey;
+}
+
+
+bool zCom::isWritable() {
+ return ((idxfp[0]->getFd() > 0) && ((idxfp[0]->mode & FileMgr::RDWR) == FileMgr::RDWR));
+}
+
+
+/******************************************************************************
+ * zCom::getRawEntry - Returns the correct verse when char * cast
+ * is requested
+ *
+ * RET: string buffer with verse
+ */
+SWBuf &zCom::getRawEntryBuf() {
+ long start = 0;
+ unsigned short size = 0;
+ VerseKey *key = &getVerseKey();
+
+ findOffset(key->Testament(), key->Index(), &start, &size);
+ entrySize = size; // support getEntrySize call
+
+ entryBuf = "";
+ zReadText(key->Testament(), start, size, entryBuf);
+
+ rawFilter(entryBuf, key);
+
+// if (!isUnicode())
+ prepText(entryBuf);
+
+ return entryBuf;
+}
+
+
+bool zCom::sameBlock(VerseKey *k1, VerseKey *k2) {
+ if (k1->Testament() != k2->Testament())
+ return false;
+
+ switch (blockType) {
+ case VERSEBLOCKS:
+ if (k1->Verse() != k2->Verse())
+ return false;
+ case CHAPTERBLOCKS:
+ if (k1->Chapter() != k2->Chapter())
+ return false;
+ case BOOKBLOCKS:
+ if (k1->Book() != k2->Book())
+ return false;
+ }
+ return true;
+}
+
+void zCom::setEntry(const char *inbuf, long len) {
+ VerseKey *key = &getVerseKey();
+
+ // see if we've jumped across blocks since last write
+ if (lastWriteKey) {
+ if (!sameBlock(lastWriteKey, key)) {
+ flushCache();
+ }
+ delete lastWriteKey;
+ }
+
+ doSetText(key->Testament(), key->Index(), inbuf, len);
+
+ lastWriteKey = (VerseKey *)key->clone(); // must delete
+}
+
+
+void zCom::linkEntry(const SWKey *inkey) {
+ VerseKey *destkey = &getVerseKey();
+ const VerseKey *srckey = 0;
+
+ // see if we have a VerseKey * or decendant
+ SWTRY {
+ srckey = (const VerseKey *) SWDYNAMIC_CAST(VerseKey, inkey);
+ }
+ SWCATCH ( ... ) {
+ }
+ // if we don't have a VerseKey * decendant, create our own
+ if (!srckey)
+ srckey = new VerseKey(inkey);
+
+ doLinkEntry(destkey->Testament(), destkey->Index(), srckey->Index());
+
+ if (inkey != srckey) // free our key if we created a VerseKey
+ delete srckey;
+}
+
+/******************************************************************************
+ * zCom::deleteEntry - deletes this entry
+ *
+ * RET: *this
+ */
+
+void zCom::deleteEntry() {
+
+ VerseKey *key = &getVerseKey();
+ doSetText(key->Testament(), key->Index(), "");
+}
+
+
+/******************************************************************************
+ * zCom::increment - Increments module key a number of entries
+ *
+ * ENT: increment - Number of entries to jump forward
+ *
+ * RET: *this
+ */
+
+void zCom::increment(int steps) {
+ long start;
+ unsigned short size;
+ VerseKey *tmpkey = &getVerseKey();
+
+ findOffset(tmpkey->Testament(), tmpkey->Index(), &start, &size);
+
+ SWKey lastgood = *tmpkey;
+ while (steps) {
+ long laststart = start;
+ unsigned short lastsize = size;
+ SWKey lasttry = *tmpkey;
+ (steps > 0) ? (*key)++ : (*key)--;
+ tmpkey = &getVerseKey();
+
+ if ((error = key->Error())) {
+ *key = lastgood;
+ break;
+ }
+ long index = tmpkey->Index();
+ findOffset(tmpkey->Testament(), index, &start, &size);
+ if (
+ (((laststart != start) || (lastsize != size)) // we're a different entry
+// && (start > 0)
+ && (size)) // and we actually have a size
+ ||(!skipConsecutiveLinks)) { // or we don't want to skip consecutive links
+ steps += (steps < 0) ? 1 : -1;
+ lastgood = *tmpkey;
+ }
+ }
+ error = (error) ? KEYERR_OUTOFBOUNDS : 0;
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/Makefile b/src/modules/common/Makefile
new file mode 100644
index 0000000..81f7721
--- /dev/null
+++ b/src/modules/common/Makefile
@@ -0,0 +1,4 @@
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/common/Makefile.am b/src/modules/common/Makefile.am
new file mode 100644
index 0000000..e688094
--- /dev/null
+++ b/src/modules/common/Makefile.am
@@ -0,0 +1,21 @@
+commondir = $(top_srcdir)/src/modules/common
+
+libsword_la_SOURCES += $(commondir)/rawstr.cpp
+libsword_la_SOURCES += $(commondir)/rawstr4.cpp
+libsword_la_SOURCES += $(commondir)/swcomprs.cpp
+libsword_la_SOURCES += $(commondir)/lzsscomprs.cpp
+
+if ZLIB
+SWZLIB = $(commondir)/zipcomprs.cpp
+else
+SWZLIB =
+endif
+libsword_la_SOURCES += $(SWZLIB)
+libsword_la_SOURCES += $(commondir)/rawverse.cpp
+libsword_la_SOURCES += $(commondir)/rawverse4.cpp
+libsword_la_SOURCES += $(commondir)/swcipher.cpp
+libsword_la_SOURCES += $(commondir)/zverse.cpp
+libsword_la_SOURCES += $(commondir)/zstr.cpp
+libsword_la_SOURCES += $(commondir)/entriesblk.cpp
+libsword_la_SOURCES += $(commondir)/sapphire.cpp
+
diff --git a/src/modules/common/compress.cpp.txt b/src/modules/common/compress.cpp.txt
new file mode 100644
index 0000000..5031adb
--- /dev/null
+++ b/src/modules/common/compress.cpp.txt
@@ -0,0 +1,767 @@
+Compression Info, 10-11-95
+Jeff Wheeler
+
+Source of Algorithm
+-------------------
+
+The compression algorithms used here are based upon the algorithms developed and published by Haruhiko Okumura in a paper entitled "Data Compression Algorithms of LARC and LHarc." This paper discusses three compression algorithms, LSZZ, LZARI, and LZHUF. LZSS is described as the "first" of these, and is described as providing moderate compression with good speed. LZARI is described as an improved LZSS, a combination of the LZSS algorithm with adaptive arithmetic compression. It is described as being slower than LZSS but with better compression. LZHUF (the basis of the common LHA compression program) was included in the paper, however, a free usage license was not included.
+
+The following are copies of the statements included at the beginning of each source code listing that was supplied in the working paper.
+
+ LZSS, dated 4/6/89, marked as "Use, distribute and
+ modify this program freely."
+
+ LZARI, dated 4/7/89, marked as "Use, distribute and
+ modify this program freely."
+
+ LZHUF, dated 11/20/88, written by Haruyasu Yoshizaki,
+ translated by Haruhiko Okumura on 4/7/89. Not
+ expressly marked as redistributable or modifiable.
+
+Since both LZSS and LZARI are marked as "use, distribute and modify freely" we have felt at liberty basing our compression algorithm on either of these.
+
+Selection of Algorithm
+----------------------
+
+Working samples of three possible compression algorithms are supplied in Okumura's paper. Which should be used?
+
+LZSS is the fastest at decompression, but does not generated as small a compressed file as the other methods. The other two methods provided, perhaps, a 15% improvement in compression. Or, put another way, on a 100K file, LZSS might compress it to 50K while the others might approach 40-45K. For STEP purposes, it was decided that decoding speed was of more importance than tighter compression. For these reasons, the first compression algorithm implemented is the LZSS algorithm.
+
+About LZSS Encoding
+-------------------
+
+(adapted from Haruhiko Okumura's paper)
+
+This scheme was proposed by Ziv and Lempel [1]. A slightly modified version is described by Storer and Szymanski [2]. An implementation using a binary tree has been proposed by Bell [3].
+
+The algorithm is quite simple.
+1. Keep a ring buffer which initially contains all space characters.
+2. Read several letters from the file to the buffer.
+3. Search the buffer for the longest string that matches the letters just read, and send its length and position into the buffer.
+
+If the ring buffer is 4096 bytes, the position can be stored in 12 bits. If the length is represented in 4 bits, the <position, length> pair is two bytes long. If the longest match is no more than two characters, then just one character is sent without encoding. The process starts again with the next character. An extra bit is sent each time to tell the decoder whether the next item is a character of a <position, length> pair.
+
+[1] J. Ziv and A. Lempel, IEEE Transactions IT-23, 337-343 (1977).
+[2] J. A. Storer and T. G. Szymanski, J. ACM, 29, 928-951 (1982).
+[3] T.C. Gell, IEEE Transactions COM-34, 1176-1182 (1986).
+
+void InitTree( // no return value
+ void); // no parameters
+
+void InsertNode( // no return value
+ short int Pos); // position in the buffer
+
+void DeleteNode( // no return value
+ short int Node); // node to be removed
+
+void Encode( // no return value
+ void); // no parameters
+
+void Decode( // no return value
+ void); // no parameters
+
+// The following are constant sizes used by the compression algorithm.
+//
+// N - This is the size of the ring buffer. It is set
+// to 4K. It is important to note that a position
+// within the ring buffer requires 12 bits.
+//
+// F - This is the maximum length of a character sequence
+// that can be taken from the ring buffer. It is set
+// to 18. Note that a length must be 3 before it is
+// worthwhile to store a position/length pair, so the
+// length can be encoded in only 4 bits. Or, put yet
+// another way, it is not necessary to encode a length
+// of 0-18, it is necessary to encode a length of
+// 3-18, which requires 4 bits.
+//
+// THRESHOLD - It takes 2 bytes to store an offset and
+// a length. If a character sequence only
+// requires 1 or 2 characters to store
+// uncompressed, then it is better to store
+// it uncompressed than as an offset into
+// the ring buffer.
+//
+// Note that the 12 bits used to store the position and the 4 bits
+// used to store the length equal a total of 16 bits, or 2 bytes.
+
+#define N 4096
+#define F 18
+#define THRESHOLD 3
+#define NOT_USED N
+
+// m_ring_buffer is a text buffer. It contains "nodes" of
+// uncompressed text that can be indexed by position. That is,
+// a substring of the ring buffer can be indexed by a position
+// and a length. When decoding, the compressed text may contain
+// a position in the ring buffer and a count of the number of
+// bytes from the ring buffer that are to be moved into the
+// uncompressed buffer.
+//
+// This ring buffer is not maintained as part of the compressed
+// text. Instead, it is reconstructed dynamically. That is,
+// it starts out empty and gets built as the text is decompressed.
+//
+// The ring buffer contain N bytes, with an additional F - 1 bytes
+// to facilitate string comparison.
+
+unsigned char m_ring_buffer[N + F - 1];
+
+// m_match_position and m_match_length are set by InsertNode().
+//
+// These variables indicate the position in the ring buffer
+// and the number of characters at that position that match
+// a given string.
+
+short int m_match_position;
+short int m_match_length;
+
+// m_lson, m_rson, and m_dad are the Japanese way of referring to
+// a tree structure. The dad is the parent and it has a right and
+// left son (child).
+//
+// For i = 0 to N-1, m_rson[i] and m_lson[i] will be the right
+// and left children of node i.
+//
+// For i = 0 to N-1, m_dad[i] is the parent of node i.
+//
+// For i = 0 to 255, rson[N + i + 1] is the root of the tree for
+// strings that begin with the character i. Note that this requires
+// one byte characters.
+//
+// These nodes store values of 0...(N-1). Memory requirements
+// can be reduces by using 2-byte integers instead of full 4-byte
+// integers (for 32-bit applications). Therefore, these are
+// defined as "short ints."
+
+short int m_lson[N + 1];
+short int m_rson[N + 257];
+short int m_dad[N + 1];
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::InitTree
+
+ This function initializes the tree nodes to "empty" states.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::InitTree( // no return value
+ void) // no parameters
+ throw() // exception list
+
+ {
+ int i;
+
+ // For i = 0 to N - 1, m_rson[i] and m_lson[i] will be the right
+ // and left children of node i. These nodes need not be
+ // initialized. However, for debugging purposes, it is nice to
+ // have them initialized. Since this is only used for compression
+ // (not decompression), I don't mind spending the time to do it.
+ //
+ // For the same range of i, m_dad[i] is the parent of node i.
+ // These are initialized to a known value that can represent
+ // a "not used" state.
+
+ for (i = 0; i < N; i++)
+ {
+ m_lson[i] = NOT_USED;
+ m_rson[i] = NOT_USED;
+ m_dad[i] = NOT_USED;
+ }
+
+ // For i = 0 to 255, m_rson[N + i + 1] is the root of the tree
+ // for strings that begin with the character i. This is why
+ // the right child array is larger than the left child array.
+ // These are also initialzied to a "not used" state.
+ //
+ // Note that there are 256 of these, one for each of the possible
+ // 256 characters.
+
+ for (i = N + 1; i <= (N + 256); i++)
+ {
+ m_rson[i] = NOT_USED;
+ }
+
+ // Done.
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::InsertNode
+
+ This function inserts a string from the ring buffer into one of
+ the trees. It loads the match position and length member variables
+ for the longest match.
+
+ The string to be inserted is identified by the parameter Pos,
+ A full F bytes are inserted. So, m_ring_buffer[Pos ... Pos+F-1]
+ are inserted.
+
+ If the matched length is exactly F, then an old node is removed
+ in favor of the new one (because the old one will be deleted
+ sooner).
+
+ Note that Pos plays a dual role. It is used as both a position
+ in the ring buffer and also as a tree node. m_ring_buffer[Pos]
+ defines a character that is used to identify a tree node.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::InsertNode( // no return value
+ short int Pos) // position in the buffer
+ throw() // exception list
+
+ {
+ short int i;
+ short int p;
+ int cmp;
+ unsigned char * key;
+
+ ASSERT(Pos >= 0);
+ ASSERT(Pos < N);
+
+ cmp = 1;
+ key = &(m_ring_buffer[Pos]);
+
+ // The last 256 entries in m_rson contain the root nodes for
+ // strings that begin with a letter. Get an index for the
+ // first letter in this string.
+
+ p = (short int) (N + 1 + key[0]);
+
+ // Set the left and right tree nodes for this position to "not
+ // used."
+
+ m_lson[Pos] = NOT_USED;
+ m_rson[Pos] = NOT_USED;
+
+ // Haven't matched anything yet.
+
+ m_match_length = 0;
+
+ for ( ; ; )
+ {
+ if (cmp >= 0)
+ {
+ if (m_rson[p] != NOT_USED)
+ {
+ p = m_rson[p];
+ }
+ else
+ {
+ m_rson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+ else
+ {
+ if (m_lson[p] != NOT_USED)
+ {
+ p = m_lson[p];
+ }
+ else
+ {
+ m_lson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+
+ // Should we go to the right or the left to look for the
+ // next match?
+
+ for (i = 1; i < F; i++)
+ {
+ cmp = key[i] - m_ring_buffer[p + i];
+ if (cmp != 0)
+ break;
+ }
+
+ if (i > m_match_length)
+ {
+ m_match_position = p;
+ m_match_length = i;
+
+ if (i >= F)
+ break;
+ }
+ }
+
+ m_dad[Pos] = m_dad[p];
+ m_lson[Pos] = m_lson[p];
+ m_rson[Pos] = m_rson[p];
+
+ m_dad[ m_lson[p] ] = Pos;
+ m_dad[ m_rson[p] ] = Pos;
+
+ if (m_rson[ m_dad[p] ] == p)
+ {
+ m_rson[ m_dad[p] ] = Pos;
+ }
+ else
+ {
+ m_lson[ m_dad[p] ] = Pos;
+ }
+
+ // Remove "p"
+
+ m_dad[p] = NOT_USED;
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::DeleteNode
+
+ This function removes the node "Node" from the tree.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::DeleteNode( // no return value
+ short int Node) // node to be removed
+ throw() // exception list
+
+ {
+ short int q;
+
+ ASSERT(Node >= 0);
+ ASSERT(Node < (N+1));
+
+ if (m_dad[Node] == NOT_USED)
+ {
+ // not in tree, nothing to do
+ return;
+ }
+
+ if (m_rson[Node] == NOT_USED)
+ {
+ q = m_lson[Node];
+ }
+ else if (m_lson[Node] == NOT_USED)
+ {
+ q = m_rson[Node];
+ }
+ else
+ {
+ q = m_lson[Node];
+ if (m_rson[q] != NOT_USED)
+ {
+ do
+ {
+ q = m_rson[q];
+ }
+ while (m_rson[q] != NOT_USED);
+
+ m_rson[ m_dad[q] ] = m_lson[q];
+ m_dad[ m_lson[q] ] = m_dad[q];
+ m_lson[q] = m_lson[Node];
+ m_dad[ m_lson[Node] ] = q;
+ }
+
+ m_rson[q] = m_rson[Node];
+ m_dad[ m_rson[Node] ] = q;
+ }
+
+ m_dad[q] = m_dad[Node];
+
+ if (m_rson[ m_dad[Node] ] == Node)
+ {
+ m_rson[ m_dad[Node] ] = q;
+ }
+ else
+ {
+ m_lson[ m_dad[Node] ] = q;
+ }
+
+ m_dad[Node] = NOT_USED;
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::Encode
+
+ This function "encodes" the input stream into the output stream.
+ The GetChars() and SendChars() functions are used to separate
+ this method from the actual i/o.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::Encode( // no return value
+ void) // no parameters
+
+ {
+ short int i; // an iterator
+ short int r; // node number in the binary tree
+ short int s; // position in the ring buffer
+ unsigned short int len; // len of initial string
+ short int last_match_length; // length of last match
+ short int code_buf_pos; // position in the output buffer
+ unsigned char code_buf[17]; // the output buffer
+ unsigned char mask; // bit mask for byte 0 of out buf
+ unsigned char c; // character read from string
+
+ // Start with a clean tree.
+
+ InitTree();
+
+ // code_buf[0] works as eight flags. A "1" represents that the
+ // unit is an unencoded letter (1 byte), and a "0" represents
+ // that the next unit is a <position,length> pair (2 bytes).
+ //
+ // code_buf[1..16] stores eight units of code. Since the best
+ // we can do is store eight <position,length> pairs, at most 16
+ // bytes are needed to store this.
+ //
+ // This is why the maximum size of the code buffer is 17 bytes.
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+
+ // Mask iterates over the 8 bits in the code buffer. The first
+ // character ends up being stored in the low bit.
+ //
+ // bit 8 7 6 5 4 3 2 1
+ // | |
+ // | first sequence in code buffer
+ // |
+ // last sequence in code buffer
+
+ mask = 1;
+
+ s = 0;
+ r = (short int) N - (short int) F;
+
+ // Initialize the ring buffer with spaces...
+
+ // Note that the last F bytes of the ring buffer are not filled.
+ // This is because those F bytes will be filled in immediately
+ // with bytes from the input stream.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ // Read F bytes into the last F bytes of the ring buffer.
+ //
+ // This function loads the buffer with X characters and returns
+ // the actual amount loaded.
+
+ len = GetChars(&(m_ring_buffer[r]), F);
+
+ // Make sure there is something to be compressed.
+
+ if (len == 0)
+ return;
+
+ // Insert the F strings, each of which begins with one or more
+ // 'space' characters. Note the order in which these strings
+ // are inserted. This way, degenerate trees will be less likely
+ // to occur.
+
+ for (i = 1; i <= F; i++)
+ {
+ InsertNode((short int) (r - i));
+ }
+
+ // Finally, insert the whole string just read. The
+ // member variables match_length and match_position are set.
+
+ InsertNode(r);
+
+ // Now that we're preloaded, continue till done.
+
+ do
+ {
+
+ // m_match_length may be spuriously long near the end of
+ // text.
+
+ if (m_match_length > len)
+ {
+ m_match_length = len;
+ }
+
+ // Is it cheaper to store this as a single character? If so,
+ // make it so.
+
+ if (m_match_length < THRESHOLD)
+ {
+ // Send one character. Remember that code_buf[0] is the
+ // set of flags for the next eight items.
+
+ m_match_length = 1;
+ code_buf[0] |= mask;
+ code_buf[code_buf_pos++] = m_ring_buffer[r];
+ }
+
+ // Otherwise, we do indeed have a string that can be stored
+ // compressed to save space.
+
+ else
+ {
+ // The next 16 bits need to contain the position (12 bits)
+ // and the length (4 bits).
+
+ code_buf[code_buf_pos++] = (unsigned char) m_match_position;
+ code_buf[code_buf_pos++] = (unsigned char) (
+ ((m_match_position >> 4) & 0xf0) |
+ (m_match_length - THRESHOLD) );
+ }
+
+ // Shift the mask one bit to the left so that it will be ready
+ // to store the new bit.
+
+ mask = (unsigned char) (mask << 1);
+
+ // If the mask is now 0, then we know that we have a full set
+ // of flags and items in the code buffer. These need to be
+ // output.
+
+ if (mask == 0)
+ {
+ // code_buf is the buffer of characters to be output.
+ // code_buf_pos is the number of characters it contains.
+
+ SendChars(code_buf, code_buf_pos);
+
+ // Reset for next buffer...
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+ mask = 1;
+ }
+
+ last_match_length = m_match_length;
+
+ // Delete old strings and read new bytes...
+
+ for (i = 0; i < last_match_length; i++)
+ {
+
+ // Get next character...
+
+ if (GetChars(&c, 1) != 1)
+ break;
+
+ // Delete "old strings"
+
+ DeleteNode(s);
+
+ // Put this character into the ring buffer.
+ //
+ // The original comment here says "If the position is near
+ // the end of the buffer, extend the buffer to make
+ // string comparison easier."
+ //
+ // That's a little misleading, because the "end" of the
+ // buffer is really what we consider to be the "beginning"
+ // of the buffer, that is, positions 0 through F.
+ //
+ // The idea is that the front end of the buffer is duplicated
+ // into the back end so that when you're looking at characters
+ // at the back end of the buffer, you can index ahead (beyond
+ // the normal end of the buffer) and see the characters
+ // that are at the front end of the buffer wihtout having
+ // to adjust the index.
+ //
+ // That is...
+ //
+ // 1234xxxxxxxxxxxxxxxxxxxxxxxxxxxxx1234
+ // | | |
+ // position 0 end of buffer |
+ // |
+ // duplicate of front of buffer
+
+ m_ring_buffer[s] = c;
+
+ if (s < F - 1)
+ {
+ m_ring_buffer[s + N] = c;
+ }
+
+ // Increment the position, and wrap around when we're at
+ // the end. Note that this relies on N being a power of 2.
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Register the string that is found in
+ // m_ring_buffer[r..r+F-1].
+
+ InsertNode(r);
+ }
+
+ // If we didn't quit because we hit the last_match_length,
+ // then we must have quit because we ran out of characters
+ // to process.
+
+ while (i++ < last_match_length)
+ {
+ DeleteNode(s);
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Note that len hitting 0 is the key that causes the
+ // do...while() to terminate. This is the only place
+ // within the loop that len is modified.
+ //
+ // Its original value is F (or a number less than F for
+ // short strings).
+
+ if (--len)
+ {
+ InsertNode(r); /* buffer may not be empty. */
+ }
+ }
+
+ // End of do...while() loop. Continue processing until there
+ // are no more characters to be compressed. The variable
+ // "len" is used to signal this condition.
+ }
+ while (len > 0);
+
+ // There could still be something in the output buffer. Send it
+ // now.
+
+ if (code_buf_pos > 1)
+ {
+ // code_buf is the encoded string to send.
+ // code_buf_ptr is the number of characters.
+
+ SendChars(code_buf, code_buf_pos);
+ }
+
+ // Done!
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::Decode
+
+ This function "decodes" the input stream into the output stream.
+ The GetChars() and SendChars() functions are used to separate
+ this method from the actual i/o.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::Decode( // no return value
+ void) // no parameters
+
+ {
+ int k;
+ int r; // node number
+ unsigned char c[F]; // an array of chars
+ unsigned char flags; // 8 bits of flags
+ int flag_count; // which flag we're on
+ short int pos; // position in the ring buffer
+ short int len; // number of chars in ring buffer
+
+ // Initialize the ring buffer with a common string.
+ //
+ // Note that the last F bytes of the ring buffer are not filled.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ r = N - F;
+
+ flags = (char) 0;
+ flag_count = 0;
+
+ for ( ; ; )
+ {
+
+ // If there are more bits of interest in this flag, then
+ // shift that next interesting bit into the 1's position.
+ //
+ // If this flag has been exhausted, the next byte must
+ // be a flag.
+
+ if (flag_count > 0)
+ {
+ flags = (unsigned char) (flags >> 1);
+ flag_count--;
+ }
+ else
+ {
+ // Next byte must be a flag.
+
+ if (GetChars(&flags, 1) != 1)
+ break;
+
+ // Set the flag counter. While at first it might appear
+ // that this should be an 8 since there are 8 bits in the
+ // flag, it should really be a 7 because the shift must
+ // be performed 7 times in order to see all 8 bits.
+
+ flag_count = 7;
+ }
+
+ // If the low order bit of the flag is now set, then we know
+ // that the next byte is a single, unencoded character.
+
+ if (flags & 1)
+ {
+ if (GetChars(c, 1) != 1)
+ break;
+
+ if (SendChars(c, 1) != 1)
+ break;
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[0];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Otherwise, we know that the next two bytes are a
+ // <position,length> pair. The position is in 12 bits and
+ // the length is in 4 bits.
+
+ else
+ {
+ // Original code:
+ // if ((i = getc(infile)) == EOF)
+ // break;
+ // if ((j = getc(infile)) == EOF)
+ // break;
+ // i |= ((j & 0xf0) << 4);
+ // j = (j & 0x0f) + THRESHOLD;
+ //
+ // I've modified this to only make one input call, and
+ // have changed the variable names to something more
+ // obvious.
+
+ if (GetChars(c, 2) != 2)
+ break;
+
+ // Convert these two characters into the position and
+ // length. Note that the length is always at least
+ // THRESHOLD, which is why we're able to get a length
+ // of 18 out of only 4 bits.
+
+ pos = (short int) ( c[0] | ((c[1] & 0xf0) << 4) );
+
+ len = (short int) ( (c[1] & 0x0f) + THRESHOLD );
+
+ // There are now "len" characters at position "pos" in
+ // the ring buffer that can be pulled out. Note that
+ // len is never more than F.
+
+ for (k = 0; k < len; k++)
+ {
+ c[k] = m_ring_buffer[(pos + k) & (N - 1)];
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[k];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Add the "len" characters to the output stream.
+
+ if (SendChars(c, len) != len)
+ break;
+ }
+ }
+ }
+
diff --git a/src/modules/common/entriesblk.cpp b/src/modules/common/entriesblk.cpp
new file mode 100644
index 0000000..216abd8
--- /dev/null
+++ b/src/modules/common/entriesblk.cpp
@@ -0,0 +1,172 @@
+#include <entriesblk.h>
+#include <stdlib.h>
+#include <string.h>
+
+SWORD_NAMESPACE_START
+
+const int EntriesBlock::METAHEADERSIZE = 4;
+ // count(4);
+const int EntriesBlock::METAENTRYSIZE = 8;
+ // offset(4); size(4);
+
+EntriesBlock::EntriesBlock(const char *iBlock, unsigned long size) {
+ if (size) {
+ block = (char *)calloc(1, size);
+ memcpy(block, iBlock, size);
+ }
+ else {
+ block = (char *)calloc(1, sizeof(__u32));
+ }
+}
+
+
+EntriesBlock::EntriesBlock() {
+ block = (char *)calloc(1, sizeof(__u32));
+}
+
+
+EntriesBlock::~EntriesBlock() {
+ free(block);
+}
+
+
+void EntriesBlock::setCount(int count) {
+ __u32 rawCount = archtosword32(count);
+ memcpy(block, &rawCount, sizeof(__u32));
+}
+
+
+int EntriesBlock::getCount() {
+ __u32 count = 0;
+ memcpy(&count, block, sizeof(__u32));
+ count = swordtoarch32(count);
+ return count;
+}
+
+
+void EntriesBlock::getMetaEntry(int index, unsigned long *offset, unsigned long *size) {
+ __u32 rawOffset = 0;
+ __u32 rawSize = 0;
+ *offset = 0;
+ *size = 0;
+ if (index >= getCount()) // assert index < count
+ return;
+
+ // first 4 bytes is count, each 6 bytes after is each meta entry
+ memcpy(&rawOffset, block + METAHEADERSIZE + (index * METAENTRYSIZE), sizeof(rawOffset));
+ memcpy(&rawSize, block + METAHEADERSIZE + (index * METAENTRYSIZE) + sizeof(rawOffset), sizeof(rawSize));
+
+ *offset = (unsigned long)swordtoarch32(rawOffset);
+ *size = (unsigned long)swordtoarch32(rawSize);
+}
+
+
+void EntriesBlock::setMetaEntry(int index, unsigned long offset, unsigned long size) {
+ __u32 rawOffset = archtosword32(offset);
+ __u32 rawSize = archtosword32(size);
+
+ if (index >= getCount()) // assert index < count
+ return;
+
+ // first 4 bytes is count, each 6 bytes after is each meta entry
+ memcpy(block + METAHEADERSIZE + (index * METAENTRYSIZE), &rawOffset, sizeof(rawOffset));
+ memcpy(block + METAHEADERSIZE + (index * METAENTRYSIZE) + sizeof(rawOffset), &rawSize, sizeof(rawSize));
+}
+
+
+const char *EntriesBlock::getRawData(unsigned long *retSize) {
+ unsigned long max = 4;
+ int loop;
+ unsigned long offset;
+ unsigned long size;
+ for (loop = 0; loop < getCount(); loop++) {
+ getMetaEntry(loop, &offset, &size);
+ max = ((offset + size) > max) ? (offset + size) : max;
+ }
+ *retSize = max;
+ return block;
+}
+
+
+int EntriesBlock::addEntry(const char *entry) {
+ unsigned long dataSize;
+ getRawData(&dataSize);
+ unsigned long len = strlen(entry);
+ unsigned long offset;
+ unsigned long size;
+ int count = getCount();
+ unsigned long dataStart = METAHEADERSIZE + (count * METAENTRYSIZE);
+ // new meta entry + new data size + 1 because null
+ block = (char *)realloc(block, dataSize + METAENTRYSIZE + len + 1);
+ // shift right to make room for new meta entry
+ memmove(block + dataStart + METAENTRYSIZE, block + dataStart, dataSize - dataStart);
+
+ for (int loop = 0; loop < count; loop++) {
+ getMetaEntry(loop, &offset, &size);
+ if (offset) { // if not a deleted entry
+ offset += METAENTRYSIZE;
+ setMetaEntry(loop, offset, size);
+ }
+ }
+
+ offset = dataSize; // original dataSize before realloc
+ size = len + 1;
+ // add our text to the end
+ memcpy(block + offset + METAENTRYSIZE, entry, size);
+ // increment count
+ setCount(count + 1);
+ // add our meta entry
+ setMetaEntry(count, offset + METAENTRYSIZE, size);
+ // return index of our new entry
+ return count;
+}
+
+
+const char *EntriesBlock::getEntry(int entryIndex) {
+ unsigned long offset;
+ unsigned long size;
+ static const char *empty = "";
+
+ getMetaEntry(entryIndex, &offset, &size);
+ return (offset) ? block+offset : empty;
+}
+
+
+unsigned long EntriesBlock::getEntrySize(int entryIndex) {
+ unsigned long offset;
+ unsigned long size;
+
+ getMetaEntry(entryIndex, &offset, &size);
+ return (offset) ? size : 0;
+}
+
+
+void EntriesBlock::removeEntry(int entryIndex) {
+ unsigned long offset;
+ unsigned long size, size2;
+ unsigned long dataSize;
+ getRawData(&dataSize);
+ getMetaEntry(entryIndex, &offset, &size);
+ int count = getCount();
+
+ if (!offset) // already deleted
+ return;
+
+ // shift left to retrieve space used for old entry
+ memmove(block + offset, block + offset + size, dataSize - (offset + size));
+
+ // fix offset for all entries after our entry that were shifted left
+ for (int loop = entryIndex + 1; loop < count; loop++) {
+ getMetaEntry(loop, &offset, &size2);
+ if (offset) { // if not a deleted entry
+ offset -= size;
+ setMetaEntry(loop, offset, size2);
+ }
+ }
+
+ // zero out our meta entry
+ setMetaEntry(entryIndex, 0L, 0);
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/lzsscomprs.cpp b/src/modules/common/lzsscomprs.cpp
new file mode 100644
index 0000000..bd8f768
--- /dev/null
+++ b/src/modules/common/lzsscomprs.cpp
@@ -0,0 +1,668 @@
+/******************************************************************************
+ * lzsscomprs.cpp - code for class 'LZSSCompress'- a driver class that
+ * provides LZSS compression
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <lzsscomprs.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * LZSSCompress Statics
+ */
+
+// m_ring_buffer is a text buffer. It contains "nodes" of
+// uncompressed text that can be indexed by position. That is,
+// a substring of the ring buffer can be indexed by a position
+// and a length. When decoding, the compressed text may contain
+// a position in the ring buffer and a count of the number of
+// bytes from the ring buffer that are to be moved into the
+// uncompressed buffer.
+//
+// This ring buffer is not maintained as part of the compressed
+// text. Instead, it is reconstructed dynamically. That is,
+// it starts out empty and gets built as the text is decompressed.
+//
+// The ring buffer contain N bytes, with an additional F - 1 bytes
+// to facilitate string comparison.
+
+unsigned char LZSSCompress::m_ring_buffer[N + F - 1];
+
+// m_match_position and m_match_length are set by InsertNode().
+//
+// These variables indicate the position in the ring buffer
+// and the number of characters at that position that match
+// a given string.
+
+short int LZSSCompress::m_match_position;
+short int LZSSCompress::m_match_length;
+
+// m_lson, m_rson, and m_dad are the Japanese way of referring to
+// a tree structure. The dad is the parent and it has a right and
+// left son (child).
+//
+// For i = 0 to N-1, m_rson[i] and m_lson[i] will be the right
+// and left children of node i.
+//
+// For i = 0 to N-1, m_dad[i] is the parent of node i.
+//
+// For i = 0 to 255, rson[N + i + 1] is the root of the tree for
+// strings that begin with the character i. Note that this requires
+// one byte characters.
+//
+// These nodes store values of 0...(N-1). Memory requirements
+// can be reduces by using 2-byte integers instead of full 4-byte
+// integers (for 32-bit applications). Therefore, these are
+// defined as "short ints."
+
+short int LZSSCompress::m_lson[N + 1];
+short int LZSSCompress::m_rson[N + 257];
+short int LZSSCompress::m_dad[N + 1];
+
+
+/******************************************************************************
+ * LZSSCompress Constructor - Initializes data for instance of LZSSCompress
+ *
+ */
+
+LZSSCompress::LZSSCompress() : SWCompress() {
+}
+
+
+/******************************************************************************
+ * LZSSCompress Destructor - Cleans up instance of LZSSCompress
+ */
+
+LZSSCompress::~LZSSCompress() {
+}
+
+
+/******************************************************************************
+ * LZSSCompress::InitTree - This function initializes the tree nodes to
+ * "empty" states.
+ */
+
+void LZSSCompress::InitTree(void) {
+ int i;
+
+ // For i = 0 to N - 1, m_rson[i] and m_lson[i] will be the right
+ // and left children of node i. These nodes need not be
+ // initialized. However, for debugging purposes, it is nice to
+ // have them initialized. Since this is only used for compression
+ // (not decompression), I don't mind spending the time to do it.
+ //
+ // For the same range of i, m_dad[i] is the parent of node i.
+ // These are initialized to a known value that can represent
+ // a "not used" state.
+
+ for (i = 0; i < N; i++) {
+ m_lson[i] = NOT_USED;
+ m_rson[i] = NOT_USED;
+ m_dad[i] = NOT_USED;
+ }
+
+ // For i = 0 to 255, m_rson[N + i + 1] is the root of the tree
+ // for strings that begin with the character i. This is why
+ // the right child array is larger than the left child array.
+ // These are also initialzied to a "not used" state.
+ //
+ // Note that there are 256 of these, one for each of the possible
+ // 256 characters.
+
+ for (i = N + 1; i <= (N + 256); i++) {
+ m_rson[i] = NOT_USED;
+ }
+}
+
+
+/******************************************************************************
+ * LZSSCompress::InsertNode - This function inserts a string from the ring
+ * buffer into one of the trees. It loads the
+ * match position and length member variables
+ * for the longest match.
+ *
+ * The string to be inserted is identified by
+ * the parameter Pos, A full F bytes are
+ * inserted. So,
+ * m_ring_buffer[Pos ... Pos+F-1]
+ * are inserted.
+ *
+ * If the matched length is exactly F, then an
+ * old node is removed in favor of the new one
+ * (because the old one will be deleted
+ * sooner).
+ *
+ * Note that Pos plays a dual role. It is
+ * used as both a position in the ring buffer
+ * and also as a tree node.
+ * m_ring_buffer[Pos] defines a character that
+ * is used to identify a tree node.
+ *
+ * ENT: pos - position in the buffer
+ */
+
+void LZSSCompress::InsertNode(short int Pos)
+{
+ short int i;
+ short int p;
+ int cmp;
+ unsigned char * key;
+
+/*
+ ASSERT(Pos >= 0);
+ ASSERT(Pos < N);
+*/
+
+ cmp = 1;
+ key = &(m_ring_buffer[Pos]);
+
+ // The last 256 entries in m_rson contain the root nodes for
+ // strings that begin with a letter. Get an index for the
+ // first letter in this string.
+
+ p = (short int) (N + 1 + key[0]);
+
+ // Set the left and right tree nodes for this position to "not
+ // used."
+
+ m_lson[Pos] = NOT_USED;
+ m_rson[Pos] = NOT_USED;
+
+ // Haven't matched anything yet.
+
+ m_match_length = 0;
+
+ for ( ; ; ) {
+ if (cmp >= 0) {
+ if (m_rson[p] != NOT_USED) {
+ p = m_rson[p];
+ }
+ else {
+ m_rson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+ else {
+ if (m_lson[p] != NOT_USED) {
+ p = m_lson[p];
+ }
+ else {
+ m_lson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+
+ // Should we go to the right or the left to look for the
+ // next match?
+
+ for (i = 1; i < F; i++) {
+ cmp = key[i] - m_ring_buffer[p + i];
+ if (cmp != 0)
+ break;
+ }
+
+ if (i > m_match_length) {
+ m_match_position = p;
+ m_match_length = i;
+
+ if (i >= F)
+ break;
+ }
+ }
+
+ m_dad[Pos] = m_dad[p];
+ m_lson[Pos] = m_lson[p];
+ m_rson[Pos] = m_rson[p];
+
+ m_dad[ m_lson[p] ] = Pos;
+ m_dad[ m_rson[p] ] = Pos;
+
+ if (m_rson[ m_dad[p] ] == p) {
+ m_rson[ m_dad[p] ] = Pos;
+ }
+ else {
+ m_lson[ m_dad[p] ] = Pos;
+ }
+
+ // Remove "p"
+
+ m_dad[p] = NOT_USED;
+}
+
+
+/******************************************************************************
+ * LZSSCompress::DeleteNode - This function removes the node "Node" from the
+ * tree.
+ *
+ * ENT: node - node to be removed
+ */
+
+void LZSSCompress::DeleteNode(short int Node)
+{
+ short int q;
+
+/*
+ ASSERT(Node >= 0);
+ ASSERT(Node < (N+1));
+*/
+
+ if (m_dad[Node] == NOT_USED) { // not in tree, nothing to do
+ return;
+ }
+
+ if (m_rson[Node] == NOT_USED) {
+ q = m_lson[Node];
+ }
+ else if (m_lson[Node] == NOT_USED) {
+ q = m_rson[Node];
+ }
+ else {
+ q = m_lson[Node];
+ if (m_rson[q] != NOT_USED) {
+ do {
+ q = m_rson[q];
+ } while (m_rson[q] != NOT_USED);
+
+ m_rson[ m_dad[q] ] = m_lson[q];
+ m_dad[ m_lson[q] ] = m_dad[q];
+ m_lson[q] = m_lson[Node];
+ m_dad[ m_lson[Node] ] = q;
+ }
+
+ m_rson[q] = m_rson[Node];
+ m_dad[ m_rson[Node] ] = q;
+ }
+
+ m_dad[q] = m_dad[Node];
+
+ if (m_rson[ m_dad[Node] ] == Node) {
+ m_rson[ m_dad[Node] ] = q;
+ }
+ else {
+ m_lson[ m_dad[Node] ] = q;
+ }
+
+ m_dad[Node] = NOT_USED;
+}
+
+
+/******************************************************************************
+ * LZSSCompress::Encode - This function "encodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ * NOTE: must set zlen for parent class to know length of
+ * compressed buffer.
+ */
+
+void LZSSCompress::Encode(void)
+{
+ short int i; // an iterator
+ short int r; // node number in the binary tree
+ short int s; // position in the ring buffer
+ unsigned short int len; // len of initial string
+ short int last_match_length; // length of last match
+ short int code_buf_pos; // position in the output buffer
+ unsigned char code_buf[17]; // the output buffer
+ unsigned char mask; // bit mask for byte 0 of out buf
+ unsigned char c; // character read from string
+
+ // Start with a clean tree.
+
+ InitTree();
+ direct = 0; // set direction needed by parent [Get|Send]Chars()
+
+ // code_buf[0] works as eight flags. A "1" represents that the
+ // unit is an unencoded letter (1 byte), and a "0" represents
+ // that the next unit is a <position,length> pair (2 bytes).
+ //
+ // code_buf[1..16] stores eight units of code. Since the best
+ // we can do is store eight <position,length> pairs, at most 16
+ // bytes are needed to store this.
+ //
+ // This is why the maximum size of the code buffer is 17 bytes.
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+
+ // Mask iterates over the 8 bits in the code buffer. The first
+ // character ends up being stored in the low bit.
+ //
+ // bit 8 7 6 5 4 3 2 1
+ // | |
+ // | first sequence in code buffer
+ // |
+ // last sequence in code buffer
+
+ mask = 1;
+
+ s = 0;
+ r = (short int) N - (short int) F;
+
+ // Initialize the ring buffer with spaces...
+
+ // Note that the last F bytes of the ring buffer are not filled.
+ // This is because those F bytes will be filled in immediately
+ // with bytes from the input stream.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ // Read F bytes into the last F bytes of the ring buffer.
+ //
+ // This function loads the buffer with X characters and returns
+ // the actual amount loaded.
+
+ len = GetChars((char *) &(m_ring_buffer[r]), F);
+
+ // Make sure there is something to be compressed.
+
+ if (len == 0)
+ return;
+
+ // Insert the F strings, each of which begins with one or more
+ // 'space' characters. Note the order in which these strings
+ // are inserted. This way, degenerate trees will be less likely
+ // to occur.
+
+ for (i = 1; i <= F; i++) {
+ InsertNode((short int) (r - i));
+ }
+
+ // Finally, insert the whole string just read. The
+ // member variables match_length and match_position are set.
+
+ InsertNode(r);
+
+ // Now that we're preloaded, continue till done.
+
+ do {
+
+ // m_match_length may be spuriously long near the end of
+ // text.
+
+ if (m_match_length > len) {
+ m_match_length = len;
+ }
+
+ // Is it cheaper to store this as a single character? If so,
+ // make it so.
+
+ if (m_match_length < THRESHOLD) {
+ // Send one character. Remember that code_buf[0] is the
+ // set of flags for the next eight items.
+
+ m_match_length = 1;
+ code_buf[0] |= mask;
+ code_buf[code_buf_pos++] = m_ring_buffer[r];
+ }
+
+ // Otherwise, we do indeed have a string that can be stored
+ // compressed to save space.
+
+ else {
+ // The next 16 bits need to contain the position (12 bits)
+ // and the length (4 bits).
+
+ code_buf[code_buf_pos++] = (unsigned char) m_match_position;
+ code_buf[code_buf_pos++] = (unsigned char) (
+ ((m_match_position >> 4) & 0xf0) |
+ (m_match_length - THRESHOLD) );
+ }
+
+ // Shift the mask one bit to the left so that it will be ready
+ // to store the new bit.
+
+ mask = (unsigned char) (mask << 1);
+
+ // If the mask is now 0, then we know that we have a full set
+ // of flags and items in the code buffer. These need to be
+ // output.
+
+ if (!mask) {
+ // code_buf is the buffer of characters to be output.
+ // code_buf_pos is the number of characters it contains.
+
+ SendChars((char *) code_buf, code_buf_pos);
+
+ // Reset for next buffer...
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+ mask = 1;
+ }
+
+ last_match_length = m_match_length;
+
+ // Delete old strings and read new bytes...
+
+ for (i = 0; i < last_match_length; i++) {
+ // Get next character...
+
+ if (GetChars((char *) &c, 1) != 1)
+ break;
+
+ // Delete "old strings"
+
+ DeleteNode(s);
+
+ // Put this character into the ring buffer.
+ //
+ // The original comment here says "If the position is near
+ // the end of the buffer, extend the buffer to make
+ // string comparison easier."
+ //
+ // That's a little misleading, because the "end" of the
+ // buffer is really what we consider to be the "beginning"
+ // of the buffer, that is, positions 0 through F.
+ //
+ // The idea is that the front end of the buffer is duplicated
+ // into the back end so that when you're looking at characters
+ // at the back end of the buffer, you can index ahead (beyond
+ // the normal end of the buffer) and see the characters
+ // that are at the front end of the buffer wihtout having
+ // to adjust the index.
+ //
+ // That is...
+ //
+ // 1234xxxxxxxxxxxxxxxxxxxxxxxxxxxxx1234
+ // | | |
+ // position 0 end of buffer |
+ // |
+ // duplicate of front of buffer
+
+ m_ring_buffer[s] = c;
+
+ if (s < F - 1) {
+ m_ring_buffer[s + N] = c;
+ }
+
+ // Increment the position, and wrap around when we're at
+ // the end. Note that this relies on N being a power of 2.
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Register the string that is found in
+ // m_ring_buffer[r..r+F-1].
+
+ InsertNode(r);
+ }
+
+ // If we didn't quit because we hit the last_match_length,
+ // then we must have quit because we ran out of characters
+ // to process.
+
+ while (i++ < last_match_length) {
+ DeleteNode(s);
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Note that len hitting 0 is the key that causes the
+ // do...while() to terminate. This is the only place
+ // within the loop that len is modified.
+ //
+ // Its original value is F (or a number less than F for
+ // short strings).
+
+ if (--len) {
+ InsertNode(r); /* buffer may not be empty. */
+ }
+ }
+
+ // End of do...while() loop. Continue processing until there
+ // are no more characters to be compressed. The variable
+ // "len" is used to signal this condition.
+ } while (len > 0);
+
+ // There could still be something in the output buffer. Send it
+ // now.
+
+ if (code_buf_pos > 1) {
+ // code_buf is the encoded string to send.
+ // code_buf_ptr is the number of characters.
+
+ SendChars((char *) code_buf, code_buf_pos);
+ }
+
+
+ // must set zlen for parent class to know length of compressed buffer
+ zlen = zpos;
+}
+
+
+/******************************************************************************
+ * LZSSCompress::Decode - This function "decodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void LZSSCompress::Decode(void)
+{
+ int k;
+ int r; // node number
+ unsigned char c[F]; // an array of chars
+ unsigned char flags; // 8 bits of flags
+ int flag_count; // which flag we're on
+ short int pos; // position in the ring buffer
+ short int len; // number of chars in ring buffer
+ unsigned long totalLen = 0;
+
+ direct = 1; // set direction needed by parent [Get|Send]Chars()
+
+ // Initialize the ring buffer with a common string.
+ //
+ // Note that the last F bytes of the ring buffer are not filled.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ r = N - F;
+
+ flags = (char) 0;
+ flag_count = 0;
+
+ for ( ; ; ) {
+
+ // If there are more bits of interest in this flag, then
+ // shift that next interesting bit into the 1's position.
+ //
+ // If this flag has been exhausted, the next byte must
+ // be a flag.
+
+ if (flag_count > 0) {
+ flags = (unsigned char) (flags >> 1);
+ flag_count--;
+ }
+ else {
+ // Next byte must be a flag.
+
+ if (GetChars((char *) &flags, 1) != 1)
+ break;
+
+ // Set the flag counter. While at first it might appear
+ // that this should be an 8 since there are 8 bits in the
+ // flag, it should really be a 7 because the shift must
+ // be performed 7 times in order to see all 8 bits.
+
+ flag_count = 7;
+ }
+
+ // If the low order bit of the flag is now set, then we know
+ // that the next byte is a single, unencoded character.
+
+ if (flags & 1) {
+ if (GetChars((char *) c, 1) != 1)
+ break;
+
+ if (SendChars((char *) c, 1) != 1) {
+ totalLen++;
+ break;
+ }
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[0];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Otherwise, we know that the next two bytes are a
+ // <position,length> pair. The position is in 12 bits and
+ // the length is in 4 bits.
+
+ else {
+ // Original code:
+ // if ((i = getc(infile)) == EOF)
+ // break;
+ // if ((j = getc(infile)) == EOF)
+ // break;
+ // i |= ((j & 0xf0) << 4);
+ // j = (j & 0x0f) + THRESHOLD;
+ //
+ // I've modified this to only make one input call, and
+ // have changed the variable names to something more
+ // obvious.
+
+ if (GetChars((char *) c, 2) != 2)
+ break;
+
+ // Convert these two characters into the position and
+ // length. Note that the length is always at least
+ // THRESHOLD, which is why we're able to get a length
+ // of 18 out of only 4 bits.
+
+ pos = (short int) ( c[0] | ((c[1] & 0xf0) << 4) );
+
+ len = (short int) ( (c[1] & 0x0f) + THRESHOLD );
+
+ // There are now "len" characters at position "pos" in
+ // the ring buffer that can be pulled out. Note that
+ // len is never more than F.
+
+ for (k = 0; k < len; k++) {
+ c[k] = m_ring_buffer[(pos + k) & (N - 1)];
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[k];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Add the "len" :characters to the output stream.
+
+ if (SendChars((char *) c, len) != (unsigned int)len) {
+ totalLen += len;
+ break;
+ }
+ }
+ }
+ slen = totalLen;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/rawstr.cpp b/src/modules/common/rawstr.cpp
new file mode 100644
index 0000000..d2da1e9
--- /dev/null
+++ b/src/modules/common/rawstr.cpp
@@ -0,0 +1,565 @@
+/******************************************************************************
+ * rawstr.cpp - code for class 'RawStr'- a module that reads raw text
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ * and provides lookup and parsing functions based on
+ * class StrKey
+ */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <utilstr.h>
+#include <rawstr.h>
+#include <sysdata.h>
+#include <swlog.h>
+#include <filemgr.h>
+#include <swbuf.h>
+#include <stringmgr.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * RawStr Statics
+ */
+
+int RawStr::instance = 0;
+char RawStr::nl = '\n';
+
+
+/******************************************************************************
+ * RawStr Constructor - Initializes data for instance of RawStr
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ * be sure to include the trailing separator (e.g. '/' or '\')
+ * (e.g. 'modules/texts/rawtext/webster/')
+ */
+
+RawStr::RawStr(const char *ipath, int fileMode)
+{
+ SWBuf buf;
+
+ lastoff = -1;
+ path = 0;
+ stdstr(&path, ipath);
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s.idx", path);
+ idxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s.dat", path);
+ datfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ if (datfd < 0) {
+ SWLog::getSystemLog()->logError("%d", errno);
+ }
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * RawStr Destructor - Cleans up instance of RawStr
+ */
+
+RawStr::~RawStr()
+{
+ if (path)
+ delete [] path;
+
+ --instance;
+
+ FileMgr::getSystemFileMgr()->close(idxfd);
+ FileMgr::getSystemFileMgr()->close(datfd);
+}
+
+
+/******************************************************************************
+ * RawStr::getidxbufdat - Gets the index string at the given idx offset
+ * NOTE: buf is allocated and must be freed by
+ * calling function
+ *
+ * ENT: ioffset - offset in dat file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void RawStr::getIDXBufDat(long ioffset, char **buf)
+{
+ int size;
+ char ch;
+ if (datfd > 0) {
+ datfd->seek(ioffset, SEEK_SET);
+ for (size = 0; datfd->read(&ch, 1) == 1; size++) {
+ if ((ch == '\\') || (ch == 10) || (ch == 13))
+ break;
+ }
+ *buf = (*buf) ? (char *)realloc(*buf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ if (size) {
+ datfd->seek(ioffset, SEEK_SET);
+ datfd->read(*buf, size);
+ }
+ (*buf)[size] = 0;
+ toupperstr_utf8(*buf, size*2);
+ }
+ else {
+ *buf = (*buf) ? (char *)realloc(*buf, 1) : (char *)malloc(1);
+ **buf = 0;
+ }
+}
+
+
+/******************************************************************************
+ * RawStr::getidxbuf - Gets the index string at the given idx offset
+ * NOTE: buf is allocated and must be freed by
+ * calling function
+ *
+ * ENT: ioffset - offset in idx file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void RawStr::getIDXBuf(long ioffset, char **buf)
+{
+ long offset;
+
+ if (idxfd > 0) {
+ idxfd->seek(ioffset, SEEK_SET);
+ idxfd->read(&offset, 4);
+
+ offset = swordtoarch32(offset);
+
+ getIDXBufDat(offset, buf);
+ }
+}
+
+
+/******************************************************************************
+ * RawStr::findoffset - Finds the offset of the key string from the indexes
+ *
+ * ENT: key - key string to lookup
+ * start - address to store the starting offset
+ * size - address to store the size of the entry
+ * away - number of entries before of after to jump
+ * (default = 0)
+ *
+ * RET: error status -1 general error; -2 new file
+ */
+
+signed char RawStr::findOffset(const char *ikey, long *start, unsigned short *size, long away, long *idxoff)
+{
+ char *trybuf, *maxbuf, *key = 0, quitflag = 0;
+ signed char retval = -1;
+ long headoff, tailoff, tryoff = 0, maxoff = 0;
+ int diff = 0;
+
+ if (idxfd->getFd() >=0) {
+ tailoff = maxoff = idxfd->seek(0, SEEK_END) - 6;
+ retval = (tailoff >= 0) ? 0 : -2; // if NOT new file
+ if (*ikey) {
+ headoff = 0;
+
+ stdstr(&key, ikey, 3);
+ toupperstr_utf8(key, strlen(key)*3);
+
+ int keylen = strlen(key);
+ bool substr = false;
+
+ trybuf = maxbuf = 0;
+ getIDXBuf(maxoff, &maxbuf);
+
+ while (headoff < tailoff) {
+ tryoff = (lastoff == -1) ? headoff + ((((tailoff / 6) - (headoff / 6))) / 2) * 6 : lastoff;
+ lastoff = -1;
+ getIDXBuf(tryoff, &trybuf);
+
+ if (!*trybuf && tryoff) { // In case of extra entry at end of idx (not first entry)
+ tryoff += (tryoff > (maxoff / 2))?-6:6;
+ retval = -1;
+ break;
+ }
+
+ diff = strcmp(key, trybuf);
+
+ if (!diff)
+ break;
+
+ if (!strncmp(trybuf, key, keylen)) substr = true;
+
+ if (diff < 0)
+ tailoff = (tryoff == headoff) ? headoff : tryoff;
+ else headoff = tryoff;
+
+ if (tailoff == headoff + 6) {
+ if (quitflag++)
+ headoff = tailoff;
+ }
+ }
+
+ // didn't find exact match
+ if (headoff >= tailoff) {
+ tryoff = headoff;
+ if (!substr && ((tryoff != maxoff)||(strncmp(key, maxbuf, keylen)<0))) {
+ away--; // if our entry doesn't startwith our key, prefer the previous entry over the next
+ }
+ }
+ if (trybuf)
+ free(trybuf);
+ delete [] key;
+ if (maxbuf)
+ free(maxbuf);
+ }
+ else tryoff = 0;
+
+ idxfd->seek(tryoff, SEEK_SET);
+
+ *start = *size = 0;
+ idxfd->read(start, 4);
+ idxfd->read(size, 2);
+ if (idxoff)
+ *idxoff = tryoff;
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch16(*size);
+
+ while (away) {
+ long laststart = *start;
+ unsigned short lastsize = *size;
+ long lasttry = tryoff;
+ tryoff += (away > 0) ? 6 : -6;
+
+ bool bad = false;
+ if (((tryoff + (away*6)) < -6) || (tryoff + (away*6) > (maxoff+6)))
+ bad = true;
+ else if (idxfd->seek(tryoff, SEEK_SET) < 0)
+ bad = true;
+ if (bad) {
+ retval = -1;
+ *start = laststart;
+ *size = lastsize;
+ tryoff = lasttry;
+ if (idxoff)
+ *idxoff = tryoff;
+ break;
+ }
+ idxfd->read(start, 4);
+ idxfd->read(size, 2);
+ if (idxoff)
+ *idxoff = tryoff;
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch16(*size);
+
+ if (((laststart != *start) || (lastsize != *size)) && (*start >= 0) && (*size))
+ away += (away < 0) ? 1 : -1;
+ }
+
+ lastoff = tryoff;
+ }
+ else {
+ *start = 0;
+ *size = 0;
+ if (idxoff)
+ *idxoff = 0;
+ retval = -1;
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * RawStr::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void RawStr::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+/******************************************************************************
+ * RawStr::readtext - gets text at a given offset
+ *
+ * ENT:
+ * start - starting offset where the text is located in the file
+ * size - size of text entry
+ * buf - buffer to store text
+ *
+ */
+
+void RawStr::readText(long istart, unsigned short *isize, char **idxbuf, SWBuf &buf)
+{
+ unsigned int ch;
+ char *idxbuflocal = 0;
+ getIDXBufDat(istart, &idxbuflocal);
+ long start = istart;
+
+ do {
+ if (*idxbuf)
+ delete [] *idxbuf;
+
+ buf = "";
+ buf.setFillByte(0);
+ buf.setSize(++(*isize));
+
+ *idxbuf = new char [ (*isize) ];
+
+ datfd->seek(start, SEEK_SET);
+ datfd->read(buf.getRawData(), (int)((*isize) - 1));
+
+ for (ch = 0; buf[ch]; ch++) { // skip over index string
+ if (buf[ch] == 10) {
+ ch++;
+ break;
+ }
+ }
+ buf = SWBuf(buf.c_str()+ch);
+ // resolve link
+ if (!strncmp(buf.c_str(), "@LINK", 5)) {
+ for (ch = 0; buf[ch]; ch++) { // null before nl
+ if (buf[ch] == 10) {
+ buf[ch] = 0;
+ break;
+ }
+ }
+ findOffset(buf.c_str() + 6, &start, isize);
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+
+ if (idxbuflocal) {
+ int localsize = strlen(idxbuflocal);
+ localsize = (localsize < (*isize - 1)) ? localsize : (*isize - 1);
+ strncpy(*idxbuf, idxbuflocal, localsize);
+ (*idxbuf)[localsize] = 0;
+ free(idxbuflocal);
+ }
+}
+
+
+/******************************************************************************
+ * RawLD::settext - Sets text for current offset
+ *
+ * ENT: key - key for this entry
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void RawStr::doSetText(const char *ikey, const char *buf, long len)
+{
+
+ long start, outstart;
+ long idxoff;
+ long endoff;
+ long shiftSize;
+ unsigned short size;
+ unsigned short outsize;
+ static const char nl[] = {13, 10};
+ char *tmpbuf = 0;
+ char *key = 0;
+ char *dbKey = 0;
+ char *idxBytes = 0;
+ char *outbuf = 0;
+ char *ch = 0;
+
+ char errorStatus = findOffset(ikey, &start, &size, 0, &idxoff);
+ stdstr(&key, ikey, 2);
+ toupperstr_utf8(key, strlen(key)*2);
+
+ len = (len < 0) ? strlen(buf) : len;
+
+ getIDXBufDat(start, &dbKey);
+
+ if (strcmp(key, dbKey) < 0) {
+ }
+ else if (strcmp(key, dbKey) > 0) {
+ if (errorStatus != (char)-2) // not a new file
+ idxoff += 6;
+ else idxoff = 0;
+ }
+ else if ((!strcmp(key, dbKey)) && (len>0 /*we're not deleting*/)) { // got absolute entry
+ do {
+ tmpbuf = new char [ size + 2 ];
+ memset(tmpbuf, 0, size + 2);
+ datfd->seek(start, SEEK_SET);
+ datfd->read(tmpbuf, (int)(size - 1));
+
+ for (ch = tmpbuf; *ch; ch++) { // skip over index string
+ if (*ch == 10) {
+ ch++;
+ break;
+ }
+ }
+ memmove(tmpbuf, ch, size - (unsigned short)(ch-tmpbuf));
+
+ // resolve link
+ if (!strncmp(tmpbuf, "@LINK", 5) && (len)) {
+ for (ch = tmpbuf; *ch; ch++) { // null before nl
+ if (*ch == 10) {
+ *ch = 0;
+ break;
+ }
+ }
+ findOffset(tmpbuf + 6, &start, &size, 0, &idxoff);
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+ }
+
+ endoff = idxfd->seek(0, SEEK_END);
+
+ shiftSize = endoff - idxoff;
+
+ if (shiftSize > 0) {
+ idxBytes = new char [ shiftSize ];
+ idxfd->seek(idxoff, SEEK_SET);
+ idxfd->read(idxBytes, shiftSize);
+ }
+
+ outbuf = new char [ len + strlen(key) + 5 ];
+ sprintf(outbuf, "%s%c%c", key, 13, 10);
+ size = strlen(outbuf);
+ memcpy(outbuf + size, buf, len);
+ size = outsize = size + (len);
+
+ start = outstart = datfd->seek(0, SEEK_END);
+
+ outstart = archtosword32(start);
+ outsize = archtosword16(size);
+
+ idxfd->seek(idxoff, SEEK_SET);
+ if (len > 0) {
+ datfd->seek(start, SEEK_SET);
+ datfd->write(outbuf, (int)size);
+
+ // add a new line to make data file easier to read in an editor
+ datfd->write(&nl, 2);
+
+ idxfd->write(&outstart, 4);
+ idxfd->write(&outsize, 2);
+ if (idxBytes) {
+ idxfd->write(idxBytes, shiftSize);
+ delete [] idxBytes;
+ }
+ }
+ else { // delete entry
+ if (idxBytes) {
+ idxfd->write(idxBytes+6, shiftSize-6);
+ idxfd->seek(-1, SEEK_CUR); // last valid byte
+ FileMgr::getSystemFileMgr()->trunc(idxfd); // truncate index
+ delete [] idxBytes;
+ }
+ }
+
+ delete [] key;
+ delete [] outbuf;
+ free(dbKey);
+}
+
+
+/******************************************************************************
+ * RawLD::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void RawStr::doLinkEntry(const char *destkey, const char *srckey) {
+ char *text = new char [ strlen(destkey) + 7 ];
+ sprintf(text, "@LINK %s", destkey);
+ doSetText(srckey, text);
+ delete [] text;
+}
+
+
+/******************************************************************************
+ * RawLD::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+signed char RawStr::createModule(const char *ipath)
+{
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s.dat", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s.idx", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/rawstr4.cpp b/src/modules/common/rawstr4.cpp
new file mode 100644
index 0000000..cbc8384
--- /dev/null
+++ b/src/modules/common/rawstr4.cpp
@@ -0,0 +1,572 @@
+/******************************************************************************
+ * rawstr.cpp - code for class 'RawStr'- a module that reads raw text
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ * and provides lookup and parsing functions based on
+ * class StrKey
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <utilstr.h>
+#include <rawstr4.h>
+#include <sysdata.h>
+#include <swlog.h>
+#include <filemgr.h>
+#include <swbuf.h>
+#include <stringmgr.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * RawStr Statics
+ */
+
+int RawStr4::instance = 0;
+
+
+/******************************************************************************
+ * RawStr Constructor - Initializes data for instance of RawStr
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ * be sure to include the trailing separator (e.g. '/' or '\')
+ * (e.g. 'modules/texts/rawtext/webster/')
+ */
+
+RawStr4::RawStr4(const char *ipath, int fileMode)
+{
+ SWBuf buf;
+
+ nl = '\n';
+ lastoff = -1;
+ path = 0;
+ stdstr(&path, ipath);
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s.idx", path);
+ idxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s.dat", path);
+ datfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ if (datfd < 0) {
+ SWLog::getSystemLog()->logError("%d", errno);
+ }
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * RawStr Destructor - Cleans up instance of RawStr
+ */
+
+RawStr4::~RawStr4()
+{
+ if (path)
+ delete [] path;
+
+ --instance;
+
+ FileMgr::getSystemFileMgr()->close(idxfd);
+ FileMgr::getSystemFileMgr()->close(datfd);
+}
+
+
+/******************************************************************************
+ * RawStr4::getidxbufdat - Gets the index string at the given idx offset
+ * NOTE: buf is allocated and must be freed by
+ * calling function
+ *
+ * ENT: ioffset - offset in dat file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void RawStr4::getIDXBufDat(long ioffset, char **buf) {
+ int size;
+ char ch;
+ if (datfd > 0) {
+ datfd->seek(ioffset, SEEK_SET);
+ for (size = 0; datfd->read(&ch, 1) == 1; size++) {
+ if ((ch == '\\') || (ch == 10) || (ch == 13))
+ break;
+ }
+ *buf = (*buf) ? (char *)realloc(*buf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ if (size) {
+ datfd->seek(ioffset, SEEK_SET);
+ datfd->read(*buf, size);
+ }
+ (*buf)[size] = 0;
+ toupperstr_utf8(*buf, size*2);
+ }
+ else {
+ *buf = (*buf) ? (char *)realloc(*buf, 1) : (char *)malloc(1);
+ **buf = 0;
+ }
+}
+
+
+/******************************************************************************
+ * RawStr4::getidxbuf - Gets the index string at the given idx offset
+ * NOTE: buf is allocated and must be freed by
+ * calling function
+ *
+ * ENT: ioffset - offset in idx file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void RawStr4::getIDXBuf(long ioffset, char **buf)
+{
+ long offset;
+
+ if (idxfd > 0) {
+ idxfd->seek(ioffset, SEEK_SET);
+ idxfd->read(&offset, 4);
+
+ offset = swordtoarch32(offset);
+
+ getIDXBufDat(offset, buf);
+
+/* What the heck is this supposed to do??????
+ for (trybuf = targetbuf = *buf; *trybuf; trybuf++, targetbuf++) {
+ *targetbuf = *trybuf;
+ }
+ *targetbuf = 0;
+ trybuf = 0;
+ toupperstr_utf8(targetbuf);
+*/
+ }
+}
+
+
+/******************************************************************************
+ * RawStr4::findoffset - Finds the offset of the key string from the indexes
+ *
+ * ENT: key - key string to lookup
+ * start - address to store the starting offset
+ * size - address to store the size of the entry
+ * away - number of entries before of after to jump
+ * (default = 0)
+ *
+ * RET: error status -1 general error; -2 new file
+ */
+
+signed char RawStr4::findOffset(const char *ikey, long *start, unsigned long *size, long away, long *idxoff)
+{
+ char *trybuf, *maxbuf, *key = 0, quitflag = 0;
+ signed char retval = -1;
+ long headoff, tailoff, tryoff = 0, maxoff = 0;
+ int diff = 0;
+
+ if (idxfd->getFd() >=0) {
+ tailoff = maxoff = idxfd->seek(0, SEEK_END) - 8;
+ retval = (tailoff >= 0) ? 0 : -2; // if NOT new file
+ if (*ikey) {
+ headoff = 0;
+
+ stdstr(&key, ikey, 3);
+ toupperstr_utf8(key, strlen(key)*3);
+
+ int keylen = strlen(key);
+ bool substr = false;
+
+ trybuf = maxbuf = 0;
+ getIDXBuf(maxoff, &maxbuf);
+
+ while (headoff < tailoff) {
+ tryoff = (lastoff == -1) ? headoff + ((((tailoff / 8) - (headoff / 8))) / 2) * 8 : lastoff;
+ lastoff = -1;
+ getIDXBuf(tryoff, &trybuf);
+
+ if (!*trybuf && tryoff) { // In case of extra entry at end of idx (not first entry)
+ tryoff += (tryoff > (maxoff / 2))?-8:8;
+ retval = -1;
+ break;
+ }
+
+ diff = strcmp(key, trybuf);
+
+ if (!diff)
+ break;
+
+ if (!strncmp(trybuf, key, keylen)) substr = true;
+
+ if (diff < 0)
+ tailoff = (tryoff == headoff) ? headoff : tryoff;
+ else headoff = tryoff;
+
+ if (tailoff == headoff + 8) {
+ if (quitflag++)
+ headoff = tailoff;
+ }
+ }
+
+ // didn't find exact match
+ if (headoff >= tailoff) {
+ tryoff = headoff;
+ if (!substr && ((tryoff != maxoff)||(strncmp(key, maxbuf, keylen)<0))) {
+ away--; // if our entry doesn't startwith our key, prefer the previous entry over the next
+ }
+ }
+ if (trybuf)
+ free(trybuf);
+ delete [] key;
+ if (maxbuf)
+ free(maxbuf);
+ }
+ else tryoff = 0;
+
+ idxfd->seek(tryoff, SEEK_SET);
+
+ *start = *size = 0;
+ idxfd->read(start, 4);
+ idxfd->read(size, 4);
+ if (idxoff)
+ *idxoff = tryoff;
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch32(*size);
+
+ while (away) {
+ long laststart = *start;
+ unsigned long lastsize = *size;
+ long lasttry = tryoff;
+ tryoff += (away > 0) ? 8 : -8;
+
+ bool bad = false;
+ if (((tryoff + (away*8)) < -8) || (tryoff + (away*8) > (maxoff+8)))
+ bad = true;
+ else if (idxfd->seek(tryoff, SEEK_SET) < 0)
+ bad = true;
+ if (bad) {
+ retval = -1;
+ *start = laststart;
+ *size = lastsize;
+ tryoff = lasttry;
+ if (idxoff)
+ *idxoff = tryoff;
+ break;
+ }
+ idxfd->read(start, 4);
+ idxfd->read(size, 4);
+ if (idxoff)
+ *idxoff = tryoff;
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch32(*size);
+
+ if (((laststart != *start) || (lastsize != *size)) && (*start >= 0) && (*size))
+ away += (away < 0) ? 1 : -1;
+ }
+
+ lastoff = tryoff;
+ }
+ else {
+ *start = 0;
+ *size = 0;
+ if (idxoff)
+ *idxoff = 0;
+ retval = -1;
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * RawStr4::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void RawStr4::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+/******************************************************************************
+ * RawStr4::readtext - gets text at a given offset
+ *
+ * ENT:
+ * start - starting offset where the text is located in the file
+ * size - size of text entry
+ * buf - buffer to store text
+ *
+ */
+
+void RawStr4::readText(long istart, unsigned long *isize, char **idxbuf, SWBuf &buf)
+{
+ unsigned int ch;
+ char *idxbuflocal = 0;
+ getIDXBufDat(istart, &idxbuflocal);
+ long start = istart;
+
+ do {
+ if (*idxbuf)
+ delete [] *idxbuf;
+
+ buf = "";
+ buf.setFillByte(0);
+ buf.setSize(++(*isize));
+
+ *idxbuf = new char [ (*isize) ];
+
+ datfd->seek(start, SEEK_SET);
+ datfd->read(buf.getRawData(), (int)((*isize) - 1));
+
+ for (ch = 0; buf[ch]; ch++) { // skip over index string
+ if (buf[ch] == 10) {
+ ch++;
+ break;
+ }
+ }
+ buf = SWBuf(buf.c_str()+ch);
+ // resolve link
+ if (!strncmp(buf.c_str(), "@LINK", 5)) {
+ for (ch = 0; buf[ch]; ch++) { // null before nl
+ if (buf[ch] == 10) {
+ buf[ch] = 0;
+ break;
+ }
+ }
+ findOffset(buf.c_str() + 6, &start, isize);
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+
+ if (idxbuflocal) {
+ unsigned int localsize = strlen(idxbuflocal);
+ localsize = (localsize < (*isize - 1)) ? localsize : (*isize - 1);
+ strncpy(*idxbuf, idxbuflocal, localsize);
+ (*idxbuf)[localsize] = 0;
+ free(idxbuflocal);
+ }
+}
+
+
+/******************************************************************************
+ * RawLD::settext - Sets text for current offset
+ *
+ * ENT: key - key for this entry
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void RawStr4::doSetText(const char *ikey, const char *buf, long len) {
+
+ long start, outstart;
+ long idxoff;
+ long endoff;
+ long shiftSize;
+ unsigned long size;
+ unsigned long outsize;
+ static const char nl[] = {13, 10};
+ char *tmpbuf = 0;
+ char *key = 0;
+ char *dbKey = 0;
+ char *idxBytes = 0;
+ char *outbuf = 0;
+ char *ch = 0;
+
+ char errorStatus = findOffset(ikey, &start, &size, 0, &idxoff);
+ stdstr(&key, ikey, 3);
+ toupperstr_utf8(key, strlen(key)*3);
+
+ len = (len < 0) ? strlen(buf) : len;
+ getIDXBufDat(start, &dbKey);
+
+ if (strcmp(key, dbKey) < 0) {
+ }
+ else if (strcmp(key, dbKey) > 0) {
+ if (errorStatus != (char)-2) // not a new file
+ idxoff += 8;
+ else idxoff = 0;
+ }
+ else if ((!strcmp(key, dbKey)) && (len>0/*we're not deleting*/)) { // got absolute entry
+ do {
+ tmpbuf = new char [ size + 2 ];
+ memset(tmpbuf, 0, size + 2);
+ datfd->seek(start, SEEK_SET);
+ datfd->read(tmpbuf, (int)(size - 1));
+
+ for (ch = tmpbuf; *ch; ch++) { // skip over index string
+ if (*ch == 10) {
+ ch++;
+ break;
+ }
+ }
+ memmove(tmpbuf, ch, size - (unsigned long)(ch-tmpbuf));
+
+ // resolve link
+ if (!strncmp(tmpbuf, "@LINK", 5) && (len > 0)) {
+ for (ch = tmpbuf; *ch; ch++) { // null before nl
+ if (*ch == 10) {
+ *ch = 0;
+ break;
+ }
+ }
+ findOffset(tmpbuf + 8, &start, &size, 0, &idxoff);
+ ++size;
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+ }
+
+ endoff = idxfd->seek(0, SEEK_END);
+
+ shiftSize = endoff - idxoff;
+
+ if (shiftSize > 0) {
+ idxBytes = new char [ shiftSize ];
+ idxfd->seek(idxoff, SEEK_SET);
+ idxfd->read(idxBytes, shiftSize);
+ }
+
+ outbuf = new char [ len + strlen(key) + 5 ];
+ sprintf(outbuf, "%s%c%c", key, 13, 10);
+ size = strlen(outbuf);
+ memcpy(outbuf + size, buf, len);
+ size = outsize = size + len;
+
+ start = outstart = datfd->seek(0, SEEK_END);
+
+ outstart = archtosword32(start);
+ outsize = archtosword32(size);
+
+ idxfd->seek(idxoff, SEEK_SET);
+ if (len>0) {
+ datfd->seek(start, SEEK_SET);
+ datfd->write(outbuf, (long)size);
+
+ // add a new line to make data file easier to read in an editor
+ datfd->write(&nl, 2);
+
+ idxfd->write(&outstart, 4);
+ idxfd->write(&outsize, 4);
+ if (idxBytes) {
+ idxfd->write(idxBytes, shiftSize);
+ delete [] idxBytes;
+ }
+ }
+ else { // delete entry
+ if (idxBytes) {
+ idxfd->write(idxBytes+8, shiftSize-8);
+ idxfd->seek(-1, SEEK_CUR); // last valid byte
+ FileMgr::getSystemFileMgr()->trunc(idxfd); // truncate index
+ delete [] idxBytes;
+ }
+ }
+
+ delete [] key;
+ delete [] outbuf;
+ free(dbKey);
+}
+
+
+/******************************************************************************
+ * RawLD::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void RawStr4::doLinkEntry(const char *destkey, const char *srckey) {
+ char *text = new char [ strlen(destkey) + 7 ];
+ sprintf(text, "@LINK %s", destkey);
+ doSetText(srckey, text);
+ delete [] text;
+}
+
+
+/******************************************************************************
+ * RawLD::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+signed char RawStr4::createModule(const char *ipath)
+{
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s.dat", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s.idx", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/rawverse.cpp b/src/modules/common/rawverse.cpp
new file mode 100644
index 0000000..934082c
--- /dev/null
+++ b/src/modules/common/rawverse.cpp
@@ -0,0 +1,350 @@
+/******************************************************************************
+ * rawverse.cpp - code for class 'RawVerse'- a module that reads raw text
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ * and provides lookup and parsing functions based on
+ * class VerseKey
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <utilstr.h>
+#include <rawverse.h>
+#include <versekey.h>
+#include <sysdata.h>
+#include <filemgr.h>
+#include <swbuf.h>
+
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * RawVerse Statics
+ */
+
+int RawVerse::instance = 0;
+const char *RawVerse::nl = "\r\n";
+
+
+/******************************************************************************
+ * RawVerse Constructor - Initializes data for instance of RawVerse
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ * be sure to include the trailing separator (e.g. '/' or '\')
+ * (e.g. 'modules/texts/rawtext/webster/')
+ */
+
+RawVerse::RawVerse(const char *ipath, int fileMode)
+{
+ SWBuf buf;
+
+ path = 0;
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s/ot.vss", path);
+ idxfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt.vss", path);
+ idxfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/ot", path);
+ textfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt", path);
+ textfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * RawVerse Destructor - Cleans up instance of RawVerse
+ */
+
+RawVerse::~RawVerse()
+{
+ int loop1;
+
+ if (path)
+ delete [] path;
+
+ --instance;
+
+ for (loop1 = 0; loop1 < 2; loop1++) {
+ FileMgr::getSystemFileMgr()->close(idxfp[loop1]);
+ FileMgr::getSystemFileMgr()->close(textfp[loop1]);
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse::findoffset - Finds the offset of the key verse from the indexes
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * idxoff - offset into .vss
+ * start - address to store the starting offset
+ * size - address to store the size of the entry
+ */
+
+void RawVerse::findOffset(char testmt, long idxoff, long *start, unsigned short *size) {
+ idxoff *= 6;
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ if (idxfp[testmt-1]->getFd() >= 0) {
+ idxfp[testmt-1]->seek(idxoff, SEEK_SET);
+ idxfp[testmt-1]->read(start, 4);
+ long len = idxfp[testmt-1]->read(size, 2); // read size
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch16(*size);
+
+ if (len < 2) {
+ *size = (unsigned short)((*start) ? (textfp[testmt-1]->seek(0, SEEK_END) - (long)*start) : 0); // if for some reason we get an error reading size, make size to end of file
+ }
+ }
+ else {
+ *start = 0;
+ *size = 0;
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void RawVerse::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse::readtext - gets text at a given offset
+ *
+ * ENT: testmt - testament file to search in (0 - Old; 1 - New)
+ * start - starting offset where the text is located in the file
+ * size - size of text entry + 2 (null)(null)
+ * buf - buffer to store text
+ *
+ */
+
+void RawVerse::readText(char testmt, long start, unsigned short size, SWBuf &buf) {
+ buf = "";
+ buf.setFillByte(0);
+ buf.setSize(size + 1);
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+ if (size) {
+ if (textfp[testmt-1]->getFd() >= 0) {
+ textfp[testmt-1]->seek(start, SEEK_SET);
+ textfp[testmt-1]->read(buf.getRawData(), (int)size);
+ }
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse::settext - Sets text for current offset
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * idxoff - offset into .vss
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void RawVerse::doSetText(char testmt, long idxoff, const char *buf, long len)
+{
+ long start, outstart;
+ unsigned short size;
+ unsigned short outsize;
+
+ idxoff *= 6;
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ size = outsize = (len < 0) ? strlen(buf) : len;
+
+ start = outstart = textfp[testmt-1]->seek(0, SEEK_END);
+ idxfp[testmt-1]->seek(idxoff, SEEK_SET);
+
+ if (size) {
+ textfp[testmt-1]->seek(start, SEEK_SET);
+ textfp[testmt-1]->write(buf, (int)size);
+
+ // add a new line to make data file easier to read in an editor
+ textfp[testmt-1]->write(nl, 2);
+ }
+ else {
+ start = 0;
+ }
+
+ outstart = archtosword32(start);
+ outsize = archtosword16(size);
+
+ idxfp[testmt-1]->write(&outstart, 4);
+ idxfp[testmt-1]->write(&outsize, 2);
+
+
+}
+
+
+/******************************************************************************
+ * RawVerse::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void RawVerse::doLinkEntry(char testmt, long destidxoff, long srcidxoff) {
+ long start;
+ unsigned short size;
+
+ destidxoff *= 6;
+ srcidxoff *= 6;
+
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ // get source
+ idxfp[testmt-1]->seek(srcidxoff, SEEK_SET);
+ idxfp[testmt-1]->read(&start, 4);
+ idxfp[testmt-1]->read(&size, 2);
+
+ // write dest
+ idxfp[testmt-1]->seek(destidxoff, SEEK_SET);
+ idxfp[testmt-1]->write(&start, 4);
+ idxfp[testmt-1]->write(&size, 2);
+}
+
+
+/******************************************************************************
+ * RawVerse::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+char RawVerse::createModule(const char *ipath)
+{
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s/ot", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/nt", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/ot.vss", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+
+ sprintf(buf, "%s/nt.vss", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+
+ VerseKey vk;
+ vk.Headings(1);
+ long offset = 0;
+ short size = 0;
+ for (vk = TOP; !vk.Error(); vk++) {
+ if (vk.Testament() == 1) {
+ fd->write(&offset, 4);
+ fd->write(&size, 2);
+ }
+ else {
+ fd2->write(&offset, 4);
+ fd2->write(&size, 2);
+ }
+ }
+
+ FileMgr::getSystemFileMgr()->close(fd);
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+ delete [] buf;
+/*
+ RawVerse rv(path);
+ VerseKey mykey("Rev 22:21");
+*/
+
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/rawverse4.cpp b/src/modules/common/rawverse4.cpp
new file mode 100644
index 0000000..bd438ec
--- /dev/null
+++ b/src/modules/common/rawverse4.cpp
@@ -0,0 +1,350 @@
+/******************************************************************************
+ * rawverse.cpp - code for class 'RawVerse4'- a module that reads raw text
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ * and provides lookup and parsing functions based on
+ * class VerseKey
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <utilstr.h>
+#include <rawverse4.h>
+#include <versekey.h>
+#include <sysdata.h>
+#include <filemgr.h>
+#include <swbuf.h>
+
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * RawVerse4 Statics
+ */
+
+int RawVerse4::instance = 0;
+const char *RawVerse4::nl = "\r\n";
+
+
+/******************************************************************************
+ * RawVerse4 Constructor - Initializes data for instance of RawVerse4
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ * be sure to include the trailing separator (e.g. '/' or '\')
+ * (e.g. 'modules/texts/rawtext/webster/')
+ */
+
+RawVerse4::RawVerse4(const char *ipath, int fileMode)
+{
+ SWBuf buf;
+
+ path = 0;
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s/ot.vss", path);
+ idxfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt.vss", path);
+ idxfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/ot", path);
+ textfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt", path);
+ textfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * RawVerse4 Destructor - Cleans up instance of RawVerse4
+ */
+
+RawVerse4::~RawVerse4()
+{
+ int loop1;
+
+ if (path)
+ delete [] path;
+
+ --instance;
+
+ for (loop1 = 0; loop1 < 2; loop1++) {
+ FileMgr::getSystemFileMgr()->close(idxfp[loop1]);
+ FileMgr::getSystemFileMgr()->close(textfp[loop1]);
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse4::findoffset - Finds the offset of the key verse from the indexes
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * idxoff - offset into .vss
+ * start - address to store the starting offset
+ * size - address to store the size of the entry
+ */
+
+void RawVerse4::findOffset(char testmt, long idxoff, long *start, unsigned long *size) {
+ idxoff *= 8;
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ if (idxfp[testmt-1]->getFd() >= 0) {
+ idxfp[testmt-1]->seek(idxoff, SEEK_SET);
+ idxfp[testmt-1]->read(start, 4);
+ long len = idxfp[testmt-1]->read(size, 4); // read size
+
+ *start = swordtoarch32(*start);
+ *size = swordtoarch32(*size);
+
+ if (len < 2) {
+ *size = (unsigned long)((*start) ? (textfp[testmt-1]->seek(0, SEEK_END) - (long)*start) : 0); // if for some reason we get an error reading size, make size to end of file
+ }
+ }
+ else {
+ *start = 0;
+ *size = 0;
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse4::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void RawVerse4::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse4::readtext - gets text at a given offset
+ *
+ * ENT: testmt - testament file to search in (0 - Old; 1 - New)
+ * start - starting offset where the text is located in the file
+ * size - size of text entry + 2 (null)(null)
+ * buf - buffer to store text
+ *
+ */
+
+void RawVerse4::readText(char testmt, long start, unsigned long size, SWBuf &buf) {
+ buf = "";
+ buf.setFillByte(0);
+ buf.setSize(size + 1);
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+ if (size) {
+ if (textfp[testmt-1]->getFd() >= 0) {
+ textfp[testmt-1]->seek(start, SEEK_SET);
+ textfp[testmt-1]->read(buf.getRawData(), (int)size);
+ }
+ }
+}
+
+
+/******************************************************************************
+ * RawVerse4::settext - Sets text for current offset
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * idxoff - offset into .vss
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void RawVerse4::doSetText(char testmt, long idxoff, const char *buf, long len)
+{
+ long start, outstart;
+ unsigned long size;
+ unsigned long outsize;
+
+ idxoff *= 8;
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ size = outsize = (len < 0) ? strlen(buf) : len;
+
+ start = outstart = textfp[testmt-1]->seek(0, SEEK_END);
+ idxfp[testmt-1]->seek(idxoff, SEEK_SET);
+
+ if (size) {
+ textfp[testmt-1]->seek(start, SEEK_SET);
+ textfp[testmt-1]->write(buf, (int)size);
+
+ // add a new line to make data file easier to read in an editor
+ textfp[testmt-1]->write(nl, 2);
+ }
+ else {
+ start = 0;
+ }
+
+ outstart = archtosword32(start);
+ outsize = archtosword32(size);
+
+ idxfp[testmt-1]->write(&outstart, 4);
+ idxfp[testmt-1]->write(&outsize, 4);
+
+
+}
+
+
+/******************************************************************************
+ * RawVerse4::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void RawVerse4::doLinkEntry(char testmt, long destidxoff, long srcidxoff) {
+ long start;
+ unsigned long size;
+
+ destidxoff *= 8;
+ srcidxoff *= 8;
+
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ // get source
+ idxfp[testmt-1]->seek(srcidxoff, SEEK_SET);
+ idxfp[testmt-1]->read(&start, 4);
+ idxfp[testmt-1]->read(&size, 4);
+
+ // write dest
+ idxfp[testmt-1]->seek(destidxoff, SEEK_SET);
+ idxfp[testmt-1]->write(&start, 4);
+ idxfp[testmt-1]->write(&size, 4);
+}
+
+
+/******************************************************************************
+ * RawVerse4::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+char RawVerse4::createModule(const char *ipath)
+{
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s/ot", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/nt", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/ot.vss", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+
+ sprintf(buf, "%s/nt.vss", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+
+ VerseKey vk;
+ vk.Headings(1);
+ long offset = 0;
+ long size = 0;
+ for (vk = TOP; !vk.Error(); vk++) {
+ if (vk.Testament() == 1) {
+ fd->write(&offset, 4);
+ fd->write(&size, 4);
+ }
+ else {
+ fd2->write(&offset, 4);
+ fd2->write(&size, 4);
+ }
+ }
+
+ FileMgr::getSystemFileMgr()->close(fd);
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+ delete [] buf;
+/*
+ RawVerse4 rv(path);
+ VerseKey mykey("Rev 22:21");
+*/
+
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/sapphire.cpp b/src/modules/common/sapphire.cpp
new file mode 100644
index 0000000..a2c5ce0
--- /dev/null
+++ b/src/modules/common/sapphire.cpp
@@ -0,0 +1,216 @@
+/* sapphire.cpp -- the Saphire II stream cipher class.
+ Dedicated to the Public Domain the author and inventor:
+ (Michael Paul Johnson). This code comes with no warranty.
+ Use it at your own risk.
+ Ported from the Pascal implementation of the Sapphire Stream
+ Cipher 9 December 1994.
+ Added hash pre- and post-processing 27 December 1994.
+ Modified initialization to make index variables key dependent,
+ made the output function more resistant to cryptanalysis,
+ and renamed to Sapphire II 2 January 1995
+*/
+
+
+#include <string.h>
+
+#include "sapphire.h"
+
+SWORD_NAMESPACE_START
+
+unsigned char sapphire::keyrand(int limit,
+ unsigned char *user_key,
+ unsigned char keysize,
+ unsigned char *rsum,
+ unsigned *keypos)
+ {
+ unsigned u, // Value from 0 to limit to return.
+ retry_limiter, // No infinite loops allowed.
+ mask; // Select just enough bits.
+
+ if (!limit) return 0; // Avoid divide by zero error.
+ retry_limiter = 0;
+ mask = 1; // Fill mask with enough bits to cover
+ while (mask < (unsigned)limit) // the desired range.
+ mask = (mask << 1) + 1;
+ do
+ {
+ *rsum = cards[*rsum] + user_key[(*keypos)++];
+ if (*keypos >= keysize)
+ {
+ *keypos = 0; // Recycle the user key.
+ *rsum += keysize; // key "aaaa" != key "aaaaaaaa"
+ }
+ u = mask & *rsum;
+ if (++retry_limiter > 11)
+ u %= limit; // Prevent very rare long loops.
+ }
+ while (u > (unsigned)limit);
+ return u;
+ }
+
+void sapphire::initialize(unsigned char *key, unsigned char keysize)
+ {
+ // Key size may be up to 256 bytes.
+ // Pass phrases may be used directly, with longer length
+ // compensating for the low entropy expected in such keys.
+ // Alternatively, shorter keys hashed from a pass phrase or
+ // generated randomly may be used. For random keys, lengths
+ // of from 4 to 16 bytes are recommended, depending on how
+ // secure you want this to be.
+
+ int i;
+ unsigned char toswap, swaptemp, rsum;
+ unsigned keypos;
+
+ // If we have been given no key, assume the default hash setup.
+
+ if (keysize < 1)
+ {
+ hash_init();
+ return;
+ }
+
+ // Start with cards all in order, one of each.
+
+ for (i=0;i<256;i++)
+ cards[i] = i;
+
+ // Swap the card at each position with some other card.
+
+ toswap = 0;
+ keypos = 0; // Start with first byte of user key.
+ rsum = 0;
+ for (i=255;i>=0;i--)
+ {
+ toswap = keyrand(i, key, keysize, &rsum, &keypos);
+ swaptemp = cards[i];
+ cards[i] = cards[toswap];
+ cards[toswap] = swaptemp;
+ }
+
+ // Initialize the indices and data dependencies.
+ // Indices are set to different values instead of all 0
+ // to reduce what is known about the state of the cards
+ // when the first byte is emitted.
+
+ rotor = cards[1];
+ ratchet = cards[3];
+ avalanche = cards[5];
+ last_plain = cards[7];
+ last_cipher = cards[rsum];
+
+ toswap = swaptemp = rsum = 0;
+ keypos = 0;
+ }
+
+void sapphire::hash_init(void)
+ {
+ // This function is used to initialize non-keyed hash
+ // computation.
+
+ int i, j;
+
+ // Initialize the indices and data dependencies.
+
+ rotor = 1;
+ ratchet = 3;
+ avalanche = 5;
+ last_plain = 7;
+ last_cipher = 11;
+
+ // Start with cards all in inverse order.
+
+ for (i=0, j=255;i<256;i++,j--)
+ cards[i] = (unsigned char) j;
+ }
+
+sapphire::sapphire(unsigned char *key, unsigned char keysize)
+ {
+ if (key && keysize)
+ initialize(key, keysize);
+ }
+
+void sapphire::burn(void)
+ {
+ // Destroy the key and state information in RAM.
+ memset(cards, 0, 256);
+ rotor = ratchet = avalanche = last_plain = last_cipher = 0;
+ }
+
+sapphire::~sapphire()
+ {
+ burn();
+ }
+
+unsigned char sapphire::encrypt(unsigned char b)
+ {
+#ifdef USBINARY
+ // Picture a single enigma rotor with 256 positions, rewired
+ // on the fly by card-shuffling.
+
+ // This cipher is a variant of one invented and written
+ // by Michael Paul Johnson in November, 1993.
+
+ unsigned char swaptemp;
+
+ // Shuffle the deck a little more.
+
+ ratchet += cards[rotor++];
+ swaptemp = cards[last_cipher];
+ cards[last_cipher] = cards[ratchet];
+ cards[ratchet] = cards[last_plain];
+ cards[last_plain] = cards[rotor];
+ cards[rotor] = swaptemp;
+ avalanche += cards[swaptemp];
+
+ // Output one byte from the state in such a way as to make it
+ // very hard to figure out which one you are looking at.
+
+ last_cipher = b^cards[(cards[ratchet] + cards[rotor]) & 0xFF] ^
+ cards[cards[(cards[last_plain] +
+ cards[last_cipher] +
+ cards[avalanche])&0xFF]];
+ last_plain = b;
+ return last_cipher;
+#else
+ return b;
+#endif
+ }
+
+unsigned char sapphire::decrypt(unsigned char b)
+ {
+ unsigned char swaptemp;
+
+ // Shuffle the deck a little more.
+
+ ratchet += cards[rotor++];
+ swaptemp = cards[last_cipher];
+ cards[last_cipher] = cards[ratchet];
+ cards[ratchet] = cards[last_plain];
+ cards[last_plain] = cards[rotor];
+ cards[rotor] = swaptemp;
+ avalanche += cards[swaptemp];
+
+ // Output one byte from the state in such a way as to make it
+ // very hard to figure out which one you are looking at.
+
+ last_plain = b^cards[(cards[ratchet] + cards[rotor]) & 0xFF] ^
+ cards[cards[(cards[last_plain] +
+ cards[last_cipher] +
+ cards[avalanche])&0xFF]];
+ last_cipher = b;
+ return last_plain;
+ }
+
+void sapphire::hash_final(unsigned char *hash, // Destination
+ unsigned char hashlength) // Size of hash.
+ {
+ int i;
+
+ for (i=255;i>=0;i--)
+ encrypt((unsigned char) i);
+ for (i=0;i<hashlength;i++)
+ hash[i] = encrypt(0);
+ }
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/swcipher.cpp b/src/modules/common/swcipher.cpp
new file mode 100644
index 0000000..bd4d551
--- /dev/null
+++ b/src/modules/common/swcipher.cpp
@@ -0,0 +1,128 @@
+/******************************************************************************
+ * swcipher.cpp - code for class 'SWCipher'- a driver class that provides
+ * cipher utilities.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <swcipher.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * SWCipher Constructor - Initializes data for instance of SWCipher
+ *
+ */
+
+SWCipher::SWCipher(unsigned char *key) {
+ master.initialize(key, strlen((char *)key));
+ buf = 0;
+}
+
+
+/******************************************************************************
+ * SWCipher Destructor - Cleans up instance of SWCipher
+ */
+
+SWCipher::~SWCipher()
+{
+ if (buf)
+ free(buf);
+}
+
+
+char *SWCipher::Buf(const char *ibuf, unsigned long ilen)
+{
+ if (ibuf) {
+
+ if (buf)
+ free(buf);
+
+ if (!ilen) {
+ len = strlen(buf);
+ ilen = len + 1;
+ }
+ else len = ilen;
+
+ buf = (char *) malloc(ilen);
+ memcpy(buf, ibuf, ilen);
+ cipher = false;
+ }
+
+ Decode();
+
+ return buf;
+}
+
+
+char *SWCipher::cipherBuf(unsigned long *ilen, const char *ibuf)
+{
+ if (ibuf) {
+
+ if (buf)
+ free(buf);
+
+ buf = (char *) malloc(*ilen+1);
+ memcpy(buf, ibuf, *ilen);
+ len = *ilen;
+ cipher = true;
+ }
+
+ Encode();
+
+ *ilen = len;
+ return buf;
+}
+
+
+/******************************************************************************
+ * SWCipher::Encode - This function "encodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void SWCipher::Encode(void)
+{
+ if (!cipher) {
+ work = master;
+ for (unsigned long i = 0; i < len; i++)
+ buf[i] = work.encrypt(buf[i]);
+ cipher = true;
+ }
+}
+
+
+/******************************************************************************
+ * SWCipher::Decode - This function "decodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void SWCipher::Decode(void)
+{
+ if (cipher) {
+ work = master;
+ unsigned long i;
+ for (i = 0; i < len; i++)
+ buf[i] = work.decrypt(buf[i]);
+ buf[i] = 0;
+ cipher = false;
+ }
+}
+
+
+/******************************************************************************
+ * SWCipher::setCipherKey - setter for a new CipherKey
+ *
+ */
+
+void SWCipher::setCipherKey(const char *ikey) {
+ unsigned char *key = (unsigned char *)ikey;
+ master.initialize(key, strlen((char *)key));
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/swcomprs.cpp b/src/modules/common/swcomprs.cpp
new file mode 100644
index 0000000..02d7d7b
--- /dev/null
+++ b/src/modules/common/swcomprs.cpp
@@ -0,0 +1,193 @@
+/******************************************************************************
+ * swcomprs.cpp - code for class 'SWCompress'- a driver class that provides
+ * compression utilities.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <swcomprs.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * SWCompress Constructor - Initializes data for instance of SWCompress
+ *
+ */
+
+SWCompress::SWCompress()
+{
+ buf = zbuf = 0;
+ Init();
+}
+
+
+/******************************************************************************
+ * SWCompress Destructor - Cleans up instance of SWCompress
+ */
+
+SWCompress::~SWCompress()
+{
+ if (zbuf)
+ free(zbuf);
+
+ if (buf)
+ free(buf);
+}
+
+
+void SWCompress::Init()
+{
+ if (buf)
+ free(buf);
+
+ if (zbuf)
+ free(zbuf);
+
+ buf = 0;
+ zbuf = 0;
+ direct = 0;
+ zlen = 0;
+ slen = 0;
+ zpos = 0;
+ pos = 0;
+}
+
+
+char *SWCompress::Buf(const char *ibuf, unsigned long *len) {
+ // setting an uncompressed buffer
+ if (ibuf) {
+ Init();
+ slen = (len) ? *len : strlen(ibuf);
+ buf = (char *) calloc(slen + 1, 1);
+ memcpy(buf, ibuf, slen);
+ }
+
+ // getting an uncompressed buffer
+ if (!buf) {
+ buf = (char *)calloc(1,1); // be sure we at least allocate an empty buf for return;
+ direct = 1;
+ Decode();
+// slen = strlen(buf);
+ if (len)
+ *len = slen;
+ }
+ return buf;
+}
+
+
+char *SWCompress::zBuf(unsigned long *len, char *ibuf)
+{
+ // setting a compressed buffer
+ if (ibuf) {
+ Init();
+ zbuf = (char *) malloc(*len);
+ memcpy(zbuf, ibuf, *len);
+ zlen = *len;
+ }
+
+ // getting a compressed buffer
+ if (!zbuf) {
+ direct = 0;
+ Encode();
+ }
+
+ *len = zlen;
+ return zbuf;
+}
+
+
+unsigned long SWCompress::GetChars(char *ibuf, unsigned long len)
+{
+ if (direct) {
+ len = (((zlen - zpos) > (unsigned)len) ? len : zlen - zpos);
+ if (len > 0) {
+ memmove(ibuf, &zbuf[zpos], len);
+ zpos += len;
+ }
+ }
+ else {
+// slen = strlen(buf);
+ len = (((slen - pos) > (unsigned)len) ? len : slen - pos);
+ if (len > 0) {
+ memmove(ibuf, &buf[pos], len);
+ pos += len;
+ }
+ }
+ return len;
+}
+
+
+unsigned long SWCompress::SendChars(char *ibuf, unsigned long len)
+{
+ if (direct) {
+ if (buf) {
+// slen = strlen(buf);
+ if ((pos + len) > (unsigned)slen) {
+ buf = (char *) realloc(buf, pos + len + 1024);
+ memset(&buf[pos], 0, len + 1024);
+ }
+ }
+ else buf = (char *)calloc(1, len + 1024);
+ memmove(&buf[pos], ibuf, len);
+ pos += len;
+ }
+ else {
+ if (zbuf) {
+ if ((zpos + len) > zlen) {
+ zbuf = (char *) realloc(zbuf, zpos + len + 1024);
+ zlen = zpos + len + 1024;
+ }
+ }
+ else {
+ zbuf = (char *)calloc(1, len + 1024);
+ zlen = len + 1024;
+ }
+ memmove(&zbuf[zpos], ibuf, len);
+ zpos += len;
+ }
+ return len;
+}
+
+
+/******************************************************************************
+ * SWCompress::Encode - This function "encodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void SWCompress::Encode(void)
+{
+ cycleStream();
+}
+
+
+/******************************************************************************
+ * SWCompress::Decode - This function "decodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void SWCompress::Decode(void)
+{
+ cycleStream();
+}
+
+
+void SWCompress::cycleStream() {
+ char buf[1024];
+ unsigned long len, totlen = 0;
+
+ do {
+ len = GetChars(buf, 1024);
+ if (len)
+ totlen += SendChars(buf, len);
+ } while (len == 1024);
+
+ zlen = slen = totlen;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/swcomprs.doc b/src/modules/common/swcomprs.doc
new file mode 100644
index 0000000..b6817f2
--- /dev/null
+++ b/src/modules/common/swcomprs.doc
@@ -0,0 +1,802 @@
+The following is the original information send from Parson's Technologies via
+Craig Rairden.
+_______________________________________________________________________________
+Compression Info, 10-11-95
+Jeff Wheeler
+
+Source of Algorithm
+-------------------
+
+The compression algorithms used here are based upon the algorithms developed
+and published by Haruhiko Okumura in a paper entitled "Data Compression
+Algorithms of LARC and LHarc." This paper discusses three compression
+algorithms, LSZZ, LZARI, and LZHUF. LZSS is described as the "first" of
+these, and is described as providing moderate compression with good speed.
+LZARI is described as an improved LZSS, a combination of the LZSS algorithm
+with adaptive arithmetic compression. It is described as being slower than
+LZSS but with better compression. LZHUF (the basis of the common LHA
+compression program) was included in the paper, however, a free usage license
+was not included.
+
+The following are copies of the statements included at the beginning of each
+source code listing that was supplied in the working paper.
+
+ LZSS, dated 4/6/89, marked as "Use, distribute and
+ modify this program freely."
+
+ LZARI, dated 4/7/89, marked as "Use, distribute and
+ modify this program freely."
+
+ LZHUF, dated 11/20/88, written by Haruyasu Yoshizaki,
+ translated by Haruhiko Okumura on 4/7/89. Not
+ expressly marked as redistributable or modifiable.
+
+Since both LZSS and LZARI are marked as "use, distribute and modify freely" we
+have felt at liberty basing our compression algorithm on either of these.
+
+Selection of Algorithm
+----------------------
+
+Working samples of three possible compression algorithms are supplied in
+Okumura's paper. Which should be used?
+
+LZSS is the fastest at decompression, but does not generated as small a
+compressed file as the other methods. The other two methods provided, perhaps,
+a 15% improvement in compression. Or, put another way, on a 100K file, LZSS
+might compress it to 50K while the others might approach 40-45K. For STEP
+purposes, it was decided that decoding speed was of more importance than
+tighter compression. For these reasons, the first compression algorithm
+implemented is the LZSS algorithm.
+
+About LZSS Encoding
+-------------------
+
+(adapted from Haruhiko Okumura's paper)
+
+This scheme was proposed by Ziv and Lempel [1]. A slightly modified version
+is described by Storer and Szymanski [2]. An implementation using a binary
+tree has been proposed by Bell [3].
+
+The algorithm is quite simple.
+1. Keep a ring buffer which initially contains all space characters.
+2. Read several letters from the file to the buffer.
+3. Search the buffer for the longest string that matches the letters just
+ read, and send its length and position into the buffer.
+
+If the ring buffer is 4096 bytes, the position can be stored in 12 bits. If the
+length is represented in 4 bits, the <position, length> pair is two bytes
+long. If the longest match is no more than two characters, then just one
+character is sent without encoding. The process starts again with the next
+character. An extra bit is sent each time to tell the decoder whether the
+next item is a character of a <position, length> pair.
+
+[1] J. Ziv and A. Lempel, IEEE Transactions IT-23, 337-343 (1977).
+[2] J. A. Storer and T. G. Szymanski, J. ACM, 29, 928-951 (1982).
+[3] T.C. Gell, IEEE Transactions COM-34, 1176-1182 (1986).
+
+class SWCompress {
+public:
+void InitTree( // no return value
+ void); // no parameters
+
+void InsertNode( // no return value
+ short int Pos); // position in the buffer
+
+void DeleteNode( // no return value
+ short int Node); // node to be removed
+
+void Encode( // no return value
+ void); // no parameters
+
+void Decode( // no return value
+ void); // no parameters
+};
+
+// The following are constant sizes used by the compression algorithm.
+//
+// N - This is the size of the ring buffer. It is set
+// to 4K. It is important to note that a position
+// within the ring buffer requires 12 bits.
+//
+// F - This is the maximum length of a character sequence
+// that can be taken from the ring buffer. It is set
+// to 18. Note that a length must be 3 before it is
+// worthwhile to store a position/length pair, so the
+// length can be encoded in only 4 bits. Or, put yet
+// another way, it is not necessary to encode a length
+// of 0-18, it is necessary to encode a length of
+// 3-18, which requires 4 bits.
+//
+// THRESHOLD - It takes 2 bytes to store an offset and
+// a length. If a character sequence only
+// requires 1 or 2 characters to store
+// uncompressed, then it is better to store
+// it uncompressed than as an offset into
+// the ring buffer.
+//
+// Note that the 12 bits used to store the position and the 4 bits
+// used to store the length equal a total of 16 bits, or 2 bytes.
+
+#define N 4096
+#define F 18
+#define THRESHOLD 3
+#define NOT_USED N
+
+// m_ring_buffer is a text buffer. It contains "nodes" of
+// uncompressed text that can be indexed by position. That is,
+// a substring of the ring buffer can be indexed by a position
+// and a length. When decoding, the compressed text may contain
+// a position in the ring buffer and a count of the number of
+// bytes from the ring buffer that are to be moved into the
+// uncompressed buffer.
+//
+// This ring buffer is not maintained as part of the compressed
+// text. Instead, it is reconstructed dynamically. That is,
+// it starts out empty and gets built as the text is decompressed.
+//
+// The ring buffer contain N bytes, with an additional F - 1 bytes
+// to facilitate string comparison.
+
+unsigned char m_ring_buffer[N + F - 1];
+
+// m_match_position and m_match_length are set by InsertNode().
+//
+// These variables indicate the position in the ring buffer
+// and the number of characters at that position that match
+// a given string.
+
+short int m_match_position;
+short int m_match_length;
+
+// m_lson, m_rson, and m_dad are the Japanese way of referring to
+// a tree structure. The dad is the parent and it has a right and
+// left son (child).
+//
+// For i = 0 to N-1, m_rson[i] and m_lson[i] will be the right
+// and left children of node i.
+//
+// For i = 0 to N-1, m_dad[i] is the parent of node i.
+//
+// For i = 0 to 255, rson[N + i + 1] is the root of the tree for
+// strings that begin with the character i. Note that this requires
+// one byte characters.
+//
+// These nodes store values of 0...(N-1). Memory requirements
+// can be reduces by using 2-byte integers instead of full 4-byte
+// integers (for 32-bit applications). Therefore, these are
+// defined as "short ints."
+
+short int m_lson[N + 1];
+short int m_rson[N + 257];
+short int m_dad[N + 1];
+
+
+
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::InitTree
+
+ This function initializes the tree nodes to "empty" states.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::InitTree( // no return value
+ void) // no parameters
+ throw() // exception list
+
+ {
+ int i;
+
+ // For i = 0 to N - 1, m_rson[i] and m_lson[i] will be the right
+ // and left children of node i. These nodes need not be
+ // initialized. However, for debugging purposes, it is nice to
+ // have them initialized. Since this is only used for compression
+ // (not decompression), I don't mind spending the time to do it.
+ //
+ // For the same range of i, m_dad[i] is the parent of node i.
+ // These are initialized to a known value that can represent
+ // a "not used" state.
+
+ for (i = 0; i < N; i++)
+ {
+ m_lson[i] = NOT_USED;
+ m_rson[i] = NOT_USED;
+ m_dad[i] = NOT_USED;
+ }
+
+ // For i = 0 to 255, m_rson[N + i + 1] is the root of the tree
+ // for strings that begin with the character i. This is why
+ // the right child array is larger than the left child array.
+ // These are also initialzied to a "not used" state.
+ //
+ // Note that there are 256 of these, one for each of the possible
+ // 256 characters.
+
+ for (i = N + 1; i <= (N + 256); i++)
+ {
+ m_rson[i] = NOT_USED;
+ }
+
+ // Done.
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::InsertNode
+
+ This function inserts a string from the ring buffer into one of
+ the trees. It loads the match position and length member variables
+ for the longest match.
+
+ The string to be inserted is identified by the parameter Pos,
+ A full F bytes are inserted. So, m_ring_buffer[Pos ... Pos+F-1]
+ are inserted.
+
+ If the matched length is exactly F, then an old node is removed
+ in favor of the new one (because the old one will be deleted
+ sooner).
+
+ Note that Pos plays a dual role. It is used as both a position
+ in the ring buffer and also as a tree node. m_ring_buffer[Pos]
+ defines a character that is used to identify a tree node.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::InsertNode( // no return value
+ short int Pos) // position in the buffer
+ throw() // exception list
+
+ {
+ short int i;
+ short int p;
+ int cmp;
+ unsigned char * key;
+
+ ASSERT(Pos >= 0);
+ ASSERT(Pos < N);
+
+ cmp = 1;
+ key = &(m_ring_buffer[Pos]);
+
+ // The last 256 entries in m_rson contain the root nodes for
+ // strings that begin with a letter. Get an index for the
+ // first letter in this string.
+
+ p = (short int) (N + 1 + key[0]);
+
+ // Set the left and right tree nodes for this position to "not
+ // used."
+
+ m_lson[Pos] = NOT_USED;
+ m_rson[Pos] = NOT_USED;
+
+ // Haven't matched anything yet.
+
+ m_match_length = 0;
+
+ for ( ; ; )
+ {
+ if (cmp >= 0)
+ {
+ if (m_rson[p] != NOT_USED)
+ {
+ p = m_rson[p];
+ }
+ else
+ {
+ m_rson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+ else
+ {
+ if (m_lson[p] != NOT_USED)
+ {
+ p = m_lson[p];
+ }
+ else
+ {
+ m_lson[p] = Pos;
+ m_dad[Pos] = p;
+ return;
+ }
+ }
+
+ // Should we go to the right or the left to look for the
+ // next match?
+
+ for (i = 1; i < F; i++)
+ {
+ cmp = key[i] - m_ring_buffer[p + i];
+ if (cmp != 0)
+ break;
+ }
+
+ if (i > m_match_length)
+ {
+ m_match_position = p;
+ m_match_length = i;
+
+ if (i >= F)
+ break;
+ }
+ }
+
+ m_dad[Pos] = m_dad[p];
+ m_lson[Pos] = m_lson[p];
+ m_rson[Pos] = m_rson[p];
+
+ m_dad[ m_lson[p] ] = Pos;
+ m_dad[ m_rson[p] ] = Pos;
+
+ if (m_rson[ m_dad[p] ] == p)
+ {
+ m_rson[ m_dad[p] ] = Pos;
+ }
+ else
+ {
+ m_lson[ m_dad[p] ] = Pos;
+ }
+
+ // Remove "p"
+
+ m_dad[p] = NOT_USED;
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::DeleteNode
+
+ This function removes the node "Node" from the tree.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::DeleteNode( // no return value
+ short int Node) // node to be removed
+ throw() // exception list
+
+ {
+ short int q;
+
+ ASSERT(Node >= 0);
+ ASSERT(Node < (N+1));
+
+ if (m_dad[Node] == NOT_USED)
+ {
+ // not in tree, nothing to do
+ return;
+ }
+
+ if (m_rson[Node] == NOT_USED)
+ {
+ q = m_lson[Node];
+ }
+ else if (m_lson[Node] == NOT_USED)
+ {
+ q = m_rson[Node];
+ }
+ else
+ {
+ q = m_lson[Node];
+ if (m_rson[q] != NOT_USED)
+ {
+ do
+ {
+ q = m_rson[q];
+ }
+ while (m_rson[q] != NOT_USED);
+
+ m_rson[ m_dad[q] ] = m_lson[q];
+ m_dad[ m_lson[q] ] = m_dad[q];
+ m_lson[q] = m_lson[Node];
+ m_dad[ m_lson[Node] ] = q;
+ }
+
+ m_rson[q] = m_rson[Node];
+ m_dad[ m_rson[Node] ] = q;
+ }
+
+ m_dad[q] = m_dad[Node];
+
+ if (m_rson[ m_dad[Node] ] == Node)
+ {
+ m_rson[ m_dad[Node] ] = q;
+ }
+ else
+ {
+ m_lson[ m_dad[Node] ] = q;
+ }
+
+ m_dad[Node] = NOT_USED;
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::Encode
+
+ This function "encodes" the input stream into the output stream.
+ The GetChars() and SendChars() functions are used to separate
+ this method from the actual i/o.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::Encode( // no return value
+ void) // no parameters
+
+ {
+ short int i; // an iterator
+ short int r; // node number in the binary tree
+ short int s; // position in the ring buffer
+ unsigned short int len; // len of initial string
+ short int last_match_length; // length of last match
+ short int code_buf_pos; // position in the output buffer
+ unsigned char code_buf[17]; // the output buffer
+ unsigned char mask; // bit mask for byte 0 of out buf
+ unsigned char c; // character read from string
+
+ // Start with a clean tree.
+
+ InitTree();
+
+ // code_buf[0] works as eight flags. A "1" represents that the
+ // unit is an unencoded letter (1 byte), and a "0" represents
+ // that the next unit is a <position,length> pair (2 bytes).
+ //
+ // code_buf[1..16] stores eight units of code. Since the best
+ // we can do is store eight <position,length> pairs, at most 16
+ // bytes are needed to store this.
+ //
+ // This is why the maximum size of the code buffer is 17 bytes.
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+
+ // Mask iterates over the 8 bits in the code buffer. The first
+ // character ends up being stored in the low bit.
+ //
+ // bit 8 7 6 5 4 3 2 1
+ // | |
+ // | first sequence in code buffer
+ // |
+ // last sequence in code buffer
+
+ mask = 1;
+
+ s = 0;
+ r = (short int) N - (short int) F;
+
+ // Initialize the ring buffer with spaces...
+
+ // Note that the last F bytes of the ring buffer are not filled.
+ // This is because those F bytes will be filled in immediately
+ // with bytes from the input stream.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ // Read F bytes into the last F bytes of the ring buffer.
+ //
+ // This function loads the buffer with X characters and returns
+ // the actual amount loaded.
+
+ len = GetChars(&(m_ring_buffer[r]), F);
+
+ // Make sure there is something to be compressed.
+
+ if (len == 0)
+ return;
+
+ // Insert the F strings, each of which begins with one or more
+ // 'space' characters. Note the order in which these strings
+ // are inserted. This way, degenerate trees will be less likely
+ // to occur.
+
+ for (i = 1; i <= F; i++)
+ {
+ InsertNode((short int) (r - i));
+ }
+
+ // Finally, insert the whole string just read. The
+ // member variables match_length and match_position are set.
+
+ InsertNode(r);
+
+ // Now that we're preloaded, continue till done.
+
+ do
+ {
+
+ // m_match_length may be spuriously long near the end of
+ // text.
+
+ if (m_match_length > len)
+ {
+ m_match_length = len;
+ }
+
+ // Is it cheaper to store this as a single character? If so,
+ // make it so.
+
+ if (m_match_length < THRESHOLD)
+ {
+ // Send one character. Remember that code_buf[0] is the
+ // set of flags for the next eight items.
+
+ m_match_length = 1;
+ code_buf[0] |= mask;
+ code_buf[code_buf_pos++] = m_ring_buffer[r];
+ }
+
+ // Otherwise, we do indeed have a string that can be stored
+ // compressed to save space.
+
+ else
+ {
+ // The next 16 bits need to contain the position (12 bits)
+ // and the length (4 bits).
+
+ code_buf[code_buf_pos++] = (unsigned char) m_match_position;
+ code_buf[code_buf_pos++] = (unsigned char) (
+ ((m_match_position >> 4) & 0xf0) |
+ (m_match_length - THRESHOLD) );
+ }
+
+ // Shift the mask one bit to the left so that it will be ready
+ // to store the new bit.
+
+ mask = (unsigned char) (mask << 1);
+
+ // If the mask is now 0, then we know that we have a full set
+ // of flags and items in the code buffer. These need to be
+ // output.
+
+ if (mask == 0)
+ {
+ // code_buf is the buffer of characters to be output.
+ // code_buf_pos is the number of characters it contains.
+
+ SendChars(code_buf, code_buf_pos);
+
+ // Reset for next buffer...
+
+ code_buf[0] = 0;
+ code_buf_pos = 1;
+ mask = 1;
+ }
+
+ last_match_length = m_match_length;
+
+ // Delete old strings and read new bytes...
+
+ for (i = 0; i < last_match_length; i++)
+ {
+
+ // Get next character...
+
+ if (GetChars(&c, 1) != 1)
+ break;
+
+ // Delete "old strings"
+
+ DeleteNode(s);
+
+ // Put this character into the ring buffer.
+ //
+ // The original comment here says "If the position is near
+ // the end of the buffer, extend the buffer to make
+ // string comparison easier."
+ //
+ // That's a little misleading, because the "end" of the
+ // buffer is really what we consider to be the "beginning"
+ // of the buffer, that is, positions 0 through F.
+ //
+ // The idea is that the front end of the buffer is duplicated
+ // into the back end so that when you're looking at characters
+ // at the back end of the buffer, you can index ahead (beyond
+ // the normal end of the buffer) and see the characters
+ // that are at the front end of the buffer wihtout having
+ // to adjust the index.
+ //
+ // That is...
+ //
+ // 1234xxxxxxxxxxxxxxxxxxxxxxxxxxxxx1234
+ // | | |
+ // position 0 end of buffer |
+ // |
+ // duplicate of front of buffer
+
+ m_ring_buffer[s] = c;
+
+ if (s < F - 1)
+ {
+ m_ring_buffer[s + N] = c;
+ }
+
+ // Increment the position, and wrap around when we're at
+ // the end. Note that this relies on N being a power of 2.
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Register the string that is found in
+ // m_ring_buffer[r..r+F-1].
+
+ InsertNode(r);
+ }
+
+ // If we didn't quit because we hit the last_match_length,
+ // then we must have quit because we ran out of characters
+ // to process.
+
+ while (i++ < last_match_length)
+ {
+ DeleteNode(s);
+
+ s = (short int) ( (s + 1) & (N - 1) );
+ r = (short int) ( (r + 1) & (N - 1) );
+
+ // Note that len hitting 0 is the key that causes the
+ // do...while() to terminate. This is the only place
+ // within the loop that len is modified.
+ //
+ // Its original value is F (or a number less than F for
+ // short strings).
+
+ if (--len)
+ {
+ InsertNode(r); /* buffer may not be empty. */
+ }
+ }
+
+ // End of do...while() loop. Continue processing until there
+ // are no more characters to be compressed. The variable
+ // "len" is used to signal this condition.
+ }
+ while (len > 0);
+
+ // There could still be something in the output buffer. Send it
+ // now.
+
+ if (code_buf_pos > 1)
+ {
+ // code_buf is the encoded string to send.
+ // code_buf_ptr is the number of characters.
+
+ SendChars(code_buf, code_buf_pos);
+ }
+
+ // Done!
+ }
+
+/*
+ -------------------------------------------------------------------------
+ cLZSS::Decode
+
+ This function "decodes" the input stream into the output stream.
+ The GetChars() and SendChars() functions are used to separate
+ this method from the actual i/o.
+ -------------------------------------------------------------------------
+*/
+
+void cLZSS::Decode( // no return value
+ void) // no parameters
+
+ {
+ int k;
+ int r; // node number
+ unsigned char c[F]; // an array of chars
+ unsigned char flags; // 8 bits of flags
+ int flag_count; // which flag we're on
+ short int pos; // position in the ring buffer
+ short int len; // number of chars in ring buffer
+
+ // Initialize the ring buffer with a common string.
+ //
+ // Note that the last F bytes of the ring buffer are not filled.
+
+ memset(m_ring_buffer, ' ', N - F);
+
+ r = N - F;
+
+ flags = (char) 0;
+ flag_count = 0;
+
+ for ( ; ; )
+ {
+
+ // If there are more bits of interest in this flag, then
+ // shift that next interesting bit into the 1's position.
+ //
+ // If this flag has been exhausted, the next byte must
+ // be a flag.
+
+ if (flag_count > 0)
+ {
+ flags = (unsigned char) (flags >> 1);
+ flag_count--;
+ }
+ else
+ {
+ // Next byte must be a flag.
+
+ if (GetChars(&flags, 1) != 1)
+ break;
+
+ // Set the flag counter. While at first it might appear
+ // that this should be an 8 since there are 8 bits in the
+ // flag, it should really be a 7 because the shift must
+ // be performed 7 times in order to see all 8 bits.
+
+ flag_count = 7;
+ }
+
+ // If the low order bit of the flag is now set, then we know
+ // that the next byte is a single, unencoded character.
+
+ if (flags & 1)
+ {
+ if (GetChars(c, 1) != 1)
+ break;
+
+ if (SendChars(c, 1) != 1)
+ break;
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[0];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Otherwise, we know that the next two bytes are a
+ // <position,length> pair. The position is in 12 bits and
+ // the length is in 4 bits.
+
+ else
+ {
+ // Original code:
+ // if ((i = getc(infile)) == EOF)
+ // break;
+ // if ((j = getc(infile)) == EOF)
+ // break;
+ // i |= ((j & 0xf0) << 4);
+ // j = (j & 0x0f) + THRESHOLD;
+ //
+ // I've modified this to only make one input call, and
+ // have changed the variable names to something more
+ // obvious.
+
+ if (GetChars(c, 2) != 2)
+ break;
+
+ // Convert these two characters into the position and
+ // length. Note that the length is always at least
+ // THRESHOLD, which is why we're able to get a length
+ // of 18 out of only 4 bits.
+
+ pos = (short int) ( c[0] | ((c[1] & 0xf0) << 4) );
+
+ len = (short int) ( (c[1] & 0x0f) + THRESHOLD );
+
+ // There are now "len" characters at position "pos" in
+ // the ring buffer that can be pulled out. Note that
+ // len is never more than F.
+
+ for (k = 0; k < len; k++)
+ {
+ c[k] = m_ring_buffer[(pos + k) & (N - 1)];
+
+ // Add to buffer, and increment to next spot. Wrap at end.
+
+ m_ring_buffer[r] = c[k];
+ r = (short int) ( (r + 1) & (N - 1) );
+ }
+
+ // Add the "len" characters to the output stream.
+
+ if (SendChars(c, len) != len)
+ break;
+ }
+ }
+ }
+
diff --git a/src/modules/common/zipcomprs.cpp b/src/modules/common/zipcomprs.cpp
new file mode 100644
index 0000000..21726bf
--- /dev/null
+++ b/src/modules/common/zipcomprs.cpp
@@ -0,0 +1,164 @@
+/******************************************************************************
+ * swcomprs.cpp - code for class 'ZipCompress'- a driver class that provides
+ * compression utilities. - using zlib
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <zipcomprs.h>
+#include <zlib.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * ZipCompress Constructor - Initializes data for instance of ZipCompress
+ *
+ */
+
+ZipCompress::ZipCompress() : SWCompress()
+{
+// fprintf(stderr, "init compress\n");
+}
+
+
+/******************************************************************************
+ * ZipCompress Destructor - Cleans up instance of ZipCompress
+ */
+
+ZipCompress::~ZipCompress() {
+}
+
+
+/******************************************************************************
+ * ZipCompress::Encode - This function "encodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ * NOTE: must set zlen for parent class to know length of
+ * compressed buffer.
+ */
+
+void ZipCompress::Encode(void)
+{
+/*
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+ direct = 0; // set direction needed by parent [Get|Send]Chars()
+
+ // get buffer
+ char chunk[1024];
+ char *buf = (char *)calloc(1, 1024);
+ char *chunkbuf = buf;
+ unsigned long chunklen;
+ unsigned long len = 0;
+ while((chunklen = GetChars(chunk, 1023))) {
+ memcpy(chunkbuf, chunk, chunklen);
+ len += chunklen;
+ if (chunklen < 1023)
+ break;
+ else buf = (char *)realloc(buf, len + 1024);
+ chunkbuf = buf+len;
+ }
+
+
+ zlen = (long) (len*1.001)+15;
+ char *zbuf = new char[zlen+1];
+ if (len)
+ {
+ //printf("Doing compress\n");
+ if (compress((Bytef*)zbuf, &zlen, (const Bytef*)buf, len) != Z_OK)
+ {
+ printf("ERROR in compression\n");
+ }
+ else {
+ SendChars(zbuf, zlen);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: no buffer to compress\n");
+ }
+ delete [] zbuf;
+ free (buf);
+}
+
+
+/******************************************************************************
+ * ZipCompress::Decode - This function "decodes" the input stream into the
+ * output stream.
+ * The GetChars() and SendChars() functions are
+ * used to separate this method from the actual
+ * i/o.
+ */
+
+void ZipCompress::Decode(void)
+{
+/*
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+ // get buffer
+ char chunk[1024];
+ char *zbuf = (char *)calloc(1, 1024);
+ char *chunkbuf = zbuf;
+ int chunklen;
+ unsigned long zlen = 0;
+ while((chunklen = GetChars(chunk, 1023))) {
+ memcpy(chunkbuf, chunk, chunklen);
+ zlen += chunklen;
+ if (chunklen < 1023)
+ break;
+ else zbuf = (char *)realloc(zbuf, zlen + 1024);
+ chunkbuf = zbuf + zlen;
+ }
+
+ //printf("Decoding complength{%ld} uncomp{%ld}\n", zlen, blen);
+ if (zlen) {
+ unsigned long blen = zlen*20; // trust compression is less than 1000%
+ char *buf = new char[blen];
+ //printf("Doing decompress {%s}\n", zbuf);
+ slen = 0;
+ switch (uncompress((Bytef*)buf, &blen, (Bytef*)zbuf, zlen)){
+ case Z_OK: SendChars(buf, blen); slen = blen; break;
+ case Z_MEM_ERROR: fprintf(stderr, "ERROR: not enough memory during decompression.\n"); break;
+ case Z_BUF_ERROR: fprintf(stderr, "ERROR: not enough room in the out buffer during decompression.\n"); break;
+ case Z_DATA_ERROR: fprintf(stderr, "ERROR: corrupt data during decompression.\n"); break;
+ default: fprintf(stderr, "ERROR: an unknown error occured during decompression.\n"); break;
+ }
+ delete [] buf;
+ }
+ else {
+ fprintf(stderr, "ERROR: no buffer to decompress!\n");
+ }
+ //printf("Finished decoding\n");
+ free (zbuf);
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/zstr.cpp b/src/modules/common/zstr.cpp
new file mode 100644
index 0000000..2539ff0
--- /dev/null
+++ b/src/modules/common/zstr.cpp
@@ -0,0 +1,731 @@
+/******************************************************************************
+ * zstr.cpp - code for class 'zStr'- a module that reads compressed text
+ * files and provides lookup and parsing functions based on
+ * class StrKey
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <utilstr.h>
+#include <zstr.h>
+#include <swcomprs.h>
+
+#include <sysdata.h>
+#include <entriesblk.h>
+#include <swlog.h>
+#include <stringmgr.h>
+#include <filemgr.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * zStr Statics
+ */
+
+int zStr::instance = 0;
+const int zStr::IDXENTRYSIZE = 8;
+const int zStr::ZDXENTRYSIZE = 8;
+
+
+/******************************************************************************
+ * zStr Constructor - Initializes data for instance of zStr
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ */
+
+zStr::zStr(const char *ipath, int fileMode, long blockCount, SWCompress *icomp)
+{
+ SWBuf buf;
+
+ nl = '\n';
+ lastoff = -1;
+ path = 0;
+ stdstr(&path, ipath);
+
+ compressor = (icomp) ? icomp : new SWCompress();
+ this->blockCount = blockCount;
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s.idx", path);
+ idxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s.dat", path);
+ datfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s.zdx", path);
+ zdxfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s.zdt", path);
+ zdtfd = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ if (datfd <= 0) {
+ SWLog::getSystemLog()->logError("%d", errno);
+ }
+
+ cacheBlock = 0;
+ cacheBlockIndex = -1;
+ cacheDirty = false;
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * zStr Destructor - Cleans up instance of zStr
+ */
+
+zStr::~zStr() {
+
+ flushCache();
+
+ if (path)
+ delete [] path;
+
+ --instance;
+
+ FileMgr::getSystemFileMgr()->close(idxfd);
+ FileMgr::getSystemFileMgr()->close(datfd);
+ FileMgr::getSystemFileMgr()->close(zdxfd);
+ FileMgr::getSystemFileMgr()->close(zdtfd);
+
+
+ if (compressor)
+ delete compressor;
+
+}
+
+
+/******************************************************************************
+ * zStr::getidxbufdat - Gets the index string at the given dat offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd and must
+ * be free'd by calling function
+ *
+ * ENT: ioffset - offset in dat file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void zStr::getKeyFromDatOffset(long ioffset, char **buf) {
+ int size;
+ char ch;
+ if (datfd > 0) {
+ datfd->seek(ioffset, SEEK_SET);
+ for (size = 0; datfd->read(&ch, 1) == 1; size++) {
+ if ((ch == '\\') || (ch == 10) || (ch == 13))
+ break;
+ }
+ *buf = (*buf) ? (char *)realloc(*buf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ if (size) {
+ datfd->seek(ioffset, SEEK_SET);
+ datfd->read(*buf, size);
+ }
+ (*buf)[size] = 0;
+ toupperstr_utf8(*buf, size*2);
+ }
+ else {
+ *buf = (*buf) ? (char *)realloc(*buf, 1) : (char *)malloc(1);
+ **buf = 0;
+ }
+}
+
+
+/******************************************************************************
+ * zStr::getidxbuf - Gets the index string at the given idx offset
+ * NOTE: buf is calloc'd, or if not null, realloc'd
+ * and must be freed by calling function
+ *
+ * ENT: ioffset - offset in idx file to lookup
+ * buf - address of pointer to allocate for storage of string
+ */
+
+void zStr::getKeyFromIdxOffset(long ioffset, char **buf) {
+ __u32 offset;
+
+ if (idxfd > 0) {
+ idxfd->seek(ioffset, SEEK_SET);
+ idxfd->read(&offset, sizeof(__u32));
+ offset = swordtoarch32(offset);
+ getKeyFromDatOffset(offset, buf);
+ }
+}
+
+
+/******************************************************************************
+ * zStr::findoffset - Finds the offset of the key string from the indexes
+ *
+ * ENT: key - key string to lookup
+ * offset - address to store the starting offset
+ * size - address to store the size of the entry
+ * away - number of entries before of after to jump
+ * (default = 0)
+ *
+ * RET: error status
+ */
+
+signed char zStr::findKeyIndex(const char *ikey, long *idxoff, long away) {
+ char *maxbuf = 0, *trybuf = 0, *key = 0, quitflag = 0;
+ signed char retval = 0;
+ __s32 headoff, tailoff, tryoff = 0, maxoff = 0;
+ __u32 start, size;
+ int diff = 0;
+
+ if (idxfd->getFd() >= 0) {
+ tailoff = maxoff = idxfd->seek(0, SEEK_END) - IDXENTRYSIZE;
+ if (*ikey) {
+ headoff = 0;
+ stdstr(&key, ikey, 3);
+ toupperstr_utf8(key, strlen(key)*3);
+
+ int keylen = strlen(key);
+ bool substr = false;
+
+ getKeyFromIdxOffset(maxoff, &maxbuf);
+
+ while (headoff < tailoff) {
+ tryoff = (lastoff == -1) ? headoff + (((((tailoff / IDXENTRYSIZE) - (headoff / IDXENTRYSIZE))) / 2) * IDXENTRYSIZE) : lastoff;
+ lastoff = -1;
+
+ getKeyFromIdxOffset(tryoff, &trybuf);
+
+ if (!*trybuf && tryoff) { // In case of extra entry at end of idx (not first entry)
+ tryoff += (tryoff > (maxoff / 2))?-IDXENTRYSIZE:IDXENTRYSIZE;
+ retval = -1;
+ break;
+ }
+
+ diff = strcmp(key, trybuf);
+
+ if (!diff)
+ break;
+
+ if (!strncmp(trybuf, key, keylen)) substr = true;
+
+ if (diff < 0)
+ tailoff = (tryoff == headoff) ? headoff : tryoff;
+ else headoff = tryoff;
+
+ if (tailoff == headoff + IDXENTRYSIZE) {
+ if (quitflag++)
+ headoff = tailoff;
+ }
+ }
+
+ // didn't find exact match
+ if (headoff >= tailoff) {
+ tryoff = headoff;
+ if (!substr && ((tryoff != maxoff)||(strncmp(key, maxbuf, keylen)<0))) {
+ away--; // if our entry doesn't startwith our key, prefer the previous entry over the next
+ }
+ }
+ if (trybuf)
+ free(trybuf);
+ delete [] key;
+ if (maxbuf)
+ free(maxbuf);
+ }
+ else { tryoff = 0; }
+
+ idxfd->seek(tryoff, SEEK_SET);
+
+ start = size = 0;
+ retval = (idxfd->read(&start, sizeof(__u32))==sizeof(__u32)) ? retval : -1;
+ retval = (idxfd->read(&size, sizeof(__u32))==sizeof(__u32)) ? retval : -1;
+ start = swordtoarch32(start);
+ size = swordtoarch32(size);
+
+ if (idxoff)
+ *idxoff = tryoff;
+
+ while (away) {
+ __u32 laststart = start;
+ __u32 lastsize = size;
+ __s32 lasttry = tryoff;
+ tryoff += (away > 0) ? IDXENTRYSIZE : -IDXENTRYSIZE;
+
+ bool bad = false;
+ if (((long)(tryoff + (away*IDXENTRYSIZE)) < -IDXENTRYSIZE) || (tryoff + (away*IDXENTRYSIZE) > (maxoff+IDXENTRYSIZE)))
+ bad = true;
+ else if (idxfd->seek(tryoff, SEEK_SET) < 0)
+ bad = true;
+ if (bad) {
+ retval = -1;
+ start = laststart;
+ size = lastsize;
+ tryoff = lasttry;
+ if (idxoff)
+ *idxoff = tryoff;
+ break;
+ }
+ idxfd->read(&start, sizeof(__u32));
+ idxfd->read(&size, sizeof(__u32));
+ start = swordtoarch32(start);
+ size = swordtoarch32(size);
+
+ if (idxoff)
+ *idxoff = tryoff;
+
+
+ if (((laststart != start) || (lastsize != size)) && (start >= 0) && (size))
+ away += (away < 0) ? 1 : -1;
+ }
+
+ lastoff = tryoff;
+ }
+ else {
+ if (idxoff)
+ *idxoff = 0;
+ retval = -1;
+ }
+ return retval;
+}
+
+
+/******************************************************************************
+ * zStr::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void zStr::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+/******************************************************************************
+ * zStr::getText - gets text at a given offset
+ *
+ * ENT:
+ * offset - idxoffset where the key is located.
+ * buf - buffer to store text
+ * idxbuf - buffer to store index key
+ * NOTE: buffer will be alloc'd / realloc'd and
+ * should be free'd by the client
+ *
+ */
+
+void zStr::getText(long offset, char **idxbuf, char **buf) {
+ char *ch;
+ char *idxbuflocal = 0;
+ getKeyFromIdxOffset(offset, &idxbuflocal);
+ __u32 start;
+ __u32 size;
+
+ do {
+ idxfd->seek(offset, SEEK_SET);
+ idxfd->read(&start, sizeof(__u32));
+ idxfd->read(&size, sizeof(__u32));
+ start = swordtoarch32(start);
+ size = swordtoarch32(size);
+
+ *buf = (*buf) ? (char *)realloc(*buf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ *idxbuf = (*idxbuf) ? (char *)realloc(*idxbuf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ memset(*buf, 0, size + 1);
+ memset(*idxbuf, 0, size + 1);
+ datfd->seek(start, SEEK_SET);
+ datfd->read(*buf, (int)(size));
+
+ for (ch = *buf; *ch; ch++) { // skip over index string
+ if (*ch == 10) {
+ ch++;
+ break;
+ }
+ }
+ memmove(*buf, ch, size - (unsigned long)(ch-*buf));
+
+ // resolve link
+ if (!strncmp(*buf, "@LINK", 5)) {
+ for (ch = *buf; *ch; ch++) { // null before nl
+ if (*ch == 10) {
+ *ch = 0;
+ break;
+ }
+ }
+ findKeyIndex(*buf + 6, &offset);
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+
+ if (idxbuflocal) {
+ __u32 localsize = strlen(idxbuflocal);
+ localsize = (localsize < (size - 1)) ? localsize : (size - 1);
+ strncpy(*idxbuf, idxbuflocal, localsize);
+ (*idxbuf)[localsize] = 0;
+ free(idxbuflocal);
+ }
+ __u32 block = 0;
+ __u32 entry = 0;
+ memmove(&block, *buf, sizeof(__u32));
+ memmove(&entry, *buf + sizeof(__u32), sizeof(__u32));
+ block = swordtoarch32(block);
+ entry = swordtoarch32(entry);
+ getCompressedText(block, entry, buf);
+}
+
+
+/******************************************************************************
+ * zStr::getCompressedText - Get text entry from a compressed index / zdata
+ * file.
+ */
+
+void zStr::getCompressedText(long block, long entry, char **buf) {
+
+ __u32 size = 0;
+
+ if (cacheBlockIndex != block) {
+ __u32 start = 0;
+
+ zdxfd->seek(block * ZDXENTRYSIZE, SEEK_SET);
+ zdxfd->read(&start, sizeof(__u32));
+ zdxfd->read(&size, sizeof(__u32));
+ start = swordtoarch32(start);
+ size = swordtoarch32(size);
+
+ SWBuf buf;
+ buf.setSize(size + 5);
+ zdtfd->seek(start, SEEK_SET);
+ zdtfd->read(buf.getRawData(), size);
+
+ flushCache();
+
+ unsigned long len = size;
+ buf.setSize(size);
+ rawZFilter(buf, 0); // 0 = decipher
+
+ compressor->zBuf(&len, buf.getRawData());
+ char *rawBuf = compressor->Buf(0, &len);
+ cacheBlock = new EntriesBlock(rawBuf, len);
+ cacheBlockIndex = block;
+ }
+ size = cacheBlock->getEntrySize(entry);
+ *buf = (*buf) ? (char *)realloc(*buf, size*2 + 1) : (char *)malloc(size*2 + 1);
+ strcpy(*buf, cacheBlock->getEntry(entry));
+}
+
+
+/******************************************************************************
+ * zLD::settext - Sets text for current offset
+ *
+ * ENT: key - key for this entry
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void zStr::setText(const char *ikey, const char *buf, long len) {
+
+ __u32 start, outstart;
+ __u32 size, outsize;
+ __s32 endoff;
+ long idxoff = 0;
+ __s32 shiftSize;
+ static const char nl[] = {13, 10};
+ char *tmpbuf = 0;
+ char *key = 0;
+ char *dbKey = 0;
+ char *idxBytes = 0;
+ char *outbuf = 0;
+ char *ch = 0;
+
+ len = (len < 0) ? strlen(buf) : len;
+ stdstr(&key, ikey, 3);
+ toupperstr_utf8(key, strlen(key)*3);
+
+ char notFound = findKeyIndex(ikey, &idxoff, 0);
+ if (!notFound) {
+ getKeyFromIdxOffset(idxoff, &dbKey);
+ int diff = strcmp(key, dbKey);
+ if (diff < 0) {
+ }
+ else if (diff > 0) {
+ idxoff += IDXENTRYSIZE;
+ }
+ else if ((!diff) && (len > 0 /*we're not deleting*/)) { // got absolute entry
+ do {
+ idxfd->seek(idxoff, SEEK_SET);
+ idxfd->read(&start, sizeof(__u32));
+ idxfd->read(&size, sizeof(__u32));
+ start = swordtoarch32(start);
+ size = swordtoarch32(size);
+
+ tmpbuf = new char [ size + 2 ];
+ memset(tmpbuf, 0, size + 2);
+ datfd->seek(start, SEEK_SET);
+ datfd->read(tmpbuf, size);
+
+ for (ch = tmpbuf; *ch; ch++) { // skip over index string
+ if (*ch == 10) {
+ ch++;
+ break;
+ }
+ }
+ memmove(tmpbuf, ch, size - (unsigned long)(ch-tmpbuf));
+
+ // resolve link
+ if (!strncmp(tmpbuf, "@LINK", 5) && (len)) {
+ for (ch = tmpbuf; *ch; ch++) { // null before nl
+ if (*ch == 10) {
+ *ch = 0;
+ break;
+ }
+ }
+ findKeyIndex(tmpbuf + IDXENTRYSIZE, &idxoff);
+ delete [] tmpbuf;
+ }
+ else break;
+ }
+ while (true); // while we're resolving links
+ }
+ }
+
+ endoff = idxfd->seek(0, SEEK_END);
+
+ shiftSize = endoff - idxoff;
+
+ if (shiftSize > 0) {
+ idxBytes = new char [ shiftSize ];
+ idxfd->seek(idxoff, SEEK_SET);
+ idxfd->read(idxBytes, shiftSize);
+ }
+
+ outbuf = new char [ len + strlen(key) + 5 ];
+ sprintf(outbuf, "%s%c%c", key, 13, 10);
+ size = strlen(outbuf);
+ if (len > 0) { // NOT a link
+ if (!cacheBlock) {
+ flushCache();
+ cacheBlock = new EntriesBlock();
+ cacheBlockIndex = (zdxfd->seek(0, SEEK_END) / ZDXENTRYSIZE);
+ }
+ else if (cacheBlock->getCount() >= blockCount) {
+ flushCache();
+ cacheBlock = new EntriesBlock();
+ cacheBlockIndex = (zdxfd->seek(0, SEEK_END) / ZDXENTRYSIZE);
+ }
+ __u32 entry = cacheBlock->addEntry(buf);
+ cacheDirty = true;
+ outstart = archtosword32(cacheBlockIndex);
+ outsize = archtosword32(entry);
+ memcpy (outbuf + size, &outstart, sizeof(__u32));
+ memcpy (outbuf + size + sizeof(__u32), &outsize, sizeof(__u32));
+ size += (sizeof(__u32) * 2);
+ }
+ else { // link
+ memcpy(outbuf + size, buf, len);
+ size += len;
+ }
+
+ start = datfd->seek(0, SEEK_END);
+
+ outstart = archtosword32(start);
+ outsize = archtosword32(size);
+
+ idxfd->seek(idxoff, SEEK_SET);
+ if (len > 0) {
+ datfd->seek(start, SEEK_SET);
+ datfd->write(outbuf, size);
+
+ // add a new line to make data file easier to read in an editor
+ datfd->write(&nl, 2);
+
+ idxfd->write(&outstart, sizeof(__u32));
+ idxfd->write(&outsize, sizeof(__u32));
+ if (idxBytes) {
+ idxfd->write(idxBytes, shiftSize);
+ }
+ }
+ else { // delete entry
+ if (idxBytes) {
+ idxfd->write(idxBytes+IDXENTRYSIZE, shiftSize-IDXENTRYSIZE);
+ idxfd->seek(-1, SEEK_CUR); // last valid byte
+ FileMgr::getSystemFileMgr()->trunc(idxfd); // truncate index
+ }
+ }
+
+ if (idxBytes)
+ delete [] idxBytes;
+ delete [] key;
+ delete [] outbuf;
+ free(dbKey);
+}
+
+
+/******************************************************************************
+ * zLD::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void zStr::linkEntry(const char *destkey, const char *srckey) {
+ char *text = new char [ strlen(destkey) + 7 ];
+ sprintf(text, "@LINK %s", destkey);
+ setText(srckey, text);
+ delete [] text;
+}
+
+
+void zStr::flushCache() {
+ if (cacheBlock) {
+ if (cacheDirty) {
+ __u32 start = 0;
+ unsigned long size = 0;
+ __u32 outstart = 0, outsize = 0;
+
+ const char *rawBuf = cacheBlock->getRawData(&size);
+ compressor->Buf(rawBuf, &size);
+ compressor->zBuf(&size);
+
+ SWBuf buf;
+ buf.setSize(size + 5);
+ memcpy(buf.getRawData(), compressor->zBuf(&size), size); // 1 = encipher
+ buf.setSize(size);
+ rawZFilter(buf, 1); // 1 = encipher
+
+ long zdxSize = zdxfd->seek(0, SEEK_END);
+ unsigned long zdtSize = zdtfd->seek(0, SEEK_END);
+
+ if ((cacheBlockIndex * ZDXENTRYSIZE) > (zdxSize - ZDXENTRYSIZE)) { // New Block
+ start = zdtSize;
+ }
+ else {
+ zdxfd->seek(cacheBlockIndex * ZDXENTRYSIZE, SEEK_SET);
+ zdxfd->read(&start, sizeof(__u32));
+ zdxfd->read(&outsize, sizeof(__u32));
+ start = swordtoarch32(start);
+ outsize = swordtoarch32(outsize);
+ if (start + outsize >= zdtSize) { // last entry, just overwrite
+ // start is already set
+ }
+ else if (size < outsize) { // middle entry, but smaller, that's fine and let's preserve bigger size
+ size = outsize;
+ }
+ else { // middle and bigger-- we have serious problems, for now let's put it at the end = lots of wasted space
+ start = zdtSize;
+ }
+ }
+
+
+
+ outstart = archtosword32(start);
+ outsize = archtosword32((__u32)size);
+
+ zdxfd->seek(cacheBlockIndex * ZDXENTRYSIZE, SEEK_SET);
+ zdtfd->seek(start, SEEK_SET);
+ zdtfd->write(buf, size);
+
+ // add a new line to make data file easier to read in an editor
+ zdtfd->write(&nl, 2);
+
+ zdxfd->write(&outstart, sizeof(__u32));
+ zdxfd->write(&outsize, sizeof(__u32));
+ }
+ delete cacheBlock;
+ cacheBlock = 0;
+ }
+ cacheBlockIndex = -1;
+ cacheDirty = false;
+}
+
+
+/******************************************************************************
+ * zLD::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+signed char zStr::createModule(const char *ipath) {
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s.dat", path);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s.idx", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ sprintf(buf, "%s.zdt", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ sprintf(buf, "%s.zdx", path);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/common/zverse.cpp b/src/modules/common/zverse.cpp
new file mode 100644
index 0000000..3f91918
--- /dev/null
+++ b/src/modules/common/zverse.cpp
@@ -0,0 +1,538 @@
+/******************************************************************************
+ * zverse.h - code for class 'zVerse'- a module that reads raw text
+ * files: ot and nt using indexs ??.bks ??.cps ??.vss
+ * and provides lookup and parsing functions based on
+ * class VerseKey for compressed modules
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <utilstr.h>
+#include <versekey.h>
+#include <zverse.h>
+#include <sysdata.h>
+#include <swbuf.h>
+#include <filemgr.h>
+#include <swcomprs.h>
+
+
+SWORD_NAMESPACE_START
+
+/******************************************************************************
+ * zVerse Statics
+ */
+
+int zVerse::instance = 0;
+
+const char zVerse::uniqueIndexID[] = {'X', 'r', 'v', 'c', 'b'};
+
+/******************************************************************************
+ * zVerse Constructor - Initializes data for instance of zVerse
+ *
+ * ENT: ipath - path of the directory where data and index files are located.
+ * be sure to include the trailing separator (e.g. '/' or '\')
+ * (e.g. 'modules/texts/rawtext/webster/')
+ * fileMode - open mode for the files (FileMgr::RDONLY, etc.)
+ * blockType - verse, chapter, book, etc.
+ */
+
+zVerse::zVerse(const char *ipath, int fileMode, int blockType, SWCompress *icomp)
+{
+ // this line, instead of just defaulting, to keep FileMgr out of header
+ if (fileMode == -1) fileMode = FileMgr::RDONLY;
+
+ SWBuf buf;
+
+ nl = '\n';
+ path = 0;
+ cacheBufIdx = -1;
+ cacheTestament = 0;
+ cacheBuf = 0;
+ dirtyCache = false;
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ compressor = (icomp) ? icomp : new SWCompress();
+
+ if (fileMode == -1) { // try read/write if possible
+ fileMode = FileMgr::RDWR;
+ }
+
+ buf.setFormatted("%s/ot.%czs", path, uniqueIndexID[blockType]);
+ idxfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt.%czs", path, uniqueIndexID[blockType]);
+ idxfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/ot.%czz", path, uniqueIndexID[blockType]);
+ textfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt.%czz", path, uniqueIndexID[blockType]);
+ textfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/ot.%czv", path, uniqueIndexID[blockType]);
+ compfp[0] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ buf.setFormatted("%s/nt.%czv", path, uniqueIndexID[blockType]);
+ compfp[1] = FileMgr::getSystemFileMgr()->open(buf, fileMode, true);
+
+ instance++;
+}
+
+
+/******************************************************************************
+ * zVerse Destructor - Cleans up instance of zVerse
+ */
+
+zVerse::~zVerse()
+{
+ int loop1;
+
+ if (cacheBuf) {
+ flushCache();
+ free(cacheBuf);
+ }
+
+ if (path)
+ delete [] path;
+
+ if (compressor)
+ delete compressor;
+
+ --instance;
+
+ for (loop1 = 0; loop1 < 2; loop1++) {
+ FileMgr::getSystemFileMgr()->close(idxfp[loop1]);
+ FileMgr::getSystemFileMgr()->close(textfp[loop1]);
+ FileMgr::getSystemFileMgr()->close(compfp[loop1]);
+ }
+}
+
+
+/******************************************************************************
+ * zVerse::findoffset - Finds the offset of the key verse from the indexes
+ *
+ *
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * book - book to find (0 - testament introduction)
+ * chapter - chapter to find (0 - book introduction)
+ * verse - verse to find (0 - chapter introduction)
+ * start - address to store the starting offset
+ * size - address to store the size of the entry
+ */
+
+void zVerse::findOffset(char testmt, long idxoff, long *start, unsigned short *size)
+{
+ // set start to offset in
+ // set size to
+ // set
+ unsigned long ulBuffNum=0; // buffer number
+ unsigned long ulVerseStart=0; // verse offset within buffer
+ unsigned short usVerseSize=0; // verse size
+ unsigned long ulCompOffset=0; // compressed buffer start
+ unsigned long ulCompSize=0; // buffer size compressed
+ unsigned long ulUnCompSize=0; // buffer size uncompressed
+
+ *start = *size = 0;
+ //printf ("Finding offset %ld\n", idxoff);
+ idxoff *= 10;
+ if (!testmt) {
+ testmt = ((idxfp[0]) ? 1:2);
+ }
+
+ // assert we have and valid file descriptor
+ if (compfp[testmt-1]->getFd() < 1)
+ return;
+
+ long newOffset = compfp[testmt-1]->seek(idxoff, SEEK_SET);
+ if (newOffset == idxoff) {
+ if (compfp[testmt-1]->read(&ulBuffNum, 4) != 4) {
+ printf ("Error reading ulBuffNum\n");
+ return;
+ }
+ }
+ else return;
+
+ ulBuffNum = swordtoarch32(ulBuffNum);
+
+ if (compfp[testmt-1]->read(&ulVerseStart, 4) < 2)
+ {
+ printf ("Error reading ulVerseStart\n");
+ return;
+ }
+ if (compfp[testmt-1]->read(&usVerseSize, 2) < 2)
+ {
+ printf ("Error reading usVerseSize\n");
+ return;
+ }
+
+ *start = swordtoarch32(ulVerseStart);
+ *size = swordtoarch16(usVerseSize);
+
+ if (*size) {
+ if (((long) ulBuffNum == cacheBufIdx) && (testmt == cacheTestament) && (cacheBuf)) {
+ // have the text buffered
+ return;
+ }
+
+ //printf ("Got buffer number{%ld} versestart{%ld} versesize{%d}\n", ulBuffNum, ulVerseStart, usVerseSize);
+
+
+ if (idxfp[testmt-1]->seek(ulBuffNum*12, SEEK_SET)!=(long) ulBuffNum*12)
+ {
+ printf ("Error seeking compressed file index\n");
+ return;
+ }
+ if (idxfp[testmt-1]->read(&ulCompOffset, 4)<4)
+ {
+ printf ("Error reading ulCompOffset\n");
+ return;
+ }
+ if (idxfp[testmt-1]->read(&ulCompSize, 4)<4)
+ {
+ printf ("Error reading ulCompSize\n");
+ return;
+ }
+ if (idxfp[testmt-1]->read(&ulUnCompSize, 4)<4)
+ {
+ printf ("Error reading ulUnCompSize\n");
+ return;
+ }
+
+ ulCompOffset = swordtoarch32(ulCompOffset);
+ ulCompSize = swordtoarch32(ulCompSize);
+ ulUnCompSize = swordtoarch32(ulUnCompSize);
+
+ if (textfp[testmt-1]->seek(ulCompOffset, SEEK_SET)!=(long)ulCompOffset)
+ {
+ printf ("Error: could not seek to right place in compressed text\n");
+ return;
+ }
+ SWBuf pcCompText;
+ pcCompText.setSize(ulCompSize+5);
+
+ if (textfp[testmt-1]->read(pcCompText.getRawData(), ulCompSize)<(long)ulCompSize) {
+ printf ("Error reading compressed text\n");
+ return;
+ }
+ pcCompText.setSize(ulCompSize);
+ rawZFilter(pcCompText, 0); // 0 = decipher
+
+ compressor->zBuf(&ulCompSize, pcCompText.getRawData());
+
+ if (cacheBuf) {
+ flushCache();
+ free(cacheBuf);
+ }
+
+ unsigned long len = 0;
+ compressor->Buf(0, &len);
+ cacheBuf = (char *)calloc(len + 1, 1);
+ memcpy(cacheBuf, compressor->Buf(), len);
+
+ cacheTestament = testmt;
+ cacheBufIdx = ulBuffNum;
+ }
+}
+
+
+/******************************************************************************
+ * zVerse::zreadtext - gets text at a given offset
+ *
+ * ENT: testmt - testament file to search in (0 - Old; 1 - New)
+ * start - starting offset where the text is located in the file
+ * size - size of text entry + 1 (null)
+ * buf - buffer to store text
+ *
+ */
+
+void zVerse::zReadText(char testmt, long start, unsigned short size, SWBuf &inBuf) {
+ inBuf = "";
+ if ( (size > 0) && cacheBuf && ((unsigned)start < strlen(cacheBuf)) ){ //TODO: optimize this, remove strlen
+ inBuf.setFillByte(0);
+ inBuf.setSize(size+1);
+ strncpy(inBuf.getRawData(), &(cacheBuf[start]), size);
+ inBuf.setSize(strlen(inBuf.c_str()));
+ }
+}
+
+
+/******************************************************************************
+ * zVerse::settext - Sets text for current offset
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * idxoff - offset into .vss
+ * buf - buffer to store
+ * len - length of buffer (0 - null terminated)
+ */
+
+void zVerse::doSetText(char testmt, long idxoff, const char *buf, long len) {
+
+ len = (len < 0) ? strlen(buf) : len;
+ if (!testmt)
+ testmt = ((idxfp[0]) ? 1:2);
+ if ((!dirtyCache) || (cacheBufIdx < 0)) {
+ cacheBufIdx = idxfp[testmt-1]->seek(0, SEEK_END) / 12;
+ cacheTestament = testmt;
+ if (cacheBuf)
+ free(cacheBuf);
+ cacheBuf = (char *)calloc(len + 1, 1);
+ }
+ else cacheBuf = (char *)((cacheBuf)?realloc(cacheBuf, strlen(cacheBuf)+(len + 1)):calloc((len + 1), 1));
+
+ dirtyCache = true;
+
+ unsigned long start, outstart;
+ unsigned long outBufIdx = cacheBufIdx;
+ unsigned short size;
+ unsigned short outsize;
+
+ idxoff *= 10;
+ size = outsize = len;
+
+ start = strlen(cacheBuf);
+
+ if (!size)
+ start = outBufIdx = 0;
+
+ outBufIdx = archtosword32(outBufIdx);
+ outstart = archtosword32(start);
+ outsize = archtosword16(size);
+
+ compfp[testmt-1]->seek(idxoff, SEEK_SET);
+ compfp[testmt-1]->write(&outBufIdx, 4);
+ compfp[testmt-1]->write(&outstart, 4);
+ compfp[testmt-1]->write(&outsize, 2);
+ strcat(cacheBuf, buf);
+}
+
+
+void zVerse::flushCache() {
+ if (dirtyCache) {
+ unsigned long idxoff;
+ unsigned long start, outstart;
+ unsigned long size, outsize;
+ unsigned long zsize, outzsize;
+
+ idxoff = cacheBufIdx * 12;
+ if (cacheBuf) {
+ size = outsize = zsize = outzsize = strlen(cacheBuf);
+ if (size) {
+ // if (compressor) {
+ // delete compressor;
+ // compressor = new LZSSCompress();
+ // }
+ compressor->Buf(cacheBuf);
+ compressor->zBuf(&zsize);
+ outzsize = zsize;
+
+ SWBuf buf;
+ buf.setSize(zsize + 5);
+ memcpy(buf.getRawData(), compressor->zBuf(&zsize), zsize);
+ buf.setSize(zsize);
+ rawZFilter(buf, 1); // 1 = encipher
+
+ start = outstart = textfp[cacheTestament-1]->seek(0, SEEK_END);
+
+ outstart = archtosword32(start);
+ outsize = archtosword32(size);
+ outzsize = archtosword32(zsize);
+
+ textfp[cacheTestament-1]->write(buf, zsize);
+
+ idxfp[cacheTestament-1]->seek(idxoff, SEEK_SET);
+ idxfp[cacheTestament-1]->write(&outstart, 4);
+ idxfp[cacheTestament-1]->write(&outzsize, 4);
+ idxfp[cacheTestament-1]->write(&outsize, 4);
+ }
+ free(cacheBuf);
+ cacheBuf = 0;
+ }
+ dirtyCache = false;
+ }
+}
+
+/******************************************************************************
+ * RawVerse::linkentry - links one entry to another
+ *
+ * ENT: testmt - testament to find (0 - Bible/module introduction)
+ * destidxoff - dest offset into .vss
+ * srcidxoff - source offset into .vss
+ */
+
+void zVerse::doLinkEntry(char testmt, long destidxoff, long srcidxoff) {
+ long bufidx;
+ long start;
+ unsigned short size;
+
+ destidxoff *= 10;
+ srcidxoff *= 10;
+
+ if (!testmt)
+ testmt = ((idxfp[1]) ? 1:2);
+
+ // get source
+ compfp[testmt-1]->seek(srcidxoff, SEEK_SET);
+ compfp[testmt-1]->read(&bufidx, 4);
+ compfp[testmt-1]->read(&start, 4);
+ compfp[testmt-1]->read(&size, 2);
+
+ // write dest
+ compfp[testmt-1]->seek(destidxoff, SEEK_SET);
+ compfp[testmt-1]->write(&bufidx, 4);
+ compfp[testmt-1]->write(&start, 4);
+ compfp[testmt-1]->write(&size, 2);
+}
+
+
+/******************************************************************************
+ * RawVerse::CreateModule - Creates new module files
+ *
+ * ENT: path - directory to store module files
+ * RET: error status
+ */
+
+char zVerse::createModule(const char *ipath, int blockBound)
+{
+ char *path = 0;
+ char *buf = new char [ strlen (ipath) + 20 ];
+ FileDesc *fd, *fd2;
+
+ stdstr(&path, ipath);
+
+ if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\'))
+ path[strlen(path)-1] = 0;
+
+ sprintf(buf, "%s/ot.%czs", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/nt.%czs", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/ot.%czz", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/nt.%czz", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+ FileMgr::getSystemFileMgr()->close(fd);
+
+ sprintf(buf, "%s/ot.%czv", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd->getFd();
+
+ sprintf(buf, "%s/nt.%czv", path, uniqueIndexID[blockBound]);
+ FileMgr::removeFile(buf);
+ fd2 = FileMgr::getSystemFileMgr()->open(buf, FileMgr::CREAT|FileMgr::WRONLY, FileMgr::IREAD|FileMgr::IWRITE);
+ fd2->getFd();
+
+ VerseKey vk;
+ vk.Headings(1);
+ long offset = 0;
+ short size = 0;
+ for (vk = TOP; !vk.Error(); vk++) {
+ if (vk.Testament() == 1) {
+ fd->write(&offset, 4); //compBufIdxOffset
+ fd->write(&offset, 4);
+ fd->write(&size, 2);
+ }
+ else {
+ fd2->write(&offset, 4); //compBufIdxOffset
+ fd2->write(&offset, 4);
+ fd2->write(&size, 2);
+ }
+ }
+
+ FileMgr::getSystemFileMgr()->close(fd);
+ FileMgr::getSystemFileMgr()->close(fd2);
+
+ delete [] path;
+ delete [] buf;
+/*
+ RawVerse rv(path);
+ VerseKey mykey("Rev 22:21");
+*/
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * zVerse::preptext - Prepares the text before returning it to external
+ * objects
+ *
+ * ENT: buf - buffer where text is stored and where to store the prep'd
+ * text.
+ */
+
+void zVerse::prepText(SWBuf &buf) {
+ unsigned int to, from;
+ char space = 0, cr = 0, realdata = 0, nlcnt = 0;
+ char *rawBuf = buf.getRawData();
+ for (to = from = 0; rawBuf[from]; from++) {
+ switch (rawBuf[from]) {
+ case 10:
+ if (!realdata)
+ continue;
+ space = (cr) ? 0 : 1;
+ cr = 0;
+ nlcnt++;
+ if (nlcnt > 1) {
+// *to++ = nl;
+ rawBuf[to++] = 10;
+// *to++ = nl[1];
+// nlcnt = 0;
+ }
+ continue;
+ case 13:
+ if (!realdata)
+ continue;
+// *to++ = nl[0];
+ rawBuf[to++] = 10;
+ space = 0;
+ cr = 1;
+ continue;
+ }
+ realdata = 1;
+ nlcnt = 0;
+ if (space) {
+ space = 0;
+ if (rawBuf[from] != ' ') {
+ rawBuf[to++] = ' ';
+ from--;
+ continue;
+ }
+ }
+ rawBuf[to++] = rawBuf[from];
+ }
+ buf.setSize(to);
+
+ while (to > 1) { // remove trailing excess
+ to--;
+ if ((rawBuf[to] == 10) || (rawBuf[to] == ' '))
+ buf.setSize(to);
+ else break;
+ }
+}
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/Makefile b/src/modules/filters/Makefile
new file mode 100644
index 0000000..1a2d00d
--- /dev/null
+++ b/src/modules/filters/Makefile
@@ -0,0 +1,5 @@
+
+root := ../../..
+
+all:
+ make -C ${root}
diff --git a/src/modules/filters/Makefile.am b/src/modules/filters/Makefile.am
new file mode 100644
index 0000000..7092c73
--- /dev/null
+++ b/src/modules/filters/Makefile.am
@@ -0,0 +1,105 @@
+filtersdir = $(top_srcdir)/src/modules/filters
+
+libsword_la_SOURCES += $(filtersdir)/swbasicfilter.cpp
+libsword_la_SOURCES += $(filtersdir)/swoptfilter.cpp
+
+GBFFIL = $(filtersdir)/gbfhtml.cpp
+GBFFIL += $(filtersdir)/gbfhtmlhref.cpp
+GBFFIL += $(filtersdir)/gbfwebif.cpp
+GBFFIL += $(filtersdir)/gbfplain.cpp
+GBFFIL += $(filtersdir)/gbfrtf.cpp
+GBFFIL += $(filtersdir)/gbfstrongs.cpp
+GBFFIL += $(filtersdir)/gbffootnotes.cpp
+GBFFIL += $(filtersdir)/gbfheadings.cpp
+GBFFIL += $(filtersdir)/gbfredletterwords.cpp
+GBFFIL += $(filtersdir)/gbfmorph.cpp
+GBFFIL += $(filtersdir)/gbfwordjs.cpp
+
+THMLFIL = $(filtersdir)/thmlstrongs.cpp
+THMLFIL += $(filtersdir)/thmlfootnotes.cpp
+THMLFIL += $(filtersdir)/thmlheadings.cpp
+THMLFIL += $(filtersdir)/thmlmorph.cpp
+THMLFIL += $(filtersdir)/thmllemma.cpp
+THMLFIL += $(filtersdir)/thmlscripref.cpp
+THMLFIL += $(filtersdir)/thmlvariants.cpp
+THMLFIL += $(filtersdir)/thmlgbf.cpp
+THMLFIL += $(filtersdir)/thmlrtf.cpp
+THMLFIL += $(filtersdir)/thmlhtml.cpp
+THMLFIL += $(filtersdir)/thmlhtmlhref.cpp
+THMLFIL += $(filtersdir)/thmlwebif.cpp
+THMLFIL += $(filtersdir)/thmlwordjs.cpp
+
+TEIFIL = $(filtersdir)/teiplain.cpp
+TEIFIL += $(filtersdir)/teirtf.cpp
+TEIFIL += $(filtersdir)/teihtmlhref.cpp
+
+CONVFIL = $(filtersdir)/gbfthml.cpp
+CONVFIL += $(filtersdir)/gbfosis.cpp
+CONVFIL += $(filtersdir)/thmlosis.cpp
+CONVFIL += $(filtersdir)/thmlplain.cpp
+CONVFIL += $(filtersdir)/osisosis.cpp
+
+OSISFIL = $(filtersdir)/osisheadings.cpp
+OSISFIL += $(filtersdir)/osisfootnotes.cpp
+OSISFIL += $(filtersdir)/osishtmlhref.cpp
+OSISFIL += $(filtersdir)/osiswebif.cpp
+OSISFIL += $(filtersdir)/osismorph.cpp
+OSISFIL += $(filtersdir)/osisstrongs.cpp
+OSISFIL += $(filtersdir)/osisplain.cpp
+OSISFIL += $(filtersdir)/osisrtf.cpp
+OSISFIL += $(filtersdir)/osislemma.cpp
+OSISFIL += $(filtersdir)/osisredletterwords.cpp
+OSISFIL += $(filtersdir)/osisscripref.cpp
+OSISFIL += $(filtersdir)/osisvariants.cpp
+OSISFIL += $(filtersdir)/osiswordjs.cpp
+OSISFIL += $(filtersdir)/osismorphsegmentation.cpp
+
+libsword_la_SOURCES += $(filtersdir)/latin1utf8.cpp
+libsword_la_SOURCES += $(filtersdir)/latin1utf16.cpp
+libsword_la_SOURCES += $(filtersdir)/utf8utf16.cpp
+libsword_la_SOURCES += $(filtersdir)/utf16utf8.cpp
+libsword_la_SOURCES += $(filtersdir)/scsuutf8.cpp
+libsword_la_SOURCES += $(filtersdir)/utf8html.cpp
+libsword_la_SOURCES += $(filtersdir)/utf8latin1.cpp
+
+libsword_la_SOURCES += $(filtersdir)/utf8cantillation.cpp
+libsword_la_SOURCES += $(filtersdir)/utf8hebrewpoints.cpp
+libsword_la_SOURCES += $(filtersdir)/utf8greekaccents.cpp
+
+libsword_la_SOURCES += $(filtersdir)/cipherfil.cpp
+
+PLFIL = $(filtersdir)/rtfhtml.cpp
+PLFIL += $(filtersdir)/plainfootnotes.cpp
+PLFIL += $(filtersdir)/plainhtml.cpp
+PLFIL += $(filtersdir)/greeklexattribs.cpp
+PLFIL += $(filtersdir)/unicodertf.cpp
+PLFIL += $(filtersdir)/papyriplain.cpp
+
+
+SWICUSRC = $(filtersdir)/utf8transliterator.cpp
+SWICUSRC += $(filtersdir)/utf8nfc.cpp
+SWICUSRC += $(filtersdir)/utf8nfkd.cpp
+SWICUSRC += $(filtersdir)/utf8arshaping.cpp
+SWICUSRC += $(filtersdir)/utf8bidireorder.cpp
+
+if ICU
+ICUDEFS = -D_ICU_
+DISTSWICUSRC =
+SWREALICUSRC = $(SWICUSRC)
+else
+if ICUSWORD
+ICUDEFS = -D_ICU_ -D_ICUSWORD_
+DISTSWICUSRC =
+SWREALICUSRC = $(SWICUSRC)
+else
+DISTSWICUSRC = $(SWICUSRC)
+SWREALICUSRC =
+endif
+endif
+
+AM_CPPFLAGS += $(ICUDEFS)
+libsword_la_SOURCES += $(SWREALICUSRC)
+EXTRA_DIST = $(DISTSWICUSRC)
+
+libsword_la_SOURCES += $(OSISFIL) $(GBFFIL) \
+ $(THMLFIL) $(CONVFIL) $(PLFIL) $(TEIFIL)
diff --git a/src/modules/filters/cipherfil.cpp b/src/modules/filters/cipherfil.cpp
new file mode 100644
index 0000000..24c665e
--- /dev/null
+++ b/src/modules/filters/cipherfil.cpp
@@ -0,0 +1,46 @@
+/******************************************************************************
+ *
+ * cipherfil - SWFilter descendant to decipher a module
+ */
+
+
+#include <stdlib.h>
+#include <cipherfil.h>
+#include <swcipher.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+CipherFilter::CipherFilter(const char *key) {
+ cipher = new SWCipher((unsigned char *)key);
+}
+
+
+CipherFilter::~CipherFilter() {
+ delete cipher;
+}
+
+
+SWCipher *CipherFilter::getCipher() {
+ return cipher;
+}
+
+
+char CipherFilter::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ if (text.length() > 2) { //check if it's large enough to substract 2 in the next step.
+ unsigned long len = text.length();
+ if (!key) { // hack, using key to determine encipher, or decipher
+ cipher->cipherBuf(&len, text.getRawData()); //set buffer to enciphered text
+ memcpy(text.getRawData(), cipher->Buf(), len);
+// text = cipher->Buf(); //get the deciphered buffer
+ }
+ else if ((unsigned long)key == 1) {
+ cipher->Buf(text.getRawData(), len);
+ memcpy(text.getRawData(), cipher->cipherBuf(&len), len);
+// text = cipher->cipherBuf(&len);
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbffootnotes.cpp b/src/modules/filters/gbffootnotes.cpp
new file mode 100644
index 0000000..bef29b8
--- /dev/null
+++ b/src/modules/filters/gbffootnotes.cpp
@@ -0,0 +1,193 @@
+/******************************************************************************
+ *
+ * gbffootnotes - SWFilter descendant to hide or show footnotes
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gbffootnotes.h>
+#include <swmodule.h>
+#include <swbuf.h>
+#include <versekey.h>
+#include <utilxml.h>
+
+SWORD_NAMESPACE_START
+
+const char oName[] = "Footnotes";
+const char oTip[] = "Toggles Footnotes On and Off if they exist";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+
+GBFFootnotes::GBFFootnotes() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+}
+
+
+GBFFootnotes::~GBFFootnotes() {
+}
+
+
+char GBFFootnotes::processText (SWBuf &text, const SWKey *key, const SWModule *module)
+{
+
+ SWBuf token;
+ bool intoken = false;
+ bool hide = false;
+ SWBuf tagText;
+ XMLTag startTag;
+ SWBuf refs = "";
+ int footnoteNum = 1;
+ char buf[254];
+ VerseKey parser = key->getText();
+
+ SWBuf orig = text;
+ const char *from = orig.c_str();
+
+ //XMLTag tag;
+
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ token = "";
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+
+ //XMLTag tag(token);
+ if (!strncmp(token, "RF",2)) {
+// tag = token;
+
+ refs = "";
+ startTag = token;
+ hide = true;
+ tagText = "";
+ continue;
+ }
+ else if (!strncmp(token, "Rf",2)) {
+ if (module->isProcessEntryAttributes()) {
+ //tag = token;
+
+ if((tagText.length() == 1) || !strcmp(module->Name(), "IGNT")) {
+ if (option) { // for ASV marks text in verse then put explanation at end of verse
+ text.append(" <FS>[");
+ text.append(tagText);
+ text.append("]<Fs>");
+ hide = false;
+ continue;
+ }
+ }
+ SWBuf fc = module->getEntryAttributes()["Footnote"]["count"]["value"];
+ footnoteNum = (fc.length()) ? atoi(fc.c_str()) : 0;
+ sprintf(buf, "%i", ++footnoteNum);
+ module->getEntryAttributes()["Footnote"]["count"]["value"] = buf;
+ StringList attributes = startTag.getAttributeNames();
+ for (StringList::const_iterator it = attributes.begin(); it != attributes.end(); it++) {
+ module->getEntryAttributes()["Footnote"][buf][it->c_str()] = startTag.getAttribute(it->c_str());
+ }
+ module->getEntryAttributes()["Footnote"][buf]["body"] = tagText;
+ startTag.setAttribute("swordFootnote", buf);
+ }
+ hide = false;
+ if (option) {
+ text.append(startTag);
+ text.append(tagText);
+ }
+ else continue;
+ }
+ if (!hide) {
+ text.append('<');
+ text.append(token);
+ text.append('>');
+ }
+ else {
+ tagText.append('<');
+ tagText.append(token);
+ tagText.append('>');
+ }
+ continue;
+ }
+ if (intoken) { //copy token
+ token.append(*from);
+ }
+ else if (!hide) { //copy text which is not inside a token
+ text.append(*from);
+ }
+ else tagText.append(*from);
+ }
+ return 0;
+
+ /*
+ if (!option) { // if we don't want footnotes
+ char token[4096]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ int len;
+ bool hide = false;
+
+ const char *from;
+ SWBuf orig = text;
+ from = orig.c_str();
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+// memset(token, 0, 4096);
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ switch (*token) {
+ case 'R': // Reference
+ switch(token[1]) {
+ case 'F': // Begin footnote
+ hide = true;
+ break;
+ case 'f': // end footnote
+ hide = false;
+ break;
+ }
+ continue; // skip token
+ case 'W':
+ if (token[1] == 'T') {
+ switch (token[2]) {
+ case 'P':
+ case 'S':
+ case 'A':
+ continue; // remove this token
+ default:
+ break;
+ }
+ }
+ }
+ // if not a footnote token, keep token in text
+ if (!hide) {
+ text += '<';
+ text += token;
+ text += '>';
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 4090)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0; // +2 cuz we init token with 2 extra '0' because of switch statement
+ }
+ else {
+ if (!hide) {
+ text += *from;
+ }
+ }
+ }
+ }
+ return 0;*/
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfheadings.cpp b/src/modules/filters/gbfheadings.cpp
new file mode 100644
index 0000000..81a4d94
--- /dev/null
+++ b/src/modules/filters/gbfheadings.cpp
@@ -0,0 +1,87 @@
+/******************************************************************************
+ *
+ * gbfheadings - SWFilter descendant to hide or show headings
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <gbfheadings.h>
+
+SWORD_NAMESPACE_START
+
+
+const char oName[] = "Headings";
+const char oTip[] = "Toggles Headings On and Off if they exist";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+GBFHeadings::GBFHeadings() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+}
+
+
+GBFHeadings::~GBFHeadings() {
+}
+
+
+char GBFHeadings::processText (SWBuf &text, const SWKey *key, const SWModule *module) {
+ if (!option) { // if we don't want headings
+ char token[2048]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ bool hide = false;
+
+ const char *from;
+ SWBuf orig = text;
+ from = orig.c_str();
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+// memset(token, 0, 2048);
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ switch (*token) {
+ case 'T': // Reference
+ switch(token[1]) {
+ case 'S': // Begin heading
+ hide = true;
+ break;
+ case 's': // end heading
+ hide = false;
+ break;
+ }
+ continue; // skip token
+ }
+ // if not a heading token, keep token in text
+ if (!hide) {
+ text += '<';
+ for (char *tok = token; *tok; tok++)
+ text += *tok;
+ text += '>';
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else {
+ if (!hide) {
+ text += *from;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfhtml.cpp b/src/modules/filters/gbfhtml.cpp
new file mode 100644
index 0000000..a9d8434
--- /dev/null
+++ b/src/modules/filters/gbfhtml.cpp
@@ -0,0 +1,181 @@
+/***************************************************************************
+ gbfhtml.cpp - GBF to HTML filter
+ -------------------
+ begin : 2001-09-03
+ copyright : 2001 by CrossWire Bible Society
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdlib.h>
+#include <gbfhtml.h>
+#include <ctype.h>
+
+SWORD_NAMESPACE_START
+
+GBFHTML::GBFHTML() {
+ setTokenStart("<");
+ setTokenEnd(">");
+
+ setTokenCaseSensitive(true);
+
+ addTokenSubstitute("Rf", ")</small></font>");
+ addTokenSubstitute("Rx", "</a>");
+ addTokenSubstitute("FI", "<i>"); // italics begin
+ addTokenSubstitute("Fi", "</i>");
+ addTokenSubstitute("FB", "<n>"); // bold begin
+ addTokenSubstitute("Fb", "</n>");
+ addTokenSubstitute("FR", "<font color=\"#FF0000\">"); // words of Jesus begin
+ addTokenSubstitute("Fr", "</font>");
+ addTokenSubstitute("FU", "<u>"); // underline begin
+ addTokenSubstitute("Fu", "</u>");
+ addTokenSubstitute("FO", "<cite>"); // Old Testament quote begin
+ addTokenSubstitute("Fo", "</cite>");
+ addTokenSubstitute("FS", "<sup>"); // Superscript begin// Subscript begin
+ addTokenSubstitute("Fs", "</sup>");
+ addTokenSubstitute("FV", "<sub>"); // Subscript begin
+ addTokenSubstitute("Fv", "</sub>");
+ addTokenSubstitute("TT", "<big>"); // Book title begin
+ addTokenSubstitute("Tt", "</big>");
+ addTokenSubstitute("PP", "<cite>"); // poetry begin
+ addTokenSubstitute("Pp", "</cite>");
+ addTokenSubstitute("Fn", "</font>"); // font end
+ addTokenSubstitute("CL", "<br />"); // new line
+ addTokenSubstitute("CM", "<!P><br />"); // paragraph <!P> is a non showing comment that can be changed in the front end to <P> if desired
+ addTokenSubstitute("CG", ""); // ???
+ addTokenSubstitute("CT", ""); // ???
+ addTokenSubstitute("JR", "<div align=\"right\">"); // right align begin
+ addTokenSubstitute("JC", "<div align=\"center\">"); // center align begin
+ addTokenSubstitute("JL", "</div>"); // align end
+
+}
+
+
+bool GBFHTML::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+ const char *tok;
+ char val[128];
+ char *valto;
+ const char *num;
+ MyUserData *u = (MyUserData *)userData;
+
+ if (!substituteToken(buf, token)) {
+ // deal with OSIS note tags. Just hide till OSISRTF
+ if (!strncmp(token, "note ", 5)) {
+ // let's stop text from going to output
+ u->suspendTextPassThru = true;
+ }
+
+ else if (!strncmp(token, "/note", 5)) {
+ u->suspendTextPassThru = false;
+ }
+
+ else if (!strncmp(token, "w", 1)) {
+ // OSIS Word (temporary until OSISRTF is done)
+ valto = val;
+ num = strstr(token, "lemma=\"x-Strongs:");
+ if (num) {
+ for (num+=17; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ buf += *tok;
+ buf += "&gt;</em></small> ";
+ }
+ } else {
+ num = strstr(token, "lemma=\"strong:");
+ if (num) {
+ for (num+=14; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ buf += *tok;
+ buf += "&gt;</em></small> ";
+ }
+ }
+ }
+ valto = val;
+ num = strstr(token, "morph=\"x-Robinson:");
+ if (num) {
+ for (num+=18; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ // normal robinsons tense
+ buf += " <small><em>(";
+ for (tok = val; *tok; tok++)
+ buf += *tok;
+ buf += ")</em></small> ";
+ }
+ }
+
+ else if (!strncmp(token, "WG", 2) || !strncmp(token, "WH", 2)) { // strong's numbers
+ buf += " <small><em>&lt;";
+ for (tok = token + 2; *tok; tok++)
+ buf += *tok;
+ buf += "&gt;</em></small> ";
+ }
+
+ else if (!strncmp(token, "WTG", 3) || !strncmp(token, "WTH", 3)) { // strong's numbers tense
+ buf += " <small><em>&lt;";
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += ")</em></small> ";
+ }
+
+ else if (!strncmp(token, "RX", 2)) {
+ buf += "<i>";
+ for (tok = token + 3; *tok; tok++) {
+ if(*tok != '<' && *tok+1 != 'R' && *tok+2 != 'x') {
+ buf += *tok;
+ }
+ else {
+ break;
+ }
+ }
+ buf += "</i>";
+ }
+
+ else if (!strncmp(token, "RB", 2)) {
+ buf += "<i>";
+ u->hasFootnotePreTag = true;
+ }
+
+ else if (!strncmp(token, "RF", 2)) {
+ if (u->hasFootnotePreTag) {
+ u->hasFootnotePreTag = false;
+ buf += "</i> ";
+ }
+ buf += "<font color=\"#800000\"><small> (";
+ }
+
+ else if (!strncmp(token, "FN", 2)) {
+ buf += "<font face=\"";
+ for (tok = token + 2; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "\">";
+ }
+
+ else if (!strncmp(token, "CA", 2)) { // ASCII value
+ buf += (char)atoi(&token[2]);
+ }
+
+ else {
+ return false;
+ }
+ }
+ return true;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfhtmlhref.cpp b/src/modules/filters/gbfhtmlhref.cpp
new file mode 100644
index 0000000..7f1c254
--- /dev/null
+++ b/src/modules/filters/gbfhtmlhref.cpp
@@ -0,0 +1,288 @@
+/***************************************************************************
+ gbfhtmlhref.cpp - GBF to HTML filter with hrefs
+ for strongs and morph tags
+ -------------------
+ begin : 2001-09-03
+ copyright : 2001 by CrossWire Bible Society
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdlib.h>
+#include <gbfhtmlhref.h>
+#include <swmodule.h>
+#include <utilxml.h>
+#include <versekey.h>
+#include <ctype.h>
+#include <url.h>
+
+SWORD_NAMESPACE_START
+
+GBFHTMLHREF::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
+ if (module) {
+ version = module->Name();
+ }
+}
+
+GBFHTMLHREF::GBFHTMLHREF() {
+ setTokenStart("<");
+ setTokenEnd(">");
+
+ setTokenCaseSensitive(true);
+
+ //addTokenSubstitute("Rf", ")</small></font>");
+ addTokenSubstitute("FA", "<font color=\"#800000\">"); // for ASV footnotes to mark text
+ addTokenSubstitute("Rx", "</a>");
+ addTokenSubstitute("FI", "<i>"); // italics begin
+ addTokenSubstitute("Fi", "</i>");
+ addTokenSubstitute("FB", "<b>"); // bold begin
+ addTokenSubstitute("Fb", "</b>");
+ addTokenSubstitute("FR", "<font color=\"#FF0000\">"); // words of Jesus begin
+ addTokenSubstitute("Fr", "</font>");
+ addTokenSubstitute("FU", "<u>"); // underline begin
+ addTokenSubstitute("Fu", "</u>");
+ addTokenSubstitute("FO", "<cite>"); // Old Testament quote begin
+ addTokenSubstitute("Fo", "</cite>");
+ addTokenSubstitute("FS", "<sup>"); // Superscript begin// Subscript begin
+ addTokenSubstitute("Fs", "</sup>");
+ addTokenSubstitute("FV", "<sub>"); // Subscript begin
+ addTokenSubstitute("Fv", "</sub>");
+ addTokenSubstitute("TT", "<big>"); // Book title begin
+ addTokenSubstitute("Tt", "</big>");
+ addTokenSubstitute("PP", "<cite>"); // poetry begin
+ addTokenSubstitute("Pp", "</cite>");
+ addTokenSubstitute("Fn", "</font>"); // font end
+ addTokenSubstitute("CL", "<br />"); // new line
+ addTokenSubstitute("CM", "<!P><br />"); // paragraph <!P> is a non showing comment that can be changed in the front end to <P> if desired
+ addTokenSubstitute("CG", ""); // ???
+ addTokenSubstitute("CT", ""); // ???
+ addTokenSubstitute("JR", "<div align=\"right\">"); // right align begin
+ addTokenSubstitute("JC", "<div align=\"center\">"); // center align begin
+ addTokenSubstitute("JL", "</div>"); // align end
+
+}
+
+
+bool GBFHTMLHREF::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+ const char *tok;
+ MyUserData *u = (MyUserData *)userData;
+
+ if (!substituteToken(buf, token)) {
+ XMLTag tag(token);
+ /*if (!strncmp(token, "w", 1)) {
+ // OSIS Word (temporary until OSISRTF is done)
+ valto = val;
+ num = strstr(token, "lemma=\"x-Strongs:");
+ if (num) {
+ for (num+=17; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;<a href=\"type=Strongs value=";
+ for (tok = val; *tok; tok++)
+ buf += *tok;
+ buf += "\">";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ buf += *tok;
+ buf += "</a>&gt;</em></small> ";
+ //cout << buf;
+
+ }
+ // forget these for now
+ //else {
+ // verb morph
+ //sprintf(wordstr, "%03d", word-1);
+ //module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ //}
+ }
+ else {
+ num = strstr(token, "lemma=\"strong:");
+ if (num) {
+ for (num+=14; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;<a href=\"type=Strongs value=";
+ for (tok = val; *tok; tok++)
+ buf += *tok;
+ buf += "\">";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ buf += *tok;
+ buf += "</a>&gt;</em></small> ";
+ //cout << buf;
+
+ }
+ // forget these for now
+ //else {
+ // verb morph
+ //sprintf(wordstr, "%03d", word-1);
+ //module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ //}
+ }
+ }
+ valto = val;
+ num = strstr(token, "morph=\"x-Robinson:");
+ if (num) {
+ for (num+=18; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ buf += " <small><em>(<a href=\"type=morph class=Robinson value=";
+ for (tok = val; *tok; tok++)
+ // normal robinsons tense
+ buf += *tok;
+ buf += "\">";
+ for (tok = val; *tok; tok++)
+ //if(*tok != '\"')
+ buf += *tok;
+ buf += "</a>)</em></small> ";
+ }
+ }*/
+
+ // else
+ if (!strncmp(token, "WG", 2)) { // strong's numbers
+ //buf += " <small><em>&lt;<a href=\"type=Strongs value=";
+ buf += " <small><em>&lt;<a href=\"passagestudy.jsp?action=showStrongs&type=Greek&value=";
+ for (tok = token+2; *tok; tok++)
+ //if(token[i] != '\"')
+ buf += *tok;
+ buf += "\">";
+ for (tok = token + 2; *tok; tok++)
+ //if(token[i] != '\"')
+ buf += *tok;
+ buf += "</a>&gt;</em></small>";
+ }
+ else if (!strncmp(token, "WH", 2)) { // strong's numbers
+ //buf += " <small><em>&lt;<a href=\"type=Strongs value=";
+ buf += " <small><em>&lt;<a href=\"passagestudy.jsp?action=showStrongs&type=Hebrew&value=";
+ for (tok = token+2; *tok; tok++)
+ //if(token[i] != '\"')
+ buf += *tok;
+ buf += "\">";
+ for (tok = token + 2; *tok; tok++)
+ //if(token[i] != '\"')
+ buf += *tok;
+ buf += "</a>&gt;</em></small>";
+ }
+ else if (!strncmp(token, "WTG", 3)) { // strong's numbers tense
+ //buf += " <small><em>(<a href=\"type=Strongs value=";
+ buf += " <small><em>(<a href=\"passagestudy.jsp?action=showStrongs&type=Greek&value=";
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "\">";
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "</a>)</em></small>";
+ }
+ else if (!strncmp(token, "WTH", 3)) { // strong's numbers tense
+ //buf += " <small><em>(<a href=\"type=Strongs value=";
+ buf += " <small><em>(<a href=\"passagestudy.jsp?action=showStrongs&type=Hebrew&value=";
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "\">";
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "</a>)</em></small>";
+ }
+
+ else if (!strncmp(token, "WT", 2) && strncmp(token, "WTH", 3) && strncmp(token, "WTG", 3)) { // morph tags
+ //buf += " <small><em>(<a href=\"type=morph class=none value=";
+ buf += " <small><em>(<a href=\"passagestudy.jsp?action=showMorph&type=Greek&value=";
+
+ for (tok = token + 2; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "\">";
+ for (tok = token + 2; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "</a>)</em></small>";
+ }
+
+ else if (!strcmp(tag.getName(), "RX")) {
+ buf += "<a href=\"";
+ for (tok = token + 3; *tok; tok++) {
+ if(*tok != '<' && *tok+1 != 'R' && *tok+2 != 'x') {
+ buf += *tok;
+ }
+ else {
+ break;
+ }
+ }
+ buf += "\">";
+ }
+ else if (!strcmp(tag.getName(), "RF")) {
+ SWBuf type = tag.getAttribute("type");
+ SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
+ VerseKey *vkey = NULL;
+ // see if we have a VerseKey * or descendant
+ SWTRY {
+ vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+ }
+ SWCATCH ( ... ) { }
+ if (vkey) {
+ // leave this special osis type in for crossReference notes types? Might thml use this some day? Doesn't hurt.
+ //char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+ buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=n&value=%s&module=%s&passage=%s\"><small><sup>*n</sup></small></a> ",
+ URL::encode(footnoteNumber.c_str()).c_str(),
+ URL::encode(u->version.c_str()).c_str(),
+ URL::encode(vkey->getText()).c_str());
+ }
+ u->suspendTextPassThru = true;
+ }
+ else if (!strcmp(tag.getName(), "Rf")) {
+ u->suspendTextPassThru = false;
+ }
+/*
+ else if (!strncmp(token, "RB", 2)) {
+ buf += "<i> ";
+ u->hasFootnotePreTag = true;
+ }
+
+ else if (!strncmp(token, "Rf", 2)) {
+ buf += "&nbsp<a href=\"note=";
+ buf += u->lastTextNode.c_str();
+ buf += "\">";
+ buf += "<small><sup>*n</sup></small></a>&nbsp";
+ // let's let text resume to output again
+ u->suspendTextPassThru = false;
+ }
+
+ else if (!strncmp(token, "RF", 2)) {
+ if (u->hasFootnotePreTag) {
+ u->hasFootnotePreTag = false;
+ buf += "</i> ";
+ }
+ u->suspendTextPassThru = true;
+ }
+*/
+ else if (!strncmp(token, "FN", 2)) {
+ buf += "<font face=\"";
+ for (tok = token + 2; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "\">";
+ }
+
+ else if (!strncmp(token, "CA", 2)) { // ASCII value
+ buf += (char)atoi(&token[2]);
+ }
+
+ else {
+ return false;
+ }
+ }
+ return true;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfmorph.cpp b/src/modules/filters/gbfmorph.cpp
new file mode 100644
index 0000000..5226db7
--- /dev/null
+++ b/src/modules/filters/gbfmorph.cpp
@@ -0,0 +1,77 @@
+/******************************************************************************
+ *
+ * gbfmorph - SWFilter descendant to hide or show morph tags
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <gbfmorph.h>
+
+SWORD_NAMESPACE_START
+
+const char oName[] = "Morphological Tags";
+const char oTip[] = "Toggles Morphological Tags On and Off if they exist";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+GBFMorph::GBFMorph() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+}
+
+
+GBFMorph::~GBFMorph() {
+}
+
+
+char GBFMorph::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ if (!option) { // if we don't want morph tags
+ const char *from;
+ char token[2048]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ bool lastspace = false;
+
+ SWBuf orig = text;
+ from = orig.c_str();
+
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ if (*token == 'W' && token[1] == 'T') { // Morph
+ if ((from[1] == ' ') || (from[1] == ',') || (from[1] == ';') || (from[1] == '.') || (from[1] == '?') || (from[1] == '!') || (from[1] == ')') || (from[1] == '\'') || (from[1] == '\"')) {
+ if (lastspace)
+ text--;
+ }
+ continue;
+ }
+ // if not a morph tag token, keep token in text
+ text += '<';
+ text += token;
+ text += '>';
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else {
+ text += *from;
+ lastspace = (*from == ' ');
+ }
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfosis.cpp b/src/modules/filters/gbfosis.cpp
new file mode 100644
index 0000000..00443f9
--- /dev/null
+++ b/src/modules/filters/gbfosis.cpp
@@ -0,0 +1,420 @@
+/******************************************************************************
+ *
+ * gbfstrongs - SWFilter descendant to hide or show strongs number
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <gbfosis.h>
+#include <swmodule.h>
+#include <versekey.h>
+#include <swlog.h>
+#include <stdarg.h>
+
+SWORD_NAMESPACE_START
+
+GBFOSIS::GBFOSIS() {
+}
+
+
+GBFOSIS::~GBFOSIS() {
+}
+
+
+char GBFOSIS::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ char token[2048]; //cheesy, we seem to like cheese :)
+ int tokpos = 0;
+ bool intoken = false;
+ bool keepToken = false;
+
+// static QuoteStack quoteStack;
+
+ SWBuf orig = text;
+ SWBuf tmp;
+ SWBuf value;
+
+ bool suspendTextPassThru = false;
+ bool handled = false;
+ bool newWord = false;
+ bool newText = false;
+ bool lastspace = false;
+
+ const char *wordStart = text.c_str();
+ const char *wordEnd = NULL;
+
+ const char *textStart = NULL;
+ const char *textEnd = NULL;
+
+ SWBuf textNode = "";
+
+ SWBuf buf;
+
+ text = "";
+ for (const char* from = orig.c_str(); *from; ++from) {
+ if (*from == '<') { //start of new token detected
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ textEnd = from-1; //end of last text node found
+ wordEnd = text.c_str() + text.length();//not good, instead of wordEnd = to!
+
+ continue;
+ }
+
+ if (*from == '>') { // process tokens
+ intoken = false;
+ keepToken = false;
+ suspendTextPassThru = false;
+ newWord = true;
+ handled = false;
+
+ while (wordStart < (text.c_str() + text.length())) { //hack
+ if (strchr(";,. :?!()'\"", *wordStart) && wordStart[0] && wordStart[1])
+ wordStart++;
+ else break;
+ }
+ while (wordEnd > wordStart) {
+ if (strchr(" ,;:.?!()'\"", *wordEnd))
+ wordEnd--;
+ else break;
+ }
+
+ // Scripture Reference
+ if (!strncmp(token, "scripRef", 8)) {
+ suspendTextPassThru = true;
+ newText = true;
+ handled = true;
+ }
+ else if (!strncmp(token, "/scripRef", 9)) {
+ tmp = "";
+ tmp.append(textStart, (int)(textEnd - textStart)+1);
+ text += VerseKey::convertToOSIS(tmp.c_str(), key);
+
+ lastspace = false;
+ suspendTextPassThru = false;
+ handled = true;
+ }
+
+ // Footnote
+ if (!strcmp(token, "RF") || !strncmp(token, "RF ", 3)) { //the GBFFootnotes filter adds the attribute "swordFootnote", we want to catch that, too
+ // pushString(buf, "<reference work=\"Bible.KJV\" reference=\"");
+ text += "<note type=\"x-StudyNote\">";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+ else if (!strcmp(token, "Rf")) {
+ text += "</note>";
+ lastspace = false;
+ handled = true;
+ }
+ // hebrew titles
+ if (!strcmp(token, "TH")) {
+ text += "<title type=\"psalm\">";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+ else if (!strcmp(token, "Th")) {
+ text += "</title>";
+ lastspace = false;
+ handled = true;
+ }
+ // Italics assume transchange
+ if (!strcmp(token, "FI")) {
+ text += "<transChange type=\"added\">";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+ else if (!strcmp(token, "Fi")) {
+ text += "</transChange>";
+ lastspace = false;
+ handled = true;
+ }
+ // less than
+ if (!strcmp(token, "CT")) {
+ text += "&lt;";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+ // greater than
+ if (!strcmp(token, "CG")) {
+ text += "&gt;";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+ // Paragraph break. For now use empty paragraph element
+ if (!strcmp(token, "CM")) {
+ text += "<milestone type=\"x-p\" />";
+ newText = true;
+ lastspace = false;
+ handled = true;
+ }
+
+ // Figure
+ else if (!strncmp(token, "img ", 4)) {
+ const char *src = strstr(token, "src");
+ if (!src) // assert we have a src attribute
+ continue;
+// return false;
+
+ text += "<figure src=\"";
+ const char *c;
+ for (c = src;((*c) && (*c != '"')); c++);
+
+// uncomment for SWORD absolute path logic
+// if (*(c+1) == '/') {
+// pushString(buf, "file:");
+// pushString(buf, module->getConfigEntry("AbsoluteDataPath"));
+// if (*((*buf)-1) == '/')
+// c++; // skip '/'
+// }
+// end of uncomment for asolute path logic
+
+ for (c++;((*c) && (*c != '"')); c++) {
+ text += *c;
+ }
+ text += "\" />";
+
+ lastspace = false;
+ handled = true;
+ }
+
+ // Strongs numbers
+ else if (*token == 'W' && (token[1] == 'G' || token[1] == 'H')) { // Strongs
+ bool divineName = false;
+ value = token+1;
+
+ // normal strongs number
+ //strstrip(val);
+ if (!strncmp(wordStart, "<w ", 3)) {
+ const char *attStart = strstr(wordStart, "lemma");
+ if (attStart) {
+ attStart += 7;
+
+ buf = "";
+ buf.appendFormatted("strong:%s ", value.c_str());
+ }
+ else { // no lemma attribute
+ attStart = wordStart + 3;
+
+ buf = "";
+ buf.appendFormatted(buf, "lemma=\"strong:%s\" ", value.c_str());
+ }
+
+ text.insert(attStart - text.c_str(), buf);
+ }
+ else { //wordStart doesn't point to an existing <w> attribute!
+ if (!strcmp(value.c_str(), "H03068")) { //divineName
+ buf = "";
+ buf.appendFormatted("<divineName><w lemma=\"strong:%s\">", value.c_str());
+
+ divineName = true;
+ }
+ else {
+ buf = "";
+ buf.appendFormatted("<w lemma=\"strong:%s\">", value.c_str());
+ }
+
+ text.insert(wordStart - text.c_str(), buf);
+
+ if (divineName) {
+ wordStart += 12;
+ text += "</w></divineName>";
+ }
+ else text += "</w>";
+
+ lastspace = false;
+ }
+ handled = true;
+ }
+
+ // Morphology
+ else if (*token == 'W' && token[1] == 'T') {
+ if (token[2] == 'G' || token[2] == 'H') { // Strongs
+ value = token+2;
+ }
+ else value = token+1;
+
+ if (!strncmp(wordStart, "<w ", 3)) {
+ const char *attStart = strstr(wordStart, "morph");
+ if (attStart) { //existing morph attribute, append this one to it
+ attStart += 7;
+ buf = "";
+ buf.appendFormatted("%s:%s ", "robinson", value.c_str());
+ }
+ else { // no lemma attribute
+ attStart = wordStart + 3;
+ buf = "";
+ buf.appendFormatted("morph=\"%s:%s\" ", "robinson", value.c_str());
+ }
+
+ text.insert(attStart - text.c_str(), buf); //hack, we have to
+ }
+ else { //no existing <w> attribute fond
+ buf = "";
+ buf.appendFormatted("<w morph=\"%s:%s\">", "robinson", value.c_str());
+ text.insert(wordStart - text.c_str(), buf);
+ text += "</w>";
+ lastspace = false;
+
+ }
+ handled = true;
+ }
+
+ if (!keepToken) {
+ if (!handled) {
+ SWLog::getSystemLog()->logError("Unprocessed Token: <%s> in key %s", token, key ? (const char*)*key : "<unknown>");
+// exit(-1);
+ }
+ if (from[1] && strchr(" ,;.:?!()'\"", from[1])) {
+ if (lastspace) {
+ text--;
+ }
+ }
+ if (newText) {
+ textStart = from+1;
+ newText = false;
+ }
+ continue;
+ }
+
+ // if not a strongs token, keep token in text
+ text.appendFormatted("<%s>", token);
+
+ if (newText) {
+ textStart = text.c_str() + text.length();
+ newWord = false;
+ }
+ continue;
+ }
+ if (intoken) {
+ if ((tokpos < 2045) && ((*from != 10)&&(*from != 13))) {
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ }
+ else {
+ switch (*from) {
+ case '\'':
+ case '\"':
+ case '`':
+// quoteStack.handleQuote(fromStart, from, &to);
+ text += *from;
+ //from++; //this line removes chars after an apostrophe! Needs fixing.
+ break;
+ default:
+ if (newWord && (*from != ' ')) {
+ wordStart = text.c_str() + text.length();
+ newWord = false;
+
+ //fix this if required?
+ //memset(to, 0, 10);
+
+ }
+
+ if (!suspendTextPassThru) {
+ text += (*from);
+ lastspace = (*from == ' ');
+ }
+ }
+ }
+ }
+
+ VerseKey *vkey = SWDYNAMIC_CAST(VerseKey, key);
+ if (vkey) {
+ SWBuf ref = "";
+ if (vkey->Verse()) {
+ ref.appendFormatted("\t\t<verse osisID=\"%s\">", vkey->getOSISRef());
+ }
+
+ if (ref.length() > 0) {
+
+ text = ref + text;
+
+ if (vkey->Verse()) {
+ VerseKey tmp;
+ tmp = *vkey;
+ tmp.AutoNormalize(0);
+ tmp.Headings(1);
+
+ text += "</verse>";
+
+ tmp = MAXVERSE;
+ if (*vkey == tmp) {
+ tmp.Verse(0);
+// sprintf(ref, "\t</div>");
+// pushString(&to, ref);
+ tmp = MAXCHAPTER;
+ tmp = MAXVERSE;
+ if (*vkey == tmp) {
+ tmp.Chapter(0);
+ tmp.Verse(0);
+// sprintf(ref, "\t</div>");
+// pushString(&to, ref);
+/*
+ if (!quoteStack.empty()) {
+ SWLog::getSystemLog()->logError("popping unclosed quote at end of book");
+ quoteStack.clear();
+ }
+*/
+ }
+ }
+ }
+// else if (vkey->Chapter()) {
+// sprintf(ref, "\t<div type=\"chapter\" osisID=\"%s\">", vkey->getOSISRef());
+// }
+// else sprintf(ref, "\t<div type=\"book\" osisID=\"%s\">", vkey->getOSISRef());
+ }
+ }
+ return 0;
+}
+
+
+QuoteStack::QuoteStack() {
+ clear();
+}
+
+
+void QuoteStack::clear() {
+ while (!quotes.empty()) quotes.pop();
+}
+
+
+QuoteStack::~QuoteStack() {
+ clear();
+}
+
+
+void QuoteStack::handleQuote(char *buf, char *quotePos, SWBuf &text) {
+//QuoteInstance(char startChar = '\"', char level = 1, string uniqueID = "", char continueCount = 0) {
+ if (!quotes.empty()) {
+ QuoteInstance last = quotes.top();
+ if (last.startChar == *quotePos) {
+ text += "</quote>";
+ quotes.pop();
+ }
+ else {
+ quotes.push(QuoteInstance(*quotePos, last.level+1));
+ quotes.top().pushStartStream(text);
+ }
+ }
+ else {
+ quotes.push(QuoteInstance(*quotePos));
+ quotes.top().pushStartStream(text);
+ }
+}
+
+void QuoteStack::QuoteInstance::pushStartStream(SWBuf &text) {
+ text.appendFormatted("<quote level=\"%d\">", level);
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfplain.cpp b/src/modules/filters/gbfplain.cpp
new file mode 100644
index 0000000..5657e20
--- /dev/null
+++ b/src/modules/filters/gbfplain.cpp
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * gbfplain - SWFilter descendant to strip out all GBF tags or convert to
+ * ASCII rendered symbols.
+ */
+
+
+#include <stdlib.h>
+#include <gbfplain.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+GBFPlain::GBFPlain() {
+}
+
+
+char GBFPlain::processText (SWBuf &text, const SWKey *key, const SWModule *module)
+{
+ char token[2048];
+ int tokpos = 0;
+ bool intoken = false;
+ SWBuf orig = text;
+ const char* from = orig.c_str();
+
+ for (text = ""; *from; ++from) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') {
+ intoken = false;
+ // process desired tokens
+ switch (*token) {
+ case 'W': // Strongs
+ switch(token[1]) {
+ case 'G': // Greek
+ case 'H': // Hebrew
+ case 'T': // Tense
+ text.append(" <");
+ //for (char *tok = token + 2; *tok; tok++)
+ // text += *tok;
+ text.append(token+2);
+ text.append("> ");
+ continue;
+ }
+ break;
+ case 'R':
+ switch(token[1]) {
+ case 'F': // footnote begin
+ text.append(" [");
+ continue;
+ case 'f': // footnote end
+ text.append("] ");
+ continue;
+ }
+ break;
+ case 'C':
+ switch(token[1]) {
+ case 'A': // ASCII value
+ text.append((char)atoi(&token[2]));
+ continue;
+ case 'G':
+ text.append('>');
+ continue;
+/* Bug in WEB
+ case 'L':
+ *to++ = '<';
+ continue;
+*/
+ case 'L': // Bug in WEB. Use above entry when fixed
+ case 'N': // new line
+ text.append('\n');
+ continue;
+ case 'M': // new paragraph
+ text.append("\n\n");
+ continue;
+ }
+ break;
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else text.append(*from);
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfredletterwords.cpp b/src/modules/filters/gbfredletterwords.cpp
new file mode 100644
index 0000000..a79802d
--- /dev/null
+++ b/src/modules/filters/gbfredletterwords.cpp
@@ -0,0 +1,93 @@
+/******************************************************************************
+ *
+ * GBFRedLetterWords - SWFilter descendant to toggle red coloring of words of
+ * Christ in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <gbfredletterwords.h>
+#include <swmodule.h>
+#include <ctype.h>
+
+SWORD_NAMESPACE_START
+
+const char oName[] = "Words of Christ in Red";
+const char oTip[] = "Toggles Red Coloring for Words of Christ On and Off if they are marked";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+GBFRedLetterWords::GBFRedLetterWords() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+}
+
+
+GBFRedLetterWords::~GBFRedLetterWords() {
+}
+
+
+char GBFRedLetterWords::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+/** This function removes the red letter words in Bible like the WEB
+* The words are marked by <FR> as start and <Fr> as end tag.
+*/
+ if (!option) { // if we don't want footnotes
+ char token[4096]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ bool hide = false;
+
+ const char *from;
+ SWBuf orig = text;
+ from = orig.c_str();
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+// memset(token, 0, 4096);
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ /*switch (*token) {
+ case 'F': // Font attribute
+ switch(token[1]) {
+ case 'R': // Begin red letter words
+ hide = true;
+ break;
+ case 'r': // end red letter words
+ hide = false;
+ break;
+ }
+ continue; // skip token
+ }*/
+
+ //hide the token if either FR or Fr was detected
+ hide = (token[0] == 'F' && ( (token[1] == 'R') || (token[1] == 'r') ));
+
+ // if not a red letter word token, keep token in text
+ if (!hide) {
+ text += '<';
+ for (char *tok = token; *tok; tok++)
+ text += *tok;
+ text += '>';
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 4090)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0; // +2 cuz we init token with 2 extra '0' because of switch statement
+ }
+ else {
+ text += *from;
+ }
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfrtf.cpp b/src/modules/filters/gbfrtf.cpp
new file mode 100644
index 0000000..eb39612
--- /dev/null
+++ b/src/modules/filters/gbfrtf.cpp
@@ -0,0 +1,311 @@
+/******************************************************************************
+ *
+ * gbfrtf - SWFilter descendant to convert all GBF tags to RTF tags
+ */
+
+
+#include <gbfrtf.h>
+#include <utilstr.h>
+#include <ctype.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+GBFRTF::GBFRTF() {
+}
+
+
+char GBFRTF::processText(SWBuf &text, const SWKey *key, const SWModule *module)
+{
+ char token[2048];
+ char val[128];
+ char *valto;
+ char *num;
+ int tokpos = 0;
+ bool intoken = false;
+ const char *tok;
+ SWBuf strongnum;
+ SWBuf strongtense;
+ bool hideText = false;
+ int wordLen = 0;
+ int wordCount = 0;
+
+ const char *from;
+ SWBuf orig = text;
+ from = orig.c_str();
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ wordLen = wordCount;
+ wordCount = 0;
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>') {
+ intoken = false;
+ // process desired tokens
+ // deal with OSIS note tags. Just hide till OSISRTF
+ if (!strncmp(token, "note ", 5)) {
+ hideText = true;
+ }
+ if (!strncmp(token, "/note", 5)) {
+ hideText = false;
+ }
+
+ switch (*token) {
+ case 'w': // OSIS Word (temporary until OSISRTF is done)
+ strongnum = "";
+ strongtense = "";
+ valto = val;
+ num = strstr(token, "lemma=\"x-Strongs:");
+ if (num) {
+ for (num+=17; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ // normal strongs number
+ strongnum += "{\\cf3 \\sub <";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ strongnum += *tok;
+ strongnum += ">}";
+ }
+ /* forget these for now
+ else {
+ // verb morph
+ sprintf(wordstr, "%03d", word-1);
+ module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ }
+ */
+ }
+ else {
+ num = strstr(token, "lemma=\"strong:");
+ if (num) {
+ for (num+=14; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ // normal strongs number
+ strongnum += "{\\cf3 \\sub <";
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++)
+ strongnum += *tok;
+ strongnum += ">}";
+ }
+ /* forget these for now
+ else {
+ // verb morph
+ sprintf(wordstr, "%03d", word-1);
+ module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ }
+ */
+ }
+ }
+ valto = val;
+ num = strstr(token, "morph=\"x-Robinson:");
+ if (num) {
+ for (num+=18; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ // normal robinsons tense
+ strongtense += "{\\cf4 \\sub (";
+ for (tok = val; *tok; tok++)
+ strongtense += *tok;
+ strongtense += ")}";
+ }
+ continue;
+
+ case '/':
+ if (token[1] == 'w') {
+ if ((wordCount > 0) || (strongnum != "{\\cf3 \\sub <3588>}")) {
+ //for (i = 0; i < strongnum.length(); i++)
+ text += strongnum;
+ //for (i = 0; i < strongtense.length(); i++)
+ text += strongtense;
+ }
+ }
+ continue;
+
+ case 'W': // Strongs
+ switch(token[1]) {
+ case 'G': // Greek
+ case 'H': // Hebrew
+ text += "{\\cf3 \\sub <";
+ for (tok = token + 2; *tok; tok++)
+ text += *tok;
+ text += ">}";
+ continue;
+
+ case 'T': // Tense
+ text += "{\\cf4 \\sub (";
+ bool separate = false;
+ for (tok = token + 2; *tok; tok++) {
+ if (separate) {
+ text += "; ";
+ separate = false;
+ }
+ switch (*tok) {
+ case 'G':
+ case 'H':
+ for (tok++; *tok; tok++) {
+ if (isdigit(*tok)) {
+ text += *tok;
+ separate = true;
+ }
+ else {
+ tok--;
+ break;
+ }
+ }
+ break;
+ default:
+ for (; *tok; tok++) {
+ text += *tok;
+ }
+ }
+ }
+ text += ")}";
+ continue;
+ }
+ break;
+ case 'R':
+ switch(token[1]) {
+ case 'X':
+ text += "<a href=\"\">";
+ continue;
+ case 'x':
+ text += "</a>";
+ continue;
+ case 'F': // footnote begin
+ text += "{\\i1 \\sub [ ";
+ continue;
+ case 'f': // footnote end
+ text += " ] }";
+ continue;
+ }
+ break;
+ case 'F': // font tags
+ switch(token[1]) {
+ case 'I': // italic start
+ text += "\\i1 ";
+ continue;
+ case 'i': // italic end
+ text += "\\i0 ";
+ continue;
+ case 'B': // bold start
+ text += "\\b1 ";
+ continue;
+ case 'b': // bold end
+ text += "\\b0 ";
+ continue;
+ case 'N':
+ text += '{';
+ if (!strnicmp(token+2, "Symbol", 6))
+ text += "\\f7 ";
+ if (!strnicmp(token+2, "Courier", 7))
+ text += "\\f8 ";
+ continue;
+ case 'n':
+ text += '}';
+ continue;
+ case 'S':
+ text += "{\\super ";
+ continue;
+ case 's':
+ text += '}';
+ continue;
+ case 'R':
+ text += "{\\cf6 ";
+ continue;
+ case 'r':
+ text += '}';
+ continue;
+ case 'O':
+ case 'C':
+ text += "\\scaps1 ";
+ continue;
+ case 'o':
+ case 'c':
+ text += "\\scaps0 ";
+ continue;
+ case 'V':
+ text += "{\\sub ";
+ continue;
+ case 'v':
+ text += '}';
+ continue;
+ case 'U':
+ text += "\\ul1 ";
+ continue;
+ case 'u':
+ text += "\\ul0 ";
+ continue;
+ }
+ break;
+ case 'C': // special character tags
+ switch(token[1]) {
+ case 'A': // ASCII value
+ text += (char)atoi(&token[2]);
+ continue;
+ case 'G':
+ text += '>';
+ continue;
+ case 'L': // line break
+ text += "\\line ";
+ continue;
+ case 'M': // new paragraph
+ text += "\\par ";
+ continue;
+ case 'T':
+ text += '<';
+ }
+ break;
+ case 'T': // title formatting
+ switch(token[1])
+ {
+ case 'T': // Book title begin
+ text += "{\\large ";
+ continue;
+ case 't':
+ text += '}';
+ continue;
+ case 'S':
+ text += "\\par {\\i1\\b1 ";
+ continue;
+ case 's':
+ text += "}\\par ";
+ continue;
+ }
+ break;
+ case 'J': // Strongs
+ switch(token[1]) {
+ case 'L':
+ text += "\\ql ";
+ case 'C':
+ text += "\\qc ";
+ case 'R':
+ text += "\\qr ";
+ case 'F':
+ text += "\\qj ";
+ }
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else {
+ if (!hideText) {
+ wordCount++;
+ text += *from;
+ }
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
+
diff --git a/src/modules/filters/gbfstrongs.cpp b/src/modules/filters/gbfstrongs.cpp
new file mode 100644
index 0000000..610edb5
--- /dev/null
+++ b/src/modules/filters/gbfstrongs.cpp
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ * gbfstrongs - SWFilter descendant to hide or show strongs number
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gbfstrongs.h>
+#include <swmodule.h>
+#include <ctype.h>
+
+SWORD_NAMESPACE_START
+
+const char oName[] = "Strong's Numbers";
+const char oTip[] = "Toggles Strong's Numbers On and Off if they exist";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+GBFStrongs::GBFStrongs() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+}
+
+
+GBFStrongs::~GBFStrongs() {
+}
+
+
+char GBFStrongs::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ char token[2048]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ bool lastspace = false;
+ int word = 1;
+ char val[128];
+ char wordstr[5];
+ char *valto;
+ unsigned int textStart = 0, textEnd = 0;
+ bool newText = false;
+ SWBuf tmp;
+ const char *from;
+
+ SWBuf orig = text;
+ from = orig.c_str();
+
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ textEnd = text.size();
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ if (*token == 'W' && (token[1] == 'G' || token[1] == 'H')) { // Strongs
+ if (module->isProcessEntryAttributes()) {
+ valto = val;
+ for (unsigned int i = 1; ((token[i]) && (i < 150)); i++)
+ *valto++ = token[i];
+ *valto = 0;
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ // normal strongs number
+ sprintf(wordstr, "%03d", word++);
+ module->getEntryAttributes()["Word"][wordstr]["PartsCount"] = "1";
+ module->getEntryAttributes()["Word"][wordstr]["Lemma"] = val;
+ module->getEntryAttributes()["Word"][wordstr]["LemmaClass"] = "strong";
+ tmp = "";
+ tmp.append(text.c_str()+textStart, (int)(textEnd - textStart));
+ module->getEntryAttributes()["Word"][wordstr]["Text"] = tmp;
+ newText = true;
+ }
+ else {
+ // verb morph
+ sprintf(wordstr, "%03d", word-1);
+ module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ module->getEntryAttributes()["Word"][wordstr]["MorphClass"] = "OLBMorph";
+ }
+ }
+
+ if (!option) {
+ if ((from[1] == ' ') || (from[1] == ',') || (from[1] == ';') || (from[1] == '.') || (from[1] == '?') || (from[1] == '!') || (from[1] == ')') || (from[1] == '\'') || (from[1] == '\"')) {
+ if (lastspace)
+ text--;
+ }
+ if (newText) {textStart = text.size(); newText = false; }
+ continue;
+ }
+ }
+ if (module->isProcessEntryAttributes()) {
+ if ((*token == 'W') && (token[1] == 'T')) { // Morph
+ valto = val;
+ for (unsigned int i = 2; ((token[i]) && (i < 150)); i++)
+ *valto++ = token[i];
+ *valto = 0;
+ sprintf(wordstr, "%03d", word-1);
+ module->getEntryAttributes()["Word"][wordstr]["MorphClass"] = "GBFMorph";
+ module->getEntryAttributes()["Word"][wordstr]["Morph"] = val;
+ newText = true;
+ }
+ }
+ // if not a strongs token, keep token in text
+ text += '<';
+ text += token;
+ text += '>';
+ if (newText) {textStart = text.size(); newText = false; }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else {
+ text += *from;
+ lastspace = (*from == ' ');
+ }
+ }
+ return 0;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfthml.cpp b/src/modules/filters/gbfthml.cpp
new file mode 100644
index 0000000..2664f48
--- /dev/null
+++ b/src/modules/filters/gbfthml.cpp
@@ -0,0 +1,216 @@
+/***************************************************************************
+ gbfthml.cpp - GBF to ThML filter
+ -------------------
+ begin : 1999-10-27
+ copyright : 2001 by CrossWire Bible Society
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdlib.h>
+#include <gbfthml.h>
+#include <swbuf.h>
+
+SWORD_NAMESPACE_START
+
+GBFThML::GBFThML()
+{
+}
+
+
+char GBFThML::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ const char *from;
+ char token[2048];
+ int tokpos = 0;
+ bool intoken = false;
+ const char *tok;
+
+ SWBuf orig = text;
+ from = orig.c_str();
+
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ continue;
+ }
+ if (*from == '>')
+ {
+ intoken = false;
+ // process desired tokens
+ switch (*token) {
+ case 'W': // Strongs
+ switch(token[1]) {
+ case 'G':
+ case 'H':
+ text += "<sync type=\"Strongs\" value=\"";
+ for (tok = token + 1; *tok; tok++)
+ text += *tok;
+ text += "\" />";
+ continue;
+
+ case 'T': // Tense
+ text += "<sync type=\"Morph\" value=\"";
+ for (tok = token + 2; *tok; tok++)
+ text += *tok;
+ text += "\" />";
+ continue;
+ }
+ break;
+ case 'R':
+ switch(token[1])
+ {
+ case 'X':
+ text += "<a href=\"";
+ for (tok = token + 3; *tok; tok++) {
+ if(*tok != '<' && *tok+1 != 'R' && *tok+2 != 'x') {
+ text += *tok;
+ }
+ else {
+ break;
+ }
+ }
+ text += "\">";
+ continue;
+ case 'x':
+ text += "</a>";
+ continue;
+ case 'F': // footnote begin
+ text += "<note>";
+ continue;
+ case 'f': // footnote end
+ text += "</note>";
+ continue;
+ }
+ break;
+ case 'F': // font tags
+ switch(token[1])
+ {
+ case 'N':
+ text += "<font face=\"";
+ for (tok = token + 2; *tok; tok++)
+ text += *tok;
+ text += "\">";
+ continue;
+ case 'n':
+ text += "</font>";
+ continue;
+ case 'I': // italic start
+ text += "<i>";
+ continue;
+ case 'i': // italic end
+ text += "</i>";
+ continue;
+ case 'B': // bold start
+ text += "<b>";
+ continue;
+ case 'b': // bold end
+ text += "</b>";
+ continue;
+
+ case 'R': // words of Jesus begin
+ text += "<font color=\"#ff0000\">";
+ continue;
+ case 'r': // words of Jesus end
+ text += "</font>";
+ continue;
+ case 'U': // Underline start
+ text += "<u>";
+ continue;
+ case 'u': // Underline end
+ text += "</u>";
+ continue;
+ case 'O': // Old Testament quote begin
+ text += "<cite>";
+ continue;
+ case 'o': // Old Testament quote end
+ text += "</cite>";
+ continue;
+ case 'S': // Superscript begin
+ text += "<sup>";
+ continue;
+ case 's': // Superscript end
+ text += "</sup>";
+ continue;
+ case 'V': // Subscript begin
+ text += "<sub>";
+ continue;
+ case 'v': // Subscript end
+ text += "</sub>";
+ continue;
+ }
+ break;
+ case 'C': // special character tags
+ switch(token[1])
+ {
+ case 'A': // ASCII value
+ text += (char)atoi(&token[2]);
+ continue;
+ case 'G':
+ //*to++ = ' ';
+ continue;
+ case 'L': // line break
+ text += "<br /> ";
+ continue;
+ case 'M': // new paragraph
+ text += "<p />";
+ continue;
+ case 'T':
+ //*to++ = ' ';
+ continue;
+ }
+ break;
+ case 'T': // title formatting
+ switch(token[1])
+ {
+ case 'T': // Book title begin
+ text += "<big>";
+ continue;
+ case 't':
+ text += "</big>";
+ continue;
+ case 'S':
+ text += "<div class=\"sechead\">";
+ continue;
+ case 's':
+ text += "</div>";
+ continue;
+ }
+ break;
+
+ case 'P': // special formatting
+ switch(token[1]) {
+ case 'P': // Poetry begin
+ text += "<verse>";
+ continue;
+ case 'p':
+ text += "</verse>";
+ continue;
+ }
+ break;
+ }
+ continue;
+ }
+ if (intoken) {
+ if (tokpos < 2045)
+ token[tokpos++] = *from;
+ token[tokpos+2] = 0;
+ }
+ else text += *from;
+ }
+ return 0;
+}
+
+
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfwebif.cpp b/src/modules/filters/gbfwebif.cpp
new file mode 100644
index 0000000..e651db6
--- /dev/null
+++ b/src/modules/filters/gbfwebif.cpp
@@ -0,0 +1,191 @@
+/***************************************************************************
+ GBFWEBIF.cpp - GBF to HTML filter with hrefs
+ for strongs and morph tags
+ -------------------
+ begin : 2001-09-03
+ copyright : 2001 by CrossWire Bible Society
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <gbfwebif.h>
+#include <ctype.h>
+#include <url.h>
+
+SWORD_NAMESPACE_START
+
+GBFWEBIF::GBFWEBIF() : baseURL(""), passageStudyURL(baseURL + "passagestudy.jsp") {
+//all is done in GBFHTMLHREF since it inherits form this class
+ addTokenSubstitute("FR", "<span class=\"wordsOfJesus\">"); // words of Jesus begin
+ addTokenSubstitute("Fr", "</span>");
+}
+
+bool GBFWEBIF::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+ const char *tok;
+ char val[128];
+ char *valto;
+ const char *num;
+ SWBuf url;
+
+ if (!substituteToken(buf, token)) {
+ if (!strncmp(token, "w", 1)) {
+ // OSIS Word (temporary until OSISRTF is done)
+ valto = val;
+ num = strstr(token, "lemma=\"x-Strongs:");
+ if (num) {
+ for (num+=17; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;";
+ url = "";
+ for (tok = val; *tok; tok++) {
+ url += *tok;
+ }
+ if ((url.length() > 1) && strchr("GH", url[0])) {
+ if (isdigit(url[1]))
+ url = url.c_str()+1;
+ }
+ buf.appendFormatted("<a href=\"%s?showStrong=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++) {
+ buf += *tok;
+ }
+ buf += "</a>&gt;</em></small> ";
+ }
+ }
+ else {
+ num = strstr(token, "lemma=\"strong:");
+ if (num) {
+ for (num+=14; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ buf += " <small><em>&lt;";
+ url = "";
+ for (tok = val; *tok; tok++) {
+ url += *tok;
+ }
+ if ((url.length() > 1) && strchr("GH", url[0])) {
+ if (isdigit(url[1]))
+ url = url.c_str()+1;
+ }
+ buf.appendFormatted("<a href=\"%s?showStrong=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = (!isdigit(*val))?val+1:val; *tok; tok++) {
+ buf += *tok;
+ }
+ buf += "</a>&gt;</em></small> ";
+ }
+ }
+ }
+ valto = val;
+ num = strstr(token, "morph=\"x-Robinson:");
+ if (num) {
+ for (num+=18; ((*num) && (*num != '\"')); num++)
+ *valto++ = *num;
+ *valto = 0;
+ buf += " <small><em>(";
+ url = "";
+ for (tok = val; *tok; tok++) {
+ // normal robinsons tense
+ buf += *tok;
+ }
+ buf.appendFormatted("<a href=\"%s?showMorph=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = val; *tok; tok++) {
+ buf += *tok;
+ }
+ buf += "</a>)</em></small> ";
+ }
+ }
+
+ else if (!strncmp(token, "WG", 2) || !strncmp(token, "WH", 2)) { // strong's numbers
+ buf += " <small><em>&lt;";
+ url = "";
+
+ for (tok = token+1; *tok; tok++) {
+ url += *tok;
+ }
+ if ((url.length() > 1) && strchr("GH", url[0])) {
+ if (isdigit(url[1]))
+ url = url.c_str()+1;
+ }
+ buf.appendFormatted("<a href=\"%s?showStrong=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = token + 2; *tok; tok++) {
+ buf += *tok;
+ }
+ buf += "</a>&gt;</em></small>";
+ }
+
+ else if (!strncmp(token, "WTG", 3) || !strncmp(token, "WTH", 3)) { // strong's numbers tense
+ buf += " <small><em>(";
+ url = "";
+ for (tok = token + 2; *tok; tok++) {
+ if(*tok != '\"')
+ url += *tok;
+ }
+ if ((url.length() > 1) && strchr("GH", url[0])) {
+ if (isdigit(url[1]))
+ url = url.c_str()+1;
+ }
+ buf.appendFormatted("<a href=\"%s?showStrong=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = token + 3; *tok; tok++)
+ if(*tok != '\"')
+ buf += *tok;
+ buf += "</a>)</em></small>";
+ }
+
+ else if (!strncmp(token, "WT", 2) && strncmp(token, "WTH", 3) && strncmp(token, "WTG", 3)) { // morph tags
+ buf += " <small><em>(";
+ for (tok = token + 2; *tok; tok++) {
+ if(*tok != '\"')
+ buf += *tok;
+ }
+ buf.appendFormatted("<a href=\"%s?showMorph=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+
+ for (tok = token + 2; *tok; tok++) {
+ if(*tok != '\"')
+ buf += *tok;
+ }
+ buf += "</a>)</em></small>";
+ }
+
+ else if (!strncmp(token, "RX", 2)) {
+ buf += "<a href=\"";
+ for (tok = token + 3; *tok; tok++) {
+ if(*tok != '<' && *tok+1 != 'R' && *tok+2 != 'x') {
+ buf += *tok;
+ }
+ else {
+ break;
+ }
+ }
+
+ buf.appendFormatted("a href=\"%s?key=%s#cv\">", passageStudyURL.c_str(), URL::encode(url).c_str());
+ }
+ // ok to leave these in
+ else if ((!strncmp(token, "span", 4))
+ || (!strncmp(token, "/span", 5))) {
+ buf.appendFormatted("<%s>", token);
+ }
+
+ else {
+ return GBFHTMLHREF::handleToken(buf, token, userData);
+ }
+ }
+ return true;
+}
+
+SWORD_NAMESPACE_END
diff --git a/src/modules/filters/gbfwordjs.cpp b/src/modules/filters/gbfwordjs.cpp
new file mode 100644
index 0000000..f81ffac
--- /dev/null
+++ b/src/modules/filters/gbfwordjs.cpp
@@ -0,0 +1,282 @@
+/******************************************************************************
+ *
+ * gbfstrongs - SWFilter descendant to hide or show strongs number
+ * in a GBF module.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gbfwordjs.h>
+#include <swmodule.h>
+#include <ctype.h>
+#include <utilstr.h>
+#include <versekey.h>
+
+SWORD_NAMESPACE_START
+
+const char oName[] = "Word Javascript";
+const char oTip[] = "Toggles Word Javascript data";
+
+const SWBuf choices[3] = {"Off", "On", ""};
+const StringList oValues(&choices[0], &choices[2]);
+
+
+GBFWordJS::GBFWordJS() : SWOptionFilter(oName, oTip, &oValues) {
+ setOptionValue("Off");
+
+ defaultGreekLex = 0;
+ defaultHebLex = 0;
+ defaultGreekParse = 0;
+ defaultHebParse = 0;
+ mgr = 0;
+}
+
+
+GBFWordJS::~GBFWordJS() {
+}
+
+
+char GBFWordJS::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
+ if (option) {
+ char token[2112]; // cheese. Fix.
+ int tokpos = 0;
+ bool intoken = false;
+ bool lastspace = false;
+ int word = 1;
+ char val[128];
+ char wordstr[5];
+ unsigned int textStart = 0, lastAppendLen = 0, textEnd = 0;
+ SWBuf tmp;
+ bool newText = false;
+ bool needWordOut = false;
+ AttributeValue *wordAttrs = 0;
+ SWBuf modName = (module)?module->Name():"";
+ SWBuf wordSrcPrefix = modName;
+
+ const SWBuf orig = text;
+ const char * from = orig.c_str();
+ VerseKey *vkey = 0;
+ if (key) {
+ vkey = SWDYNAMIC_CAST(VerseKey, key);
+ }
+
+ for (text = ""; *from; from++) {
+ if (*from == '<') {
+ intoken = true;
+ tokpos = 0;
+ token[0] = 0;
+ token[1] = 0;
+ token[2] = 0;
+ textEnd = text.length();
+ continue;
+ }
+ if (*from == '>') { // process tokens
+ intoken = false;
+ if (*token == 'W' && (token[1] == 'G' || token[1] == 'H')) { // Strongs
+ strcpy(val,token+1);
+ if (atoi((!isdigit(*val))?val+1:val) < 5627) {
+ // normal strongs number
+ sprintf(wordstr, "%03d", word++);
+ needWordOut = (word > 2);
+ wordAttrs = &(module->getEntryAttributes()["Word"][wordstr]);
+ (*wordAttrs)["Lemma"] = val;
+ //printf("Adding: [\"Word\"][%s][\"Strongs\"] = %s\n", wordstr, val);
+ tmp = "";
+ tmp.append(text.c_str()+textStart, (int)(textEnd - textStart));
+ (*wordAttrs)["Text"] = tmp;
+ text.append("</span>");
+ SWBuf ts;
+ ts.appendFormatted("%d", textStart);
+ (*wordAttrs)["TextStart"] = ts;
+ //printf("Adding: [\"Word\"][%s][\"Text\"] = %s\n", wordstr, tmp.c_str());
+ newText = true;
+ }
+ else {
+ // verb morph
+ if (wordAttrs) {
+ (*wordAttrs)["Morph"] = val;
+ }
+ //printf("Adding: [\"Word\"][%s][\"Morph\"] = %s\n", wordstr, val);
+ }
+
+ }
+ if (*token == 'W' && token[1] == 'T') { // Morph
+ if (token[2] == 'G' || token[2] == 'H') {
+ strcpy(val, token+2);
+ }
+ else strcpy(val, token+1);
+ if (wordAttrs) {
+ (*wordAttrs)["Morph"] = val;
+ (*wordAttrs)["MorphClass"] = "StrongsMorph";
+ }
+ newText = true;
+ }
+ // if not a strongs token, keep token in text
+ text += '<';
+ text += token;
+ text += '>';
+ if (needWordOut) {
+ char wstr[10];
+ sprintf(wstr, "%03d", word-2);
+ AttributeValue *wAttrs = &(module->getEntryAttributes()["Word"][wstr]);
+ needWordOut = false;
+ SWBuf strong = (*wAttrs)["Lemma"];
+ SWBuf morph = (*wAttrs)["Morph"];
+ SWBuf morphClass = (*wAttrs)["MorphClass"];
+ SWBuf wordText = (*wAttrs)["Text"];
+ SWBuf textSt = (*wAttrs)["TextStart"];
+ if (strong.size()) {
+ char gh = 0;
+ gh = isdigit(strong[0]) ? 0:strong[0];
+ if (!gh) {
+ if (vkey) {
+ gh = vkey->Testament() ? 'H' : 'G';
+ }
+ }
+ else strong << 1;
+
+ SWModule *sLex = 0;
+ SWModule *sMorph = 0;
+ if (gh == 'G') {
+ sLex = defaultGreekLex;
+ sMorph = defaultGreekParse;
+ }
+ if (gh == 'H') {
+ sLex = defaultHebLex;
+ sMorph = defaultHebParse;
+ }
+ SWBuf lexName = "";
+ if (sLex) {
+ // we can pass the real lex name in, but we have some
+ // aliases in the javascript to optimize bandwidth
+ lexName = sLex->Name();
+ if (lexName == "StrongsGreek")
+ lexName = "G";
+ if (lexName == "StrongsHebrew")
+ lexName = "H";
+ }
+ SWBuf wordID;
+ if (vkey) {
+ // optimize for bandwidth and use only the verse as the unique entry id
+ wordID.appendFormatted("%d", vkey->Verse());
+ }
+ else {
+ wordID = key->getText();
+ }
+ for (unsigned int i = 0; i < wordID.size(); i++) {
+ if ((!isdigit(wordID[i])) && (!isalpha(wordID[i]))) {
+ wordID[i] = '_';
+ }
+ }
+ wordID.appendFormatted("_%s%d", wordSrcPrefix.c_str(), atoi(wstr));
+ if (textSt.size()) {
+ int textStr = atoi(textSt.c_str());
+ textStr += lastAppendLen;
+ SWBuf spanStart = "";
+
+
+
+/*
+ if (sMorph) {
+ SWBuf popMorph = "<a onclick=\"";
+ popMorph.appendFormatted("p(\'%s\',\'%s\','%s','');\" >%s</a>", sMorph->Name(), morph.c_str(), wordID.c