1 /******************************************************************************
2 *
3 * versekey.cpp - code for class 'VerseKey'- a standard Biblical
4 * verse key
5 *
6 * $Id: versekey.cpp 3439 2016-10-23 08:32:02Z scribe $
7 *
8 * Copyright 1998-2013 CrossWire Bible Society (http://www.crosswire.org)
9 * CrossWire Bible Society
10 * P. O. Box 2528
11 * Tempe, AZ 85280-2528
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation version 2.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 */
23
24
25 #include <swmacs.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 #include <utilstr.h>
32 #include <stringmgr.h>
33 #include <swkey.h>
34 #include <swlog.h>
35 #include <versekey.h>
36 #include <swlocale.h>
37 #include <roman.h>
38 #include <versificationmgr.h>
39
40 SWORD_NAMESPACE_START
41
42 static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0};
43 SWClass VerseKey::classdef(classes);
44
45 /******************************************************************************
46 * Initialize static members of VerseKey
47 */
48
49 int VerseKey::instance = 0;
50
51
52 /******************************************************************************
53 * VerseKey::init - initializes instance of VerseKey
54 */
55
init(const char * v11n)56 void VerseKey::init(const char *v11n) {
57 myclass = &classdef;
58
59 instance++;
60 autonorm = 1; // default auto normalization to true
61 intros = false; // default display intros option is false
62 upperBound = 0;
63 lowerBound = 0;
64 boundSet = false;
65 testament = 1;
66 book = 1;
67 chapter = 1;
68 verse = 1;
69 suffix = 0;
70 tmpClone = 0;
71 refSys = 0;
72
73 setVersificationSystem(v11n);
74 }
75
76 /******************************************************************************
77 * VerseKey Constructor - initializes instance of VerseKey
78 *
79 * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'. See
80 * VerseKey::parse for more detailed information)
81 */
82
VerseKey(const SWKey & ikey)83 VerseKey::VerseKey(const SWKey &ikey) : SWKey(ikey)
84 {
85 init();
86 copyFrom(ikey);
87 }
88
89
VerseKey(const SWKey * ikey)90 VerseKey::VerseKey(const SWKey *ikey) : SWKey(*ikey)
91 {
92 init();
93 if (ikey)
94 copyFrom(*ikey);
95 }
96
97
98 /******************************************************************************
99 * VerseKey Constructor - initializes instance of VerseKey
100 *
101 * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'. See
102 * VerseKey::parse for more detailed information)
103 */
104
VerseKey(const char * ikeyText)105 VerseKey::VerseKey(const char *ikeyText) : SWKey(ikeyText)
106 {
107 init();
108 if (ikeyText)
109 parse();
110 }
111
112
VerseKey(VerseKey const & k)113 VerseKey::VerseKey(VerseKey const &k) : SWKey(k)
114 {
115 init();
116 copyFrom(k);
117 }
118
119
120 /******************************************************************************
121 * VerseKey::setFromOther - Positions this VerseKey to another VerseKey
122 */
123
setFromOther(const VerseKey & ikey)124 void VerseKey::setFromOther(const VerseKey &ikey) {
125 if (refSys == ikey.refSys) {
126 testament = ikey.getTestament();
127 book = ikey.getBook();
128 chapter = ikey.getChapter();
129 verse = ikey.getVerse();
130 suffix = ikey.getSuffix();
131 }
132 else {
133 // map verse between systems
134 const char* map_book = ikey.getOSISBookName();
135 int map_chapter = ikey.getChapter();
136 int map_verse = ikey.getVerse();
137 int map_range = map_verse;
138
139 ikey.refSys->translateVerse(refSys, &map_book, &map_chapter, &map_verse, &map_range);
140 //printf("verse: %s.%i.%i-%i\n",map_book,map_chapter,map_verse,map_range);
141
142 book = refSys->getBookNumberByOSISName(map_book);
143
144 // check existence
145 if (book == -1) {
146 book = 1;
147 error = KEYERR_OUTOFBOUNDS;
148 }
149 else if (refSys->getBook(book-1)->getChapterMax() < map_chapter) {
150 map_chapter = refSys->getBook(book-1)->getChapterMax();
151 map_verse = refSys->getBook(book-1)->getVerseMax(map_chapter);
152 error = KEYERR_OUTOFBOUNDS;
153 }
154 else if (map_chapter > 0 && refSys->getBook(book-1)->getVerseMax(map_chapter) < map_verse) {
155 map_verse = refSys->getBook(book-1)->getVerseMax(map_chapter);
156 error = KEYERR_OUTOFBOUNDS;
157 }
158
159 // set values
160 if (book > BMAX[0])
161 book -= BMAX[0], testament = 2;
162 else
163 testament = 1;
164
165 //if (map_verse == 0) Headings(1);
166
167 chapter = map_chapter;
168 verse = map_verse;
169 suffix = ikey.getSuffix();
170
171 if (map_verse < map_range) {
172 if (map_range > refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getVerseMax(chapter))
173 ++map_range;
174 verse = map_range;
175 setUpperBound(this);
176 verse = map_verse;
177 setLowerBound(this);
178 }
179 }
180 }
181
182
positionFrom(const SWKey & ikey)183 void VerseKey::positionFrom(const SWKey &ikey) {
184 error = 0;
185 const SWKey *fromKey = &ikey;
186 ListKey *tryList = SWDYNAMIC_CAST(ListKey, fromKey);
187 if (tryList) {
188 SWKey *k = tryList->getElement();
189 if (k) fromKey = k;
190 }
191 VerseKey *tryVerse = SWDYNAMIC_CAST(VerseKey, fromKey);
192 if (tryVerse) {
193 setFromOther(*tryVerse);
194 }
195 else {
196 SWKey::positionFrom(*fromKey);
197 // extraneous parse which inadvertently clears error flag
198 // SWKey::positionFrom already calls copyFrom which calls setText, which VerseKey::setText already calls parse()
199 // parse();
200 }
201
202 // should we always perform bounds checks? Tried but seems to cause infinite recursion
203 if (_compare(getUpperBound()) > 0) {
204 setFromOther(getUpperBound());
205 error = KEYERR_OUTOFBOUNDS;
206 }
207 if (_compare(getLowerBound()) < 0) {
208 setFromOther(getLowerBound());
209 error = KEYERR_OUTOFBOUNDS;
210 }
211 }
212
213
214 /******************************************************************************
215 * VerseKey::copyFrom - Equates this VerseKey to another VerseKey
216 */
217
copyFrom(const VerseKey & ikey)218 void VerseKey::copyFrom(const VerseKey &ikey) {
219 autonorm = ikey.autonorm;
220 intros = ikey.intros;
221 testament = ikey.getTestament();
222 book = ikey.getBook();
223 chapter = ikey.getChapter();
224 verse = ikey.getVerse();
225 suffix = ikey.getSuffix();
226 setLocale(ikey.getLocale());
227 setVersificationSystem(ikey.getVersificationSystem());
228 if (ikey.isBoundSet()) {
229 setLowerBound(ikey.getLowerBound());
230 setUpperBound(ikey.getUpperBound());
231 }
232 }
233
234
235 /******************************************************************************
236 * VerseKey::copyFrom - Equates this VerseKey to another SWKey
237 */
238
copyFrom(const SWKey & ikey)239 void VerseKey::copyFrom(const SWKey &ikey) {
240 // check to see if we can do a more specific copy
241 // plus some optimizations
242 const SWKey *fromKey = &ikey;
243 ListKey *tryList = SWDYNAMIC_CAST(ListKey, fromKey);
244 if (tryList) {
245 SWKey *k = tryList->getElement();
246 if (k) fromKey = k;
247 }
248 VerseKey *tryVerse = SWDYNAMIC_CAST(VerseKey, fromKey);
249 if (tryVerse) {
250 copyFrom(*tryVerse);
251 }
252 else {
253 SWKey::copyFrom(*fromKey);
254 // extraneous parse which inadvertently clears error flag
255 // SWKey::copyFrom already calls setText, which VerseKey::setText already calls parse()
256 // parse();
257 }
258 }
259
260
VerseKey(const char * min,const char * max,const char * v11n)261 VerseKey::VerseKey(const char *min, const char *max, const char *v11n) : SWKey()
262 {
263 init(v11n);
264 ListKey tmpListKey = parseVerseList(min);
265 if (tmpListKey.getCount()) {
266 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.getElement(0));
267 setLowerBound(*newElement);
268 }
269 tmpListKey = parseVerseList(max, min, true);
270 if (tmpListKey.getCount()) {
271 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.getElement(0));
272 setUpperBound((newElement->isBoundSet())?newElement->getUpperBound():*newElement);
273 }
274 setPosition(TOP);
275 }
276
277
clone() const278 SWKey *VerseKey::clone() const
279 {
280 return new VerseKey(*this);
281 }
282
283
284 /******************************************************************************
285 * VerseKey Destructor - cleans up instance of VerseKey
286 *
287 * ENT: ikey - text key
288 */
289
~VerseKey()290 VerseKey::~VerseKey() {
291
292 delete tmpClone;
293
294 --instance;
295 }
296
297
setVersificationSystem(const char * name)298 void VerseKey::setVersificationSystem(const char *name) {
299 const VersificationMgr::System *newRefSys = VersificationMgr::getSystemVersificationMgr()->getVersificationSystem(name);
300 // TODO: cheese, but what should we do if requested v11n system isn't found?
301 if (!newRefSys) newRefSys = VersificationMgr::getSystemVersificationMgr()->getVersificationSystem("KJV");
302 if (refSys != newRefSys) {
303 refSys = newRefSys;
304 BMAX[0] = refSys->getBMAX()[0];
305 BMAX[1] = refSys->getBMAX()[1];
306
307 // TODO: adjust bounds for versificaion system ???
308 // TODO: when we have mapping done, rethink this
309 //necessary as our bounds might not mean anything in the new v11n system
310 clearBounds();
311 }
312
313 }
314
315
getVersificationSystem() const316 const char *VerseKey::getVersificationSystem() const { return refSys->getName(); }
317
318
319
320 /******************************************************************************
321 * VerseKey::parse - parses keytext into testament|book|chapter|verse
322 *
323 * RET: error status
324 */
325
parse(bool checkAutoNormalize)326 char VerseKey::parse(bool checkAutoNormalize)
327 {
328 testament = BMAX[1]?2:1;
329 book = BMAX[BMAX[1]?1:0];
330 chapter = 1;
331 verse = 1;
332
333 int error = 0;
334
335 if (keytext) {
336 // pass our own copy of keytext as keytext memory may be freshed during parse
337 ListKey tmpListKey = parseVerseList(SWBuf(keytext).c_str());
338 if (tmpListKey.getCount()) {
339 this->positionFrom(*tmpListKey.getElement(0));
340 error = this->error;
341 } else error = 1;
342 }
343 if (checkAutoNormalize) {
344 normalize(true);
345 }
346 freshtext();
347
348 return (this->error) ? this->error : (this->error = error);
349 }
350
351
352 /******************************************************************************
353 * VerseKey::freshtext - refreshes keytext based on
354 * testament|book|chapter|verse
355 */
356
freshtext() const357 void VerseKey::freshtext() const
358 {
359 char buf[2024];
360 int realTest = testament;
361 int realbook = book;
362
363 if (book < 1) {
364 if (testament < 1)
365 sprintf(buf, "[ Module Heading ]");
366 else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
367 }
368 else {
369 if (realbook > BMAX[realTest-1]) {
370 realbook -= BMAX[realTest-1];
371 if (realTest < 2)
372 realTest++;
373 if (realbook > BMAX[realTest-1])
374 realbook = BMAX[realTest-1];
375 }
376 sprintf(buf, "%s %d:%d", getBookName(), chapter, verse);
377 if (suffix) {
378 buf[strlen(buf)+1] = 0;
379 buf[strlen(buf)] = suffix;
380 }
381 }
382
383 stdstr((char **)&keytext, buf);
384 }
385
386
387
388 /************************************************************************
389 * VerseKey::getBookAbbrev - Attempts to find a book no from a name or
390 * abbreviation
391 *
392 * ENT: abbr - key for which to search;
393 * RET: book number or < 0 = not valid
394 */
395
getBookFromAbbrev(const char * iabbr) const396 int VerseKey::getBookFromAbbrev(const char *iabbr) const
397 {
398 int diff, abLen, min, max, target, retVal = -1;
399
400 char *abbr = 0;
401
402 int abbrevsCnt;
403
404 const struct abbrev *abbrevs = getPrivateLocale()->getBookAbbrevs(&abbrevsCnt);
405
406 StringMgr* stringMgr = StringMgr::getSystemStringMgr();
407 const bool hasUTF8Support = StringMgr::hasUTF8Support();
408
409 // The first iteration of this loop tries to uppercase
410 // the string. If we still fail to match, we try
411 // matching the string without any toupper logic.
412 // This is useful for, say, Chinese user input
413 // on a system that doesn't properly support
414 // a true Unicode-toupper function (!hasUTF8Support)
415 for (int i = 0; i < 2; i++) {
416 stdstr(&abbr, iabbr, 2);
417 strstrip(abbr);
418
419 if (!i) {
420 if (hasUTF8Support) { //we have support for UTF-8 handling; we expect UTF-8 encoded locales
421 stringMgr->upperUTF8(abbr, (unsigned int)(strlen(abbr)*2));
422 }
423 else {
424 stringMgr->upperLatin1(abbr);
425 }
426 }
427
428 abLen = (int)strlen(abbr);
429
430 if (abLen) {
431 min = 0;
432 max = abbrevsCnt;
433
434 // binary search for a match
435 while(1) {
436 target = min + ((max - min) / 2);
437 diff = strncmp(abbr, abbrevs[target].ab, abLen);
438 if ((!diff)||(target >= max)||(target <= min))
439 break;
440 if (diff > 0)
441 min = target;
442 else max = target;
443 }
444
445 // lets keep backing up till we find the 'first' valid match
446 for (; target > 0; target--) {
447 if (strncmp(abbr, abbrevs[target-1].ab, abLen))
448 break;
449 }
450
451 if (!diff) {
452 // lets keep moving forward till we find an abbrev in our refSys
453 retVal = refSys->getBookNumberByOSISName(abbrevs[target].osis);
454 while ((retVal < 0) && (target < max) && (!strncmp(abbr, abbrevs[target+1].ab, abLen))) {
455 target++;
456 retVal = refSys->getBookNumberByOSISName(abbrevs[target].osis);
457 }
458 }
459 else retVal = -1;
460 }
461 if (retVal > 0)
462 break;
463 }
464 delete [] abbr;
465 return retVal;
466 }
467
468
469 /******************************************************************************
470 * VerseKey::validateCurrentLocale - be sure a locale book abbrevs set is complete
471 *
472 */
validateCurrentLocale() const473 void VerseKey::validateCurrentLocale() const {
474 if (SWLog::getSystemLog()->getLogLevel() >= SWLog::LOG_DEBUG) { //make sure log is wanted, this loop stuff costs a lot of time
475 for (int i = 0; i < refSys->getBookCount(); i++) {
476 const int bn = getBookFromAbbrev(getPrivateLocale()->translate(refSys->getBook(i)->getLongName()));
477 if (bn != i+1) {
478 char *abbr = 0;
479 stdstr(&abbr, getPrivateLocale()->translate(refSys->getBook(i)->getLongName()), 2);
480 strstrip(abbr);
481 SWLog::getSystemLog()->logDebug("VerseKey::Book: %s does not have a matching toupper abbrevs entry! book number returned was: %d, should be %d. Required entry to add to locale:", abbr, bn, i);
482
483 StringMgr* stringMgr = StringMgr::getSystemStringMgr();
484 const bool hasUTF8Support = StringMgr::hasUTF8Support();
485 if (hasUTF8Support) { //we have support for UTF-8 handling; we expect UTF-8 encoded locales
486 stringMgr->upperUTF8(abbr, (unsigned int)(strlen(abbr)*2));
487 }
488 else {
489 stringMgr->upperLatin1(abbr);
490 }
491 SWLog::getSystemLog()->logDebug("%s=%s\n", abbr, refSys->getBook(i)->getOSISName());
492 delete [] abbr;
493 }
494 }
495 }
496 }
497
498
499 /******************************************************************************
500 * VerseKey::parseVerseList - Attempts to parse a buffer into separate
501 * verse entries returned in a ListKey
502 *
503 * ENT: buf - buffer to parse;
504 * defaultKey - if verse, chap, book, or testament is left off,
505 * pull info from this key (ie. Gen 2:3; 4:5;
506 * Gen would be used when parsing the 4:5 section)
507 * expandRange - whether or not to expand eg. John 1:10-12 or just
508 * save John 1:10
509 *
510 * RET: ListKey reference filled with verse entries contained in buf
511 *
512 * COMMENT: This code works but wreaks. Rewrite to make more maintainable.
513 */
514
parseVerseList(const char * buf,const char * defaultKey,bool expandRange,bool useChapterAsVerse)515 ListKey VerseKey::parseVerseList(const char *buf, const char *defaultKey, bool expandRange, bool useChapterAsVerse) {
516
517 // hold on to our own copy of params, as threads/recursion may change outside values
518 const char *bufStart = buf;
519 SWBuf iBuf = buf;
520 buf = iBuf.c_str();
521 SWBuf iDefaultKey = defaultKey;
522 if (defaultKey) defaultKey = iDefaultKey.c_str();
523
524 char book[2048]; // TODO: bad, remove
525 char number[2048]; // TODO: bad, remove
526 *book = 0;
527 *number = 0;
528 int tobook = 0;
529 int tonumber = 0;
530 char suffix = 0;
531 int chap = -1, verse = -1;
532 int bookno = 0;
533 int loop;
534 char comma = 0;
535 char dash = 0;
536 const char *orig = buf;
537 int q;
538 ListKey tmpListKey;
539 ListKey internalListKey;
540 char lastPartial = 0;
541 bool inTerm = true;
542 int notAllDigits = 0;
543 bool doubleF = false;
544
545 // assert we have a buffer
546 if (!buf) return internalListKey;
547
548 VerseKey *curKey = (VerseKey *)this->clone();
549 VerseKey *lastKey = (VerseKey *)this->clone();
550 lastKey->clearBounds();
551 curKey->clearBounds();
552
553 // some silly checks for corner cases
554 if (!strcmp(buf, "[ Module Heading ]")) {
555 curKey->setVerse(0);
556 curKey->setChapter(0);
557 curKey->setBook(0);
558 curKey->setTestament(0);
559 lastKey->setLowerBound(*curKey);
560 lastKey->setUpperBound(*curKey);
561 internalListKey << *lastKey;
562 delete curKey;
563 delete lastKey;
564 return internalListKey;
565 }
566 if ((!strncmp(buf, "[ Testament ", 12)) &&
567 (isdigit(buf[12])) &&
568 (!strcmp(buf+13, " Heading ]"))) {
569 curKey->setVerse(0);
570 curKey->setChapter(0);
571 curKey->setBook(0);
572 curKey->setTestament(buf[12]-48);
573 lastKey->setLowerBound(*curKey);
574 lastKey->setUpperBound(*curKey);
575 internalListKey << *lastKey;
576 delete curKey;
577 delete lastKey;
578 return internalListKey;
579 }
580
581 curKey->setAutoNormalize(isAutoNormalize());
582 lastKey->setAutoNormalize(false);
583 if (defaultKey) *lastKey = defaultKey;
584
585 while (*buf) {
586 switch (*buf) {
587 case ':':
588 if (buf[1] != ' ') { // for silly "Mat 1:1: this verse...."
589 number[tonumber] = 0;
590 tonumber = 0;
591 if (*number) {
592 if (chap >= 0)
593 verse = atoi(number);
594 else chap = atoi(number);
595 }
596 *number = 0;
597 comma = 0;
598 break;
599 }
600 goto terminate_range;
601 // otherwise drop down to next case
602 case ' ':
603 inTerm = true;
604 while (true) {
605 if ((!*number) || (chap < 0))
606 break;
607 for (q = 1; ((buf[q]) && (buf[q] != ' ')); q++);
608 if (buf[q] == ':')
609 break;
610 inTerm = false;
611 break;
612 }
613 if (inTerm) {
614 if (tobook < 1 || book[tobook-1] != ' ') {
615 book[tobook++] = ' ';
616 }
617 break;
618 }
619
620 case '-':
621 if (chap == -1) {
622 book[tobook] = *buf;
623 book[tobook+1] = *(buf+1);
624 book[tobook+2] = 0;
625 int bookno = getBookFromAbbrev(book);
626 if (bookno > -1) {
627 tobook++;
628 buf++;
629 break;
630 }
631 }
632 case ',': // on number new verse
633 case ';': // on number new chapter
634 terminate_range:
635 number[tonumber] = 0;
636 tonumber = 0;
637 if (*number) {
638 if (chap >= 0)
639 verse = atoi(number);
640 else chap = atoi(number);
641 }
642 *number = 0;
643 book[tobook] = 0;
644 tobook = 0;
645 bookno = -1;
646 if (*book) {
647 loop = (int)strlen(book) - 1;
648
649 for (; loop+1; loop--) { if (book[loop] == ' ') book[loop] = 0; else break; }
650
651 if (loop > 0 && isdigit(book[loop-1]) && book[loop] >= 'a' && book[loop] <= 'z') {
652 book[loop--] = 0;
653 }
654 for (; loop+1; loop--) {
655 if ((isdigit(book[loop])) || (book[loop] == ' ')) {
656 book[loop] = 0;
657 continue;
658 }
659 else {
660 if ((SW_toupper(book[loop])=='F')&&(loop)) {
661 if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
662 book[loop] = 0;
663 continue;
664 }
665 }
666 }
667 break;
668 }
669
670 for (loop = (int)strlen(book) - 1; loop+1; loop--) {
671 if (book[loop] == ' ') {
672 // "PS C" is ok, but "II C" is not ok
673 if (isroman(&book[loop+1]) && !isroman(book,loop)) {
674 if (verse == -1) {
675 verse = chap;
676 chap = from_rom(&book[loop+1]);
677 book[loop] = 0;
678 }
679 }
680 break;
681 }
682 }
683
684 // check for special inscriptio and subscriptio which are saved as book intro and chap 1 intro (for INTF)
685 for (loop = (int)strlen(book) - 1; loop+1; loop--) {
686 if (book[loop] == ' ') {
687 if (!strnicmp(&book[loop+1], "inscriptio", (int)strlen(&book[loop+1]))) {
688 book[loop] = 0;
689 verse = 0;
690 chap = 0;
691 }
692 else if (!strnicmp(&book[loop+1], "subscriptio", (int)strlen(&book[loop+1]))) {
693 book[loop] = 0;
694 verse = 0;
695 chap = 1;
696 }
697 break;
698 }
699 }
700
701 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev
702 if (verse == -1) {
703 verse = chap;
704 chap = lastKey->getChapter();
705 *book = 0;
706 }
707 }
708 if ((!stricmp(book, "ch")) || (!stricmp(book, "chap"))) { // Verse abbrev
709 strcpy(book, lastKey->getBookName());
710 }
711 bookno = getBookFromAbbrev(book);
712 if ((bookno > -1) && (suffix == 'f') && (book[strlen(book)-1] == 'f')) {
713 suffix = 0;
714 }
715 }
716 if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
717 char partial = 0;
718 curKey->setVerse(1);
719 curKey->setChapter(1);
720 curKey->setBook(1);
721
722 if (bookno < 0) {
723 curKey->setTestament(lastKey->getTestament());
724 curKey->setBook(lastKey->getBook());
725 }
726 else {
727 int t = 1;
728 if (bookno > BMAX[0]) {
729 t++;
730 bookno -= BMAX[0];
731 }
732 curKey->setTestament(t);
733 curKey->setBook(bookno);
734 }
735
736
737 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
738 // if (comma) {
739 curKey->setChapter(lastKey->getChapter());
740 curKey->setVerse(chap); // chap because this is the first number captured
741 if (suffix) {
742 curKey->setSuffix(suffix);
743 }
744 }
745 else {
746 if (useChapterAsVerse && verse < 0 && chap > 0 && curKey->getChapterMax() == 1) {
747 verse = chap;
748 chap = 1;
749 }
750
751
752 if (chap >= 0) {
753 curKey->setChapter(chap);
754 }
755 else {
756 partial++;
757 curKey->setChapter(1);
758 }
759 if (verse >= 0) {
760 curKey->setVerse(verse);
761 if (suffix) {
762 curKey->setSuffix(suffix);
763 }
764 }
765 else {
766 partial++;
767 curKey->setVerse(1);
768 }
769 }
770
771 // check for '-'
772 for (q = 0; ((buf[q]) && (buf[q] == ' ')); q++);
773 if ((buf[q] == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper
774 buf+=q;
775 lastKey->setLowerBound(*curKey);
776 lastKey->setPosition(TOP);
777 tmpListKey << *lastKey;
778 ((VerseKey *)tmpListKey.getElement())->setAutoNormalize(isAutoNormalize());
779 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
780 }
781 else {
782 if (!dash) { // if last separator was not a dash just add
783 if (expandRange && partial) {
784 lastKey->setLowerBound(*curKey);
785 if (partial > 1)
786 curKey->setPosition(MAXCHAPTER);
787 if (partial > 0)
788 *curKey = MAXVERSE;
789 lastKey->setUpperBound(*curKey);
790 *lastKey = TOP;
791 tmpListKey << *lastKey;
792 ((VerseKey *)tmpListKey.getElement())->setAutoNormalize(isAutoNormalize());
793 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
794 }
795 else {
796 bool f = false;
797 if (curKey->getSuffix() == 'f') {
798 curKey->setSuffix(0);
799 f = true;
800 }
801 lastKey->setLowerBound(*curKey);
802 if (f && doubleF) (*curKey) = MAXVERSE;
803 else if (f) (*curKey)++;
804 lastKey->setUpperBound(*curKey);
805 *lastKey = TOP;
806 tmpListKey << *lastKey;
807 ((VerseKey *)tmpListKey.getElement())->setAutoNormalize(isAutoNormalize());
808 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
809 }
810 }
811 else if (expandRange) {
812 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.getElement());
813 if (newElement) {
814 if (partial > 1)
815 *curKey = MAXCHAPTER;
816 if (partial > 0)
817 *curKey = MAXVERSE;
818 newElement->setUpperBound(*curKey);
819 *lastKey = *curKey;
820 *newElement = TOP;
821 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
822 }
823 }
824 }
825 lastPartial = partial;
826 }
827 *book = 0;
828 chap = -1;
829 verse = -1;
830 suffix = 0;
831 if (*buf == ',')
832 comma = 1;
833 else comma = 0;
834 if (*buf == '-')
835 dash = 1;
836 else dash = 0;
837 break;
838 case 10: // ignore these
839 case 13:
840 case '[':
841 case ']':
842 case '(':
843 case ')':
844 case '{':
845 case '}':
846 break;
847 case '.':
848 if (buf > orig) { // ignore (break) if preceeding char is not a digit
849 for (notAllDigits = tobook; notAllDigits; notAllDigits--) {
850 if ((!isdigit(book[notAllDigits-1])) && (!strchr(" .", book[notAllDigits-1])))
851 break;
852 }
853 if (!notAllDigits && !isdigit(buf[1]))
854 break;
855 }
856
857 number[tonumber] = 0;
858 tonumber = 0;
859 if (*number) {
860 if (chap >= 0)
861 verse = atoi(number);
862 else chap = atoi(number);
863 *number = 0;
864 }
865 else if (chap == -1 && (tobook < 1 || book[tobook-1] != ' ')) {
866 book[tobook++] = ' ';
867 }
868
869 break;
870
871 default:
872 if (isdigit(*buf)) {
873 number[tonumber++] = *buf;
874 suffix = 0;
875 doubleF = 0;
876 }
877 else {
878 switch (*buf) {
879 case ' ': // ignore these and don't reset number
880 case 'F':
881 break;
882 default:
883 // suffixes (and oddly 'f'-- ff.)
884 if ((*buf >= 'a' && *buf <= 'z' && (chap >=0 || bookno > -1 || lastKey->isBoundSet()))
885 || *buf == 'f') {
886 // if suffix is already an 'f', then we need to mark if we're doubleF.
887 doubleF = (*buf == 'f' && suffix == 'f');
888 if (suffix && !doubleF) {
889 // we've already had a suffix one, so this is another letter, thus any number is not a number, e.g., '2jn'. We're on 'n'
890 number[tonumber] = 0;
891 tonumber = 0;
892 }
893 suffix = *buf;
894 }
895 else {
896 number[tonumber] = 0;
897 tonumber = 0;
898 }
899 break;
900 }
901 }
902 if (chap == -1)
903 book[tobook++] = *buf;
904 }
905 buf++;
906 }
907 number[tonumber] = 0;
908 tonumber = 0;
909 if (*number) {
910 if (chap >= 0)
911 verse = atoi(number);
912 else chap = atoi(number);
913 }
914 *number = 0;
915 book[tobook] = 0;
916 tobook = 0;
917 if (*book) {
918 loop = (int)strlen(book) - 1;
919
920 // strip trailing spaces
921 for (; loop+1; loop--) { if (book[loop] == ' ') book[loop] = 0; else break; }
922
923 // check if endsWith([0-9][a-z]) and kill the last letter, e.g., ...12a, and chop off the 'a'
924 // why? What's this for? wouldn't this mess up 2t?
925 if (loop > 0 && isdigit(book[loop-1]) && book[loop] >= 'a' && book[loop] <= 'z') {
926 book[loop--] = 0;
927 }
928
929 // skip trailing spaces and numbers
930 for (; loop+1; loop--) {
931 if ((isdigit(book[loop])) || (book[loop] == ' ')) {
932 book[loop] = 0;
933 continue;
934 }
935 else {
936 if ((SW_toupper(book[loop])=='F')&&(loop)) {
937 if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
938 book[loop] = 0;
939 continue;
940 }
941 }
942 }
943 break;
944 }
945
946 // check for roman numeral chapter
947 for (loop = (int)strlen(book) - 1; loop+1; loop--) {
948 if (book[loop] == ' ') {
949 // "PS C" is ok, but "II C" is not ok
950 if (isroman(&book[loop+1]) && !isroman(book,loop)) {
951 if (verse == -1) {
952 verse = chap;
953 chap = from_rom(&book[loop+1]);
954 book[loop] = 0;
955 }
956 }
957 break;
958 }
959 }
960 // check for special inscriptio and subscriptio which are saved as book intro and chap 1 intro (for INTF)
961 for (loop = (int)strlen(book) - 1; loop+1; loop--) {
962 if (book[loop] == ' ') {
963 if (!strnicmp(&book[loop+1], "inscriptio", (int)strlen(&book[loop+1]))) {
964 book[loop] = 0;
965 verse = 0;
966 chap = 0;
967 suffix = 0;
968 }
969 else if (!strnicmp(&book[loop+1], "subscriptio", (int)strlen(&book[loop+1]))) {
970 book[loop] = 0;
971 verse = 0;
972 chap = 1;
973 suffix = 0;
974 }
975 break;
976 }
977 }
978
979 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev.
980 if (verse == -1) {
981 verse = chap;
982 chap = lastKey->getChapter();
983 *book = 0;
984 }
985 }
986
987 if ((!stricmp(book, "ch")) || (!stricmp(book, "chap"))) { // Verse abbrev
988 strcpy(book, lastKey->getBookName());
989 }
990 bookno = getBookFromAbbrev(book);
991 if ((bookno > -1) && (suffix == 'f') && (book[strlen(book)-1] == 'f')) {
992 suffix = 0;
993 }
994 }
995 if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
996 char partial = 0;
997 curKey->setVerse(1);
998 curKey->setChapter(1);
999 curKey->setBook(1);
1000
1001 if (bookno < 0) {
1002 curKey->setTestament(lastKey->getTestament());
1003 curKey->setBook(lastKey->getBook());
1004 }
1005 else {
1006 int t = 1;
1007 if (bookno > BMAX[0]) {
1008 t++;
1009 bookno -= BMAX[0];
1010 }
1011 curKey->setTestament(t);
1012 curKey->setBook(bookno);
1013 }
1014
1015 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
1016 curKey->setChapter(lastKey->getChapter());
1017 curKey->setVerse(chap); // chap because this is the first number captured
1018 if (suffix) {
1019 curKey->setSuffix(suffix);
1020 }
1021 }
1022 else {
1023 if (useChapterAsVerse && verse < 0 && chap > 0 && curKey->getChapterMax() == 1) {
1024 verse = chap;
1025 chap = 1;
1026 }
1027
1028
1029 if (chap >= 0) {
1030 curKey->setChapter(chap);
1031 }
1032 else {
1033 partial++;
1034 curKey->setChapter(1);
1035 }
1036 if (verse >= 0) {
1037 curKey->setVerse(verse);
1038 if (suffix) {
1039 curKey->setSuffix(suffix);
1040 }
1041 }
1042 else {
1043 partial++;
1044 curKey->setVerse(1);
1045 }
1046 }
1047
1048 if ((*buf == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper
1049 lastKey->setLowerBound(*curKey);
1050 *lastKey = TOP;
1051 tmpListKey << *lastKey;
1052 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
1053 }
1054 else {
1055 if (!dash) { // if last separator was not a dash just add
1056 if (expandRange && partial) {
1057 lastKey->setLowerBound(*curKey);
1058 if (partial > 1)
1059 *curKey = MAXCHAPTER;
1060 if (partial > 0)
1061 *curKey = MAXVERSE;
1062 lastKey->setUpperBound(*curKey);
1063 *lastKey = TOP;
1064 tmpListKey << *lastKey;
1065 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
1066 }
1067 else {
1068 bool f = false;
1069 if (curKey->getSuffix() == 'f') {
1070 curKey->setSuffix(0);
1071 f = true;
1072 }
1073 lastKey->setLowerBound(*curKey);
1074 if (f && doubleF) (*curKey) = MAXVERSE;
1075 else if (f) (*curKey)++;
1076 lastKey->setUpperBound(*curKey);
1077 *lastKey = TOP;
1078 tmpListKey << *lastKey;
1079 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
1080 }
1081 }
1082 else if (expandRange) {
1083 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.getElement());
1084 if (newElement) {
1085 if (partial > 1)
1086 *curKey = MAXCHAPTER;
1087 if (partial > 0)
1088 *curKey = MAXVERSE;
1089 newElement->setUpperBound(*curKey);
1090 *newElement = TOP;
1091 tmpListKey.getElement()->userData = (__u64)(bufStart+(buf-iBuf.c_str()));
1092 }
1093 }
1094 }
1095 }
1096 *book = 0;
1097 tmpListKey = TOP;
1098 internalListKey = tmpListKey;
1099 internalListKey = TOP; // Align internalListKey to first element before passing back;
1100
1101 delete curKey;
1102 delete lastKey;
1103
1104 return internalListKey;
1105 }
1106
1107
1108 /******************************************************************************
1109 * VerseKey::setLowerBound - sets / gets the lower boundary for this key
1110 */
1111
setLowerBound(const VerseKey & lb)1112 void VerseKey::setLowerBound(const VerseKey &lb)
1113 {
1114 initBounds();
1115
1116 lowerBound = lb.getIndex();
1117 lowerBoundComponents.test = lb.getTestament();
1118 lowerBoundComponents.book = lb.getBook();
1119 lowerBoundComponents.chap = lb.getChapter();
1120 lowerBoundComponents.verse = lb.getVerse();
1121 lowerBoundComponents.suffix = lb.getSuffix();
1122
1123 // both this following check and UpperBound check force upperBound to
1124 // change allowing LowerBound then UpperBound logic to always flow
1125 // and set values without restrictions, as expected
1126 if (upperBound < lowerBound) upperBound = lowerBound;
1127 boundSet = true;
1128 }
1129
1130
1131 /******************************************************************************
1132 * VerseKey::setUpperBound - sets / gets the upper boundary for this key
1133 */
1134
setUpperBound(const VerseKey & ub)1135 void VerseKey::setUpperBound(const VerseKey &ub)
1136 {
1137 initBounds();
1138
1139 upperBound = ub.getIndex();
1140 upperBoundComponents.test = ub.getTestament();
1141 upperBoundComponents.book = ub.getBook();
1142 upperBoundComponents.chap = ub.getChapter();
1143 upperBoundComponents.verse = ub.getVerse();
1144 upperBoundComponents.suffix = ub.getSuffix();
1145
1146 // see setLowerBound comment, above
1147 if (upperBound < lowerBound) upperBound = lowerBound;
1148 boundSet = true;
1149 }
1150
1151
1152 /******************************************************************************
1153 * VerseKey::getLowerBound - gets the lower boundary for this key
1154 */
1155
getLowerBound() const1156 VerseKey &VerseKey::getLowerBound() const
1157 {
1158 initBounds();
1159 if (!isAutoNormalize()) {
1160 tmpClone->testament = lowerBoundComponents.test;
1161 tmpClone->book = lowerBoundComponents.book;
1162 tmpClone->chapter = lowerBoundComponents.chap;
1163 tmpClone->setVerse (lowerBoundComponents.verse);
1164 tmpClone->setSuffix (lowerBoundComponents.suffix);
1165 }
1166 else {
1167 tmpClone->setIndex(lowerBound);
1168 tmpClone->setSuffix (lowerBoundComponents.suffix);
1169 }
1170
1171 return (*tmpClone);
1172 }
1173
1174
1175 /******************************************************************************
1176 * VerseKey::getUpperBound - sets / gets the upper boundary for this key
1177 */
1178
getUpperBound() const1179 VerseKey &VerseKey::getUpperBound() const
1180 {
1181 initBounds();
1182 if (!isAutoNormalize()) {
1183 tmpClone->testament = upperBoundComponents.test;
1184 tmpClone->book = upperBoundComponents.book;
1185 tmpClone->chapter = upperBoundComponents.chap;
1186 tmpClone->setVerse (upperBoundComponents.verse);
1187 tmpClone->setSuffix (upperBoundComponents.suffix);
1188 }
1189 else {
1190 tmpClone->setIndex(upperBound);
1191 tmpClone->setSuffix (upperBoundComponents.suffix);
1192 }
1193
1194 return (*tmpClone);
1195 }
1196
1197
1198 /******************************************************************************
1199 * VerseKey::clearBounds - clears bounds for this VerseKey
1200 */
1201
clearBounds()1202 void VerseKey::clearBounds()
1203 {
1204 delete tmpClone;
1205 tmpClone = 0;
1206 boundSet = false;
1207 }
1208
1209
initBounds() const1210 void VerseKey::initBounds() const
1211 {
1212 if (!tmpClone) {
1213 tmpClone = (VerseKey *)this->clone();
1214 tmpClone->setAutoNormalize(false);
1215 tmpClone->setIntros(true);
1216 tmpClone->setTestament((BMAX[1])?2:1);
1217 tmpClone->setBook(BMAX[(BMAX[1])?1:0]);
1218 tmpClone->setChapter(tmpClone->getChapterMax());
1219 tmpClone->setVerse(tmpClone->getVerseMax());
1220 upperBound = tmpClone->getIndex();
1221 upperBoundComponents.test = tmpClone->getTestament();
1222 upperBoundComponents.book = tmpClone->getBook();
1223 upperBoundComponents.chap = tmpClone->getChapter();
1224 upperBoundComponents.verse = tmpClone->getVerse();
1225 upperBoundComponents.suffix = tmpClone->getSuffix();
1226
1227 lowerBound = 0;
1228 lowerBoundComponents.test = 0;
1229 lowerBoundComponents.book = 0;
1230 lowerBoundComponents.chap = 0;
1231 lowerBoundComponents.verse = 0;
1232 lowerBoundComponents.suffix = 0;
1233
1234 }
1235 else tmpClone->setLocale(getLocale());
1236 }
1237
1238
1239 /******************************************************************************
1240 * VerseKey::getText - refreshes keytext before returning if cast to
1241 * a (char *) is requested
1242 */
1243
getText() const1244 const char *VerseKey::getText() const {
1245 freshtext();
1246 return keytext;
1247 }
1248
1249
getShortText() const1250 const char *VerseKey::getShortText() const {
1251 static char *stext = 0;
1252 char buf[2047];
1253 freshtext();
1254 if (book < 1) {
1255 if (testament < 1)
1256 sprintf(buf, "[ Module Heading ]");
1257 else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
1258 }
1259 else {
1260 sprintf(buf, "%s %d:%d", getBookAbbrev(), chapter, verse);
1261 }
1262 stdstr(&stext, buf);
1263 return stext;
1264 }
1265
1266
getBookName() const1267 const char *VerseKey::getBookName() const {
1268 return getPrivateLocale()->translate(refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getLongName());
1269 }
1270
1271
getOSISBookName() const1272 const char *VerseKey::getOSISBookName() const {
1273 return refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getOSISName();
1274 }
1275
1276
getBookAbbrev() const1277 const char *VerseKey::getBookAbbrev() const {
1278 return refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getPreferredAbbreviation();
1279 }
1280
1281
1282 /******************************************************************************
1283 * VerseKey::setPosition(SW_POSITION) - Positions this key
1284 *
1285 * ENT: p - position
1286 *
1287 * RET: *this
1288 */
1289
setPosition(SW_POSITION p)1290 void VerseKey::setPosition(SW_POSITION p) {
1291 switch (p) {
1292 case POS_TOP: {
1293 const VerseKey *lb = &getLowerBound();
1294 testament = (lb->getTestament() || intros) ? lb->getTestament() : 1;
1295 book = (lb->getBook() || intros) ? lb->getBook() : 1;
1296 chapter = (lb->getChapter() || intros) ? lb->getChapter() : 1;
1297 verse = (lb->getVerse() || intros) ? lb->getVerse() : 1;
1298 suffix = lb->getSuffix();
1299 break;
1300 }
1301 case POS_BOTTOM: {
1302 const VerseKey *ub = &getUpperBound();
1303 testament = (ub->getTestament() || intros) ? ub->getTestament() : 1;
1304 book = (ub->getBook() || intros) ? ub->getBook() : 1;
1305 chapter = (ub->getChapter() || intros) ? ub->getChapter() : 1;
1306 verse = (ub->getVerse() || intros) ? ub->getVerse() : 1;
1307 suffix = ub->getSuffix();
1308 break;
1309 }
1310 case POS_MAXVERSE:
1311 suffix = 0;
1312 verse = 1;
1313 normalize();
1314 verse = getVerseMax();
1315 suffix = 0;
1316 break;
1317 case POS_MAXCHAPTER:
1318 suffix = 0;
1319 verse = 1;
1320 chapter = 1;
1321 normalize();
1322 chapter = getChapterMax();
1323 break;
1324 }
1325 normalize(true);
1326 popError(); // clear error from normalize
1327 }
1328
getChapterMax() const1329 int VerseKey::getChapterMax() const {
1330 if (book < 1) return 0;
1331 const VersificationMgr::Book *b = refSys->getBook(((testament>1)?BMAX[0]:0)+book-1);
1332 return (b) ? b->getChapterMax() : -1;
1333 }
1334
getVerseMax() const1335 int VerseKey::getVerseMax() const {
1336 if (book < 1) return 0;
1337 const VersificationMgr::Book *b = refSys->getBook(((testament>1)?BMAX[0]:0)+book-1);
1338 return (b) ? b->getVerseMax(chapter) : -1;
1339 }
1340
1341
1342 /******************************************************************************
1343 * VerseKey::increment - Increments key a number of verses
1344 *
1345 * ENT: step - Number of verses to jump forward
1346 *
1347 * RET: *this
1348 */
1349
increment(int step)1350 void VerseKey::increment(int step) {
1351 // if we're not autonormalizing and we're already not normalized
1352 if (!autonorm && chapter > 0 && verse > getVerseMax()) {
1353 verse += step;
1354 checkBounds();
1355 return;
1356 }
1357 char ierror = 0;
1358 setIndex(getIndex() + step);
1359 while ((!verse) && (!intros) && (!ierror)) {
1360 setIndex(getIndex() + 1);
1361 ierror = popError();
1362 }
1363
1364 error = (ierror) ? ierror : error;
1365 }
1366
1367
1368 /******************************************************************************
1369 * VerseKey::decrement - Decrements key a number of verses
1370 *
1371 * ENT: step - Number of verses to jump backward
1372 *
1373 * RET: *this
1374 */
1375
decrement(int step)1376 void VerseKey::decrement(int step) {
1377 // if we're not autonormalizing and we're already not normalized
1378 if (!autonorm && chapter > 0 && verse > getVerseMax()) {
1379 verse -= step;
1380 checkBounds();
1381 return;
1382 }
1383 char ierror = 0;
1384 setIndex(getIndex() - step);
1385 while ((!verse) && (!intros) && (!ierror)) {
1386 setIndex(getIndex() - 1);
1387 ierror = popError();
1388 }
1389 if ((ierror) && (!intros))
1390 (*this)++;
1391
1392 error = (ierror) ? ierror : error;
1393 }
1394
1395
1396 /******************************************************************************
1397 * VerseKey::normalize - checks limits and normalizes if necessary (e.g.
1398 * Matthew 29:47 = Mark 2:2). If last verse is
1399 * exceeded, key is set to last Book CH:VS
1400 * RET: *this
1401 */
1402
normalize(bool autocheck)1403 void VerseKey::normalize(bool autocheck)
1404 {
1405
1406 if ((!autocheck || autonorm) // only normalize if we were explicitely called or if autonorm is turned on
1407 ) {
1408 error = 0;
1409
1410 while ((testament < 3) && (testament > 0)) {
1411
1412
1413 if (book > BMAX[testament-1]) {
1414 book -= (BMAX[testament-1] + (intros?1:0));
1415 testament++;
1416 continue;
1417 }
1418 if (book < (intros?0:1)) {
1419 if (--testament > 0) {
1420 book += (BMAX[testament-1] + (intros?1:0));
1421 }
1422 continue;
1423 }
1424
1425
1426 if (chapter > getChapterMax()) {
1427 chapter -= (getChapterMax() + (intros?1:0));
1428 book++;
1429 continue;
1430 }
1431 if (chapter < (intros?0:1)) {
1432 --book;
1433 if (book < (intros?0:1)) {
1434 if (--testament > 0) {
1435 book += (BMAX[testament-1] + (intros?1:0));
1436 }
1437 }
1438 chapter += (getChapterMax() + (intros?1:0));
1439 continue;
1440 }
1441
1442
1443 if (chapter > 0 && verse > getVerseMax()) {
1444 verse -= (getVerseMax() + (intros?1:0));
1445 chapter++;
1446 continue;
1447 }
1448 if (verse < (intros?0:1)) {
1449 if (--chapter < (intros?0:1)) {
1450 --book;
1451 if (book < (intros?0:1)) {
1452 if (--testament > 0) {
1453 book += (BMAX[testament-1] + (intros?1:0));
1454 }
1455 }
1456 chapter += (getChapterMax() + (intros?1:0));
1457 }
1458 verse += (getVerseMax() + (intros?1:0));
1459 continue;
1460 }
1461
1462 break; // If we've made it this far (all failure checks continue) we're ok
1463 }
1464
1465 if (testament > (BMAX[1]?2:1)) {
1466 testament = BMAX[1]?2:1;
1467 book = BMAX[testament-1];
1468 chapter = getChapterMax();
1469 verse = getVerseMax();
1470 error = KEYERR_OUTOFBOUNDS;
1471 }
1472
1473 if (testament < 1) {
1474 error = ((!intros) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0;
1475 testament = ((intros) ? 0 : 1);
1476 book = ((intros) ? 0 : 1);
1477 chapter = ((intros) ? 0 : 1);
1478 verse = ((intros) ? 0 : 1);
1479 }
1480
1481 // should we always perform bounds checks? Tried but seems to cause infinite recursion
1482 if (_compare(getUpperBound()) > 0) {
1483 positionFrom(getUpperBound());
1484 error = KEYERR_OUTOFBOUNDS;
1485 }
1486 if (_compare(getLowerBound()) < 0) {
1487 positionFrom(getLowerBound());
1488 error = KEYERR_OUTOFBOUNDS;
1489 }
1490 }
1491 }
1492
1493
1494 /******************************************************************************
1495 * VerseKey::getTestament - Gets testament
1496 *
1497 * RET: value of testament
1498 */
1499
getTestament() const1500 char VerseKey::getTestament() const
1501 {
1502 return testament;
1503 }
1504
1505
1506 /******************************************************************************
1507 * VerseKey::getBook - Gets book
1508 *
1509 * RET: value of book
1510 */
1511
getBook() const1512 char VerseKey::getBook() const
1513 {
1514 return book;
1515 }
1516
1517
1518 /******************************************************************************
1519 * VerseKey::getChapter - Gets chapter
1520 *
1521 * RET: value of chapter
1522 */
1523
getChapter() const1524 int VerseKey::getChapter() const
1525 {
1526 return chapter;
1527 }
1528
1529
1530 /******************************************************************************
1531 * VerseKey::getVerse - Gets verse
1532 *
1533 * RET: value of verse
1534 */
1535
getVerse() const1536 int VerseKey::getVerse() const
1537 {
1538 return verse;
1539 }
1540
1541
1542 /******************************************************************************
1543 * VerseKey::setTestament - Sets/gets testament
1544 *
1545 * ENT: itestament - value which to set testament
1546 * [MAXPOS(char)] - only get
1547 *
1548 */
1549
setTestament(char itestament)1550 void VerseKey::setTestament(char itestament)
1551 {
1552 suffix = 0;
1553 verse = (intros) ? 0 : 1;
1554 chapter = (intros) ? 0 : 1;
1555 book = (intros) ? 0 : 1;
1556 testament = itestament;
1557 normalize(true);
1558 }
1559
1560
1561 /******************************************************************************
1562 * VerseKey::setBook - Sets/gets book
1563 *
1564 * ENT: ibook - value which to set book
1565 */
1566
setBook(char ibook)1567 void VerseKey::setBook(char ibook)
1568 {
1569 suffix = 0;
1570 verse = (intros) ? 0 : 1;
1571 chapter = (intros) ? 0 : 1;
1572 book = ibook;
1573 normalize(true);
1574 }
1575
1576
1577
1578 /******************************************************************************
1579 * VerseKey::setBookName - Sets/gets book by name
1580 *
1581 * ENT: bname - book name/abbrev
1582 */
1583
setBookName(const char * bname)1584 void VerseKey::setBookName(const char *bname)
1585 {
1586 int bnum = getBookFromAbbrev(bname);
1587 if (bnum > -1) {
1588 if (bnum > BMAX[0]) {
1589 bnum -= BMAX[0];
1590 testament = 2;
1591 }
1592 else testament = 1;
1593 setBook(bnum);
1594 }
1595 else error = KEYERR_OUTOFBOUNDS;
1596 }
1597
1598
1599 /******************************************************************************
1600 * VerseKey::setChapter - Sets/gets chapter
1601 *
1602 * ENT: ichapter - value which to set chapter
1603 */
1604
setChapter(int ichapter)1605 void VerseKey::setChapter(int ichapter)
1606 {
1607 suffix = 0;
1608 verse = (intros) ? 0 : 1;
1609 chapter = ichapter;
1610 normalize(true);
1611 }
1612
1613
1614 /******************************************************************************
1615 * VerseKey::setVerse - Sets/gets verse
1616 *
1617 * ENT: iverse - value which to set verse
1618 * [MAXPOS(int)] - only get
1619 *
1620 * RET: if unchanged -> value of verse
1621 * if changed -> previous value of verse
1622 */
1623
setVerse(int iverse)1624 void VerseKey::setVerse(int iverse)
1625 {
1626 suffix = 0;
1627 verse = iverse;
1628 normalize(true);
1629 }
1630
1631
getSuffix() const1632 char VerseKey::getSuffix() const {
1633 return suffix;
1634 }
1635
setSuffix(char suf)1636 void VerseKey::setSuffix(char suf) {
1637 suffix = suf;
1638 }
1639
1640 /******************************************************************************
1641 * VerseKey::isAutoNormalize - gets flag that tells VerseKey to auto-
1642 * matically normalize itself when modified
1643 */
1644
isAutoNormalize() const1645 bool VerseKey::isAutoNormalize() const
1646 {
1647 return autonorm;
1648 }
1649
setAutoNormalize(bool iautonorm)1650 void VerseKey::setAutoNormalize(bool iautonorm)
1651 {
1652 autonorm = iautonorm?1:0;
1653 normalize(true);
1654 }
1655
1656
1657 /******************************************************************************
1658 * VerseKey::setIntros - Sets flag that tells VerseKey to include
1659 * chap/book/testmnt/module introductions
1660 *
1661 * ENT: val - value which to set intros
1662 *
1663 */
1664
setIntros(bool val)1665 void VerseKey::setIntros(bool val)
1666 {
1667 intros = val;
1668 normalize(true);
1669 }
1670
isIntros() const1671 bool VerseKey::isIntros() const
1672 {
1673 return intros;
1674 }
1675
1676
1677 /******************************************************************************
1678 * VerseKey::getIndex - Gets index based upon current verse
1679 *
1680 * RET: offset
1681 */
1682
getIndex() const1683 long VerseKey::getIndex() const
1684 {
1685 long offset;
1686
1687 if (!testament) { // if we want module heading
1688 offset = 0;
1689 }
1690 else if (!book) { // we want testament heading
1691 offset = ((testament == 2) ? refSys->getNTStartOffset():0) + 1;
1692 }
1693 else {
1694 offset = refSys->getOffsetFromVerse((((testament>1)?BMAX[0]:0)+book-1), chapter, verse);
1695 }
1696 return offset;
1697 }
1698
1699
1700 /******************************************************************************
1701 * VerseKey::getTestamentIndex - Gets index based upon current verse
1702 *
1703 * RET: offset
1704 */
1705
getTestamentIndex() const1706 long VerseKey::getTestamentIndex() const
1707 {
1708 long offset = getIndex();
1709 return (testament > 1) ? offset - refSys->getNTStartOffset() : offset;
1710 }
1711
1712
1713 /******************************************************************************
1714 * VerseKey::setIndex - Sets index based upon current verse
1715 *
1716 * ENT: iindex - value to set index to
1717 *
1718 */
1719
setIndex(long iindex)1720 void VerseKey::setIndex(long iindex)
1721 {
1722 // assert we're sane
1723 if (iindex < 0) {
1724 error = KEYERR_OUTOFBOUNDS;
1725 return;
1726 }
1727
1728 int b;
1729 error = refSys->getVerseFromOffset(iindex, &b, &chapter, &verse);
1730 book = (unsigned char)b;
1731 testament = 1;
1732 if (book > BMAX[0]) {
1733 book -= BMAX[0];
1734 testament = 2;
1735 }
1736 // special case for Module and Testament heading
1737 if (book < 0) { testament = 0; book = 0; }
1738 if (chapter < 0) { book = 0; chapter = 0; }
1739
1740 checkBounds();
1741 }
1742
checkBounds()1743 void VerseKey::checkBounds() {
1744
1745 long i = getIndex();
1746
1747 initBounds();
1748 if (i > upperBound) {
1749 setIndex(upperBound);
1750 i = getIndex();
1751 error = KEYERR_OUTOFBOUNDS;
1752 }
1753 if (i < lowerBound) {
1754 setIndex(lowerBound);
1755 error = KEYERR_OUTOFBOUNDS;
1756 }
1757 }
1758
1759
1760 /******************************************************************************
1761 * VerseKey::compare - Compares another SWKey object
1762 *
1763 * ENT: ikey - key to compare with this one
1764 *
1765 * RET: >0 if this versekey is greater than compare versekey
1766 * <0 <
1767 * 0 =
1768 */
1769
compare(const SWKey & ikey)1770 int VerseKey::compare(const SWKey &ikey)
1771 {
1772 const SWKey *testKey = &ikey;
1773 const VerseKey *vkey = (const VerseKey *)SWDYNAMIC_CAST(VerseKey, testKey);
1774 if (vkey) {
1775 return _compare(*vkey);
1776 }
1777 const VerseKey ivkey = (const char *)ikey;
1778 return _compare(ivkey);
1779 }
1780
1781
1782 /******************************************************************************
1783 * VerseKey::_compare - Compares another VerseKey object
1784 *
1785 * ENT: ikey - key to compare with this one
1786 *
1787 * RET: >0 if this versekey is greater than compare versekey
1788 * <0 <
1789 * 0 =
1790 */
1791
_compare(const VerseKey & ivkey)1792 int VerseKey::_compare(const VerseKey &ivkey)
1793 {
1794 unsigned long keyval1 = 0;
1795 unsigned long keyval2 = 0;
1796
1797 keyval1 += getTestament() * 1000000000;
1798 keyval2 += ivkey.getTestament() * 1000000000;
1799 keyval1 += getBook() * 10000000;
1800 keyval2 += ivkey.getBook() * 10000000;
1801 keyval1 += getChapter() * 10000;
1802 keyval2 += ivkey.getChapter() * 10000;
1803 keyval1 += getVerse() * 50;
1804 keyval2 += ivkey.getVerse() * 50;
1805 keyval1 += (int)getSuffix();
1806 keyval2 += (int)ivkey.getSuffix();
1807 keyval1 = (keyval1 != keyval2) ? ((keyval1 > keyval2) ? 1 : -1) : 0; // -1 | 0 | 1
1808 return (int)keyval1;
1809 }
1810
1811
getOSISRef() const1812 const char *VerseKey::getOSISRef() const {
1813 static char buf[5][254];
1814 static int loop = 0;
1815
1816 if (loop > 4)
1817 loop = 0;
1818
1819 if (getVerse())
1820 sprintf(buf[loop], "%s.%d.%d", getOSISBookName(), getChapter(), getVerse());
1821 else if (getChapter())
1822 sprintf(buf[loop], "%s.%d", getOSISBookName(), getChapter());
1823 else if (getBook())
1824 sprintf(buf[loop], "%s", getOSISBookName());
1825 else buf[loop][0] = 0;
1826 return buf[loop++];
1827 }
1828
1829
1830 /******************************************************************************
1831 * VerseKey::getRangeText - returns parsable range text for this key
1832 */
1833
getRangeText() const1834 const char *VerseKey::getRangeText() const {
1835 if (isBoundSet() && lowerBound != upperBound) {
1836 SWBuf buf = (const char *)getLowerBound();
1837 buf += "-";
1838 buf += (const char *)getUpperBound();
1839 stdstr(&rangeText, buf.c_str());
1840 }
1841 else stdstr(&rangeText, getText());
1842 return rangeText;
1843 }
1844
1845
1846 /******************************************************************************
1847 * VerseKey::getOSISRefRangeText - returns parsable range text for this key
1848 */
1849
getOSISRefRangeText() const1850 const char *VerseKey::getOSISRefRangeText() const {
1851 if (isBoundSet() && (lowerBound != upperBound)) {
1852 SWBuf buf = getLowerBound().getOSISRef();
1853 buf += "-";
1854 buf += getUpperBound().getOSISRef();
1855 stdstr(&rangeText, buf.c_str());
1856 }
1857 else stdstr(&rangeText, getOSISRef());
1858 return rangeText;
1859 }
1860
1861
1862 // TODO: this is static so we have no context. We can only parse KJV v11n now
1863 // possibly add a const char *versification = KJV param?
convertToOSIS(const char * inRef,const SWKey * lastKnownKey)1864 const char *VerseKey::convertToOSIS(const char *inRef, const SWKey *lastKnownKey) {
1865 static SWBuf outRef;
1866
1867 outRef = "";
1868
1869 VerseKey defLanguage;
1870 ListKey verses = defLanguage.parseVerseList(inRef, (*lastKnownKey), true);
1871 const char *startFrag = inRef;
1872 for (int i = 0; i < verses.getCount(); i++) {
1873 SWKey *element = verses.getElement(i);
1874 // VerseKey *element = SWDYNAMIC_CAST(VerseKey, verses.GetElement(i));
1875 SWBuf buf;
1876 // TODO: This code really needs to not use fixed size arrays
1877 char frag[800];
1878 char preJunk[800];
1879 char postJunk[800];
1880 memset(frag, 0, 800);
1881 memset(preJunk, 0, 800);
1882 memset(postJunk, 0, 800);
1883 while ((*startFrag) && (strchr(" {}:;,()[].", *startFrag))) {
1884 outRef += *startFrag;
1885 startFrag++;
1886 }
1887 memmove(frag, startFrag, (size_t)((const char *)element->userData - startFrag) + 1);
1888 frag[((const char *)element->userData - startFrag) + 1] = 0;
1889 int j;
1890 for (j = strlen(frag)-1; j && (strchr(" {}:;,()[].", frag[j])); j--);
1891 if (frag[j+1])
1892 strcpy(postJunk, frag+j+1);
1893 frag[j+1]=0;
1894 startFrag += ((const char *)element->userData - startFrag) + 1;
1895 buf = "<reference osisRef=\"";
1896 buf += element->getOSISRefRangeText();
1897 buf += "\">";
1898 buf += frag;
1899 buf += "</reference>";
1900 buf += postJunk;
1901
1902 outRef += buf;
1903
1904 }
1905 if (startFrag < (inRef + strlen(inRef)))
1906 outRef += startFrag;
1907 return outRef.c_str();
1908 }
1909 SWORD_NAMESPACE_END
1910