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