1 /******************************************************************************
2  *
3  *  versificationmgr.cpp -	implementation of class VersificationMgr used
4  *				for managing versification systems
5  *
6  * $Id: versificationmgr.cpp 3515 2017-11-01 11:38:09Z scribe $
7  *
8  * Copyright 2008-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 #include <versificationmgr.h>
25 #include <vector>
26 #include <map>
27 #include <treekey.h>
28 #include <canon.h>		// KJV internal versification system
29 #include <swlog.h>
30 #include <algorithm>
31 
32 #include <canon_null.h>		// null v11n system
33 
34 #include <canon_leningrad.h>	// Leningrad Codex (WLC) v11n system
35 #include <canon_mt.h>		// Masoretic Text (MT) v11n system
36 #include <canon_kjva.h>		// KJV + Apocrypha v11n system
37 #include <canon_nrsv.h>		// NRSV v11n system
38 #include <canon_nrsva.h>	// NRSV + Apocrypha v11n system
39 #include <canon_synodal.h>	// Russian Synodal v11n system
40 #include <canon_synodalprot.h>	// Russian Synodal v11n system
41 #include <canon_vulg.h>		// Vulgate v11n system
42 #include <canon_german.h>	// German v11n system
43 #include <canon_luther.h>	// Luther v11n system
44 #include <canon_catholic.h>	// Catholic v11n system (10 chapter Esther)
45 #include <canon_catholic2.h>	// Catholic2 v11n system (16 chapter Esther)
46 #include <canon_lxx.h>		// General LXX v11n system (includes GNT, as used in Orthodox Bibles)
47 #include <canon_orthodox.h>	// Orthodox v11n system as used in Orthodox Bibles
48 #include <canon_segond.h>	// French v11n system as used by Segond Bibles and its derivatives
49 #include <canon_calvin.h>	// French v11n system
50 #include <canon_darbyfr.h>	// French v11n system based on John Darby's French translation
51 
52 using std::vector;
53 using std::map;
54 using std::distance;
55 using std::lower_bound;
56 
57 
58 SWORD_NAMESPACE_START
59 
60 
getSystemVersificationMgr()61 VersificationMgr *VersificationMgr::getSystemVersificationMgr() {
62 	if (!systemVersificationMgr) {
63 		systemVersificationMgr = new VersificationMgr();
64 		systemVersificationMgr->registerVersificationSystem("KJV", otbooks, ntbooks, vm);
65 		systemVersificationMgr->registerVersificationSystem("Leningrad", otbooks_leningrad, ntbooks_null, vm_leningrad);
66 		systemVersificationMgr->registerVersificationSystem("MT", otbooks_mt, ntbooks_null, vm_mt);
67 		systemVersificationMgr->registerVersificationSystem("KJVA", otbooks_kjva, ntbooks, vm_kjva);
68 		systemVersificationMgr->registerVersificationSystem("NRSV", otbooks, ntbooks, vm_nrsv, mappings_nrsv);
69 		systemVersificationMgr->registerVersificationSystem("NRSVA", otbooks_nrsva, ntbooks, vm_nrsva);
70 		systemVersificationMgr->registerVersificationSystem("Synodal", otbooks_synodal, ntbooks_synodal, vm_synodal, mappings_synodal);
71 		systemVersificationMgr->registerVersificationSystem("SynodalProt", otbooks_synodalProt, ntbooks_synodal, vm_synodalProt);
72 		systemVersificationMgr->registerVersificationSystem("Vulg", otbooks_vulg, ntbooks_vulg, vm_vulg, mappings_vulg);
73 		systemVersificationMgr->registerVersificationSystem("German", otbooks_german, ntbooks, vm_german);
74 		systemVersificationMgr->registerVersificationSystem("Luther", otbooks_luther, ntbooks_luther, vm_luther);
75 		systemVersificationMgr->registerVersificationSystem("Catholic", otbooks_catholic, ntbooks, vm_catholic);
76 		systemVersificationMgr->registerVersificationSystem("Catholic2", otbooks_catholic2, ntbooks, vm_catholic2);
77 		systemVersificationMgr->registerVersificationSystem("LXX", otbooks_lxx, ntbooks, vm_lxx);
78 		systemVersificationMgr->registerVersificationSystem("Orthodox", otbooks_orthodox, ntbooks, vm_orthodox);
79 		systemVersificationMgr->registerVersificationSystem("Calvin", otbooks, ntbooks, vm_calvin, mappings_calvin);
80 		systemVersificationMgr->registerVersificationSystem("DarbyFr", otbooks, ntbooks, vm_darbyfr, mappings_darbyfr);
81 		systemVersificationMgr->registerVersificationSystem("Segond", otbooks, ntbooks, vm_segond, mappings_segond);
82 	}
83 	return systemVersificationMgr;
84 }
85 
86 
87 class VersificationMgr::System::Private {
88 public:
89 	/** Array[chapmax] of maximum verses in chapters */
90 	vector<Book> books;
91 	map<SWBuf, int> osisLookup;
92 	/** General mapping rule is that first verse of every chapter corresponds first
93 		verse of another chapter in default intermediate canon(kjva), so mapping data
94 		contains expections. Intermediate canon could not contain corresponding data.
95 
96 		pointers on uchar[7]: 1 value - book id 1-based, ot+nt, 2-4 map to, 5-7 map
97 		from (chap,verse from, verse to if greater then "verse from")
98 
99 		TODO what if book name in one v11n differs from cannon
100 			special section in mapping for book transformation
101 	*/
102 	typedef vector<const unsigned char*> mapping;
103 	vector<mapping> mappings;
104 	vector<const char*> mappingsExtraBooks;
105 
Private()106 	Private() {
107 	}
Private(const VersificationMgr::System::Private & other)108 	Private(const VersificationMgr::System::Private &other) {
109 		books = other.books;
110 		osisLookup = other.osisLookup;
111 	}
operator =(const VersificationMgr::System::Private & other)112 	VersificationMgr::System::Private &operator =(const VersificationMgr::System::Private &other) {
113 		books = other.books;
114 		osisLookup = other.osisLookup;
115 		return *this;
116 	}
117 };
118 
119 
120 class VersificationMgr::Book::Private {
121 friend struct BookOffsetLess;
122 public:
123 	/** Array[chapmax] of maximum verses in chapters */
124 	vector<int> verseMax;
125 	vector<long> offsetPrecomputed;
126 
Private()127 	Private() {
128 		verseMax.clear();
129 	}
Private(const VersificationMgr::Book::Private & other)130 	Private(const VersificationMgr::Book::Private &other) {
131 		verseMax.clear();
132 		verseMax = other.verseMax;
133 		offsetPrecomputed = other.offsetPrecomputed;
134 	}
operator =(const VersificationMgr::Book::Private & other)135 	VersificationMgr::Book::Private &operator =(const VersificationMgr::Book::Private &other) {
136 		verseMax.clear();
137                 int s = (int)other.verseMax.size();
138                 if (s) verseMax = other.verseMax;
139 		offsetPrecomputed = other.offsetPrecomputed;
140 		return *this;
141 	}
142 };
143 
144 
145 struct BookOffsetLess {
operator ()BookOffsetLess146 	bool operator() (const VersificationMgr::Book &o1, const VersificationMgr::Book &o2) const { return o1.p->offsetPrecomputed[0] < o2.p->offsetPrecomputed[0]; }
operator ()BookOffsetLess147 	bool operator() (const long &o1, const VersificationMgr::Book &o2) const { return o1 < o2.p->offsetPrecomputed[0]; }
operator ()BookOffsetLess148 	bool operator() (const VersificationMgr::Book &o1, const long &o2) const { return o1.p->offsetPrecomputed[0] < o2; }
operator ()BookOffsetLess149 	bool operator() (const long &o1, const long &o2) const { return o1 < o2; }
150 };
151 
152 
init()153 void VersificationMgr::Book::init() {
154 	p = new Private();
155 }
156 
157 
init()158 void VersificationMgr::System::init() {
159 	p = new Private();
160 	BMAX[0] = 0;
161 	BMAX[1] = 0;
162 	ntStartOffset = 0;
163 }
164 
165 
System(const System & other)166 VersificationMgr::System::System(const System &other) {
167 	init();
168 	name = other.name;
169 	BMAX[0] = other.BMAX[0];
170 	BMAX[1] = other.BMAX[1];
171 	(*p) = *(other.p);
172 	ntStartOffset = other.ntStartOffset;
173 }
174 
175 
operator =(const System & other)176 VersificationMgr::System &VersificationMgr::System::operator =(const System &other) {
177 	name = other.name;
178 	BMAX[0] = other.BMAX[0];
179 	BMAX[1] = other.BMAX[1];
180 	(*p) = *(other.p);
181 	ntStartOffset = other.ntStartOffset;
182 	return *this;
183 }
184 
185 
~System()186 VersificationMgr::System::~System() {
187 	delete p;
188 }
189 
190 
getBook(int number) const191 const VersificationMgr::Book *VersificationMgr::System::getBook(int number) const {
192 	return (number < (signed int)p->books.size()) ? &(p->books[number]) : 0;
193 }
194 
195 
getBookNumberByOSISName(const char * bookName) const196 int VersificationMgr::System::getBookNumberByOSISName(const char *bookName) const {
197 	map<SWBuf, int>::const_iterator it = p->osisLookup.find(bookName);
198 	return (it != p->osisLookup.end()) ? it->second : -1;
199 }
200 
201 
loadFromSBook(const sbook * ot,const sbook * nt,int * chMax,const unsigned char * mappings)202 void VersificationMgr::System::loadFromSBook(const sbook *ot, const sbook *nt, int *chMax, const unsigned char *mappings) {
203 	int chap = 0;
204 	int book = 0;
205 	long offset = 0;	// module heading
206 	offset++;			// testament heading
207 	while (ot->chapmax) {
208 		p->books.push_back(Book(ot->name, ot->osis, ot->prefAbbrev, ot->chapmax));
209 		offset++;		// book heading
210 		Book &b = p->books[p->books.size()-1];
211 		p->osisLookup[b.getOSISName()] = (int)p->books.size();
212 		for (int i = 0; i < ot->chapmax; i++) {
213 			b.p->verseMax.push_back(chMax[chap]);
214 			offset++;		// chapter heading
215 			b.p->offsetPrecomputed.push_back(offset);
216 			offset += chMax[chap++];
217 		}
218 		ot++;
219 		book++;
220 	}
221 	BMAX[0] = book;
222 	book = 0;
223 	ntStartOffset = offset;
224 	offset++;			// testament heading
225 	while (nt->chapmax) {
226 		p->books.push_back(Book(nt->name, nt->osis, nt->prefAbbrev, nt->chapmax));
227 		offset++;		// book heading
228 		Book &b = p->books[p->books.size()-1];
229 		p->osisLookup[b.getOSISName()] = (int)p->books.size();
230 		for (int i = 0; i < nt->chapmax; i++) {
231 			b.p->verseMax.push_back(chMax[chap]);
232 			offset++;		// chapter heading
233 			b.p->offsetPrecomputed.push_back(offset);
234 			offset += chMax[chap++];
235 		}
236 		nt++;
237 		book++;
238 	}
239 	BMAX[1] = book;
240 
241 	// TODO: build offset speed array
242 
243 	// parse mappings
244 	if (mappings != NULL) {
245 		const unsigned char *m=mappings;
246 		for (; *m != 0; m += strlen((const char*)m)+1) {
247 			p->mappingsExtraBooks.push_back((const char*)m);
248 		}
249 		p->mappings.resize(p->books.size()+p->mappingsExtraBooks.size());
250 
251 		for (++m; *m != 0; m += 7) {
252 			p->mappings[m[0]-1].push_back(m);
253 			if (*m > p->books.size()) {
254 				p->mappings[m[7]-1].push_back(m);
255 				m += 1;
256 			}
257 		}
258 	}
259 }
260 
261 
Book(const Book & other)262 VersificationMgr::Book::Book(const Book &other) {
263 	longName = other.longName;
264 	osisName = other.osisName;
265 	prefAbbrev = other.prefAbbrev;
266 	chapMax = other.chapMax;
267 	init();
268 	(*p) = *(other.p);
269 }
270 
271 
operator =(const Book & other)272 VersificationMgr::Book& VersificationMgr::Book::operator =(const Book &other) {
273 	longName = other.longName;
274 	osisName = other.osisName;
275 	prefAbbrev = other.prefAbbrev;
276 	chapMax = other.chapMax;
277 	init();
278 	(*p) = *(other.p);
279 	return *this;
280 }
281 
282 
~Book()283 VersificationMgr::Book::~Book() {
284 	delete p;
285 }
286 
287 
getVerseMax(int chapter) const288 int VersificationMgr::Book::getVerseMax(int chapter) const {
289 	chapter--;
290 	return (p && (chapter < (signed int)p->verseMax.size()) && (chapter > -1)) ? p->verseMax[chapter] : -1;
291 }
292 
293 
getBookCount() const294 int VersificationMgr::System::getBookCount() const {
295 	return (int)(p ? p->books.size() : 0);
296 }
297 
298 
getOffsetFromVerse(int book,int chapter,int verse) const299 long VersificationMgr::System::getOffsetFromVerse(int book, int chapter, int verse) const {
300 	long  offset = -1;
301 	chapter--;
302 
303 	const Book *b = getBook(book);
304 
305 	if (!b)                                        return -1;	// assert we have a valid book
306 	if ((chapter > -1) && (chapter >= (signed int)b->p->offsetPrecomputed.size())) return -1;	// assert we have a valid chapter
307 
308 	offset = b->p->offsetPrecomputed[(chapter > -1)?chapter:0];
309 	if (chapter < 0) offset--;
310 
311 /* old code
312  *
313 	offset = offsets[testament-1][0][book];
314 	offset = offsets[testament-1][1][(int)offset + chapter];
315 	if (!(offset|verse)) // if we have a testament but nothing else.
316 		offset = 1;
317 
318 */
319 
320 	return (offset + verse);
321 }
322 
323 
getVerseFromOffset(long offset,int * book,int * chapter,int * verse) const324 char VersificationMgr::System::getVerseFromOffset(long offset, int *book, int *chapter, int *verse) const {
325 
326 	if (offset < 1) {	// just handle the module heading corner case up front (and error case)
327 		(*book) = -1;
328 		(*chapter) = 0;
329 		(*verse) = 0;
330 		return offset;	// < 0 = error
331 	}
332 
333 	// binary search for book
334 	vector<Book>::iterator b = lower_bound(p->books.begin(), p->books.end(), offset, BookOffsetLess());
335 	if (b == p->books.end()) b--;
336 	(*book)    = distance(p->books.begin(), b)+1;
337 	if (offset < (*(b->p->offsetPrecomputed.begin()))-((((!(*book)) || (*book)==BMAX[0]+1))?2:1)) { // -1 for chapter headings
338 		(*book)--;
339 		if (b != p->books.begin()) {
340 			b--;
341 		}
342 	}
343 	vector<long>::iterator c = lower_bound(b->p->offsetPrecomputed.begin(), b->p->offsetPrecomputed.end(), offset);
344 
345 	// if we're a book heading, we are lessthan chapter precomputes, but greater book.  This catches corner case.
346 	if (c == b->p->offsetPrecomputed.end()) {
347 		c--;
348 	}
349 	if ((offset < *c) && (c == b->p->offsetPrecomputed.begin())) {
350 		(*chapter) = (offset - *c)+1;	// should be 0 or -1 (for testament heading)
351 		(*verse) = 0;
352 	}
353 	else {
354 		if (offset < *c) c--;
355 		(*chapter) = distance(b->p->offsetPrecomputed.begin(), c)+1;
356 		(*verse)   = (offset - *c);
357 	}
358 	return ((*chapter > 0) && (*verse > b->getVerseMax(*chapter))) ? KEYERR_OUTOFBOUNDS : 0;
359 }
360 
361 
362 /***************************************************
363  * VersificationMgr
364  */
365 
366 class VersificationMgr::Private {
367 public:
Private()368 	Private() {
369 	}
Private(const VersificationMgr::Private & other)370 	Private(const VersificationMgr::Private &other) {
371 		systems = other.systems;
372 	}
operator =(const VersificationMgr::Private & other)373 	VersificationMgr::Private &operator =(const VersificationMgr::Private &other) {
374 		systems = other.systems;
375 		return *this;
376 	}
377 	map<SWBuf, System> systems;
378 };
379 // ---------------- statics -----------------
380 VersificationMgr *VersificationMgr::systemVersificationMgr = 0;
381 
382 class __staticsystemVersificationMgr {
383 public:
__staticsystemVersificationMgr()384 	__staticsystemVersificationMgr() { }
~__staticsystemVersificationMgr()385 	~__staticsystemVersificationMgr() { delete VersificationMgr::systemVersificationMgr; }
386 } _staticsystemVersificationMgr;
387 
388 
init()389 void VersificationMgr::init() {
390 	p = new Private();
391 }
392 
393 
~VersificationMgr()394 VersificationMgr::~VersificationMgr() {
395 	delete p;
396 }
397 
398 
setSystemVersificationMgr(VersificationMgr * newVersificationMgr)399 void VersificationMgr::setSystemVersificationMgr(VersificationMgr *newVersificationMgr) {
400 	if (systemVersificationMgr)
401 		delete systemVersificationMgr;
402 	systemVersificationMgr = newVersificationMgr;
403 }
404 
405 
getVersificationSystem(const char * name) const406 const VersificationMgr::System *VersificationMgr::getVersificationSystem(const char *name) const {
407 	map<SWBuf, System>::const_iterator it = p->systems.find(name);
408 	return (it != p->systems.end()) ? &(it->second) : 0;
409 }
410 
411 
registerVersificationSystem(const char * name,const sbook * ot,const sbook * nt,int * chMax,const unsigned char * mappings)412 void VersificationMgr::registerVersificationSystem(const char *name, const sbook *ot, const sbook *nt, int *chMax, const unsigned char *mappings) {
413 	p->systems[name] = name;
414 	System &s = p->systems[name];
415 	s.loadFromSBook(ot, nt, chMax, mappings);
416 }
417 
418 
registerVersificationSystem(const char * name,const TreeKey * tk)419 void VersificationMgr::registerVersificationSystem(const char *name, const TreeKey *tk) {
420 }
421 
422 
getVersificationSystems() const423 const StringList VersificationMgr::getVersificationSystems() const {
424 	StringList retVal;
425 	for (map<SWBuf, System>::const_iterator it = p->systems.begin(); it != p->systems.end(); it++) {
426 		retVal.push_back(it->first);
427 	}
428 	return retVal;
429 }
430 
translateVerse(const System * dstSys,const char ** book,int * chapter,int * verse,int * verse_end) const431 void VersificationMgr::System::translateVerse(const System *dstSys, const char **book, int *chapter, int *verse, int *verse_end) const {
432 	SWLog::getSystemLog()->logDebug("translate verse from %s to %s: %s.%i.%i-%i\n",getName(), dstSys->getName(), *book, *chapter, *verse, *verse_end);
433 
434 	if (!strcmp(getName(),"KJVA") || !strcmp(getName(),"KJV")) {
435 		if (!strcmp(dstSys->getName(),"KJVA") || !strcmp(dstSys->getName(),"KJV"))
436 			return;
437 		// reversed mapping
438 		SWLog::getSystemLog()->logDebug("Perform reversed mapping.\n");
439 		int b = dstSys->getBookNumberByOSISName(*book)-1;
440 
441 		SWLog::getSystemLog()->logDebug("\tgetBookNumberByOSISName %i %s.\n", b, *book);
442 
443 		if (b < 0) {
444 			SWLog::getSystemLog()->logDebug("\tmappingsExtraBooks.size() %i.\n", dstSys->p->mappingsExtraBooks.size());
445 			for (int i=0; i<(int)dstSys->p->mappingsExtraBooks.size(); ++i) {
446 				SWLog::getSystemLog()->logDebug("\t%s %s.\n", *book, dstSys->p->mappingsExtraBooks[i]);
447 				if (!strcmp(*book, dstSys->p->mappingsExtraBooks[i])) {
448 					b = (int)p->books.size()+i-2;
449 					break;
450 				}
451 			}
452 		}
453 
454 		SWLog::getSystemLog()->logDebug("\tb %i.\n", b);
455 
456 		if (b >= (int)dstSys->p->mappings.size() || b < 0) {
457 			SWLog::getSystemLog()->logDebug("no modification");
458 			return;
459 		}
460 
461 		const unsigned char *a = NULL;
462 
463 		// reversed mapping should use forward search for item
464 		for (unsigned int i=0; i<dstSys->p->mappings[b].size(); ++i) {
465 			const unsigned char *m = dstSys->p->mappings[b][i];
466 			if (m[4] == *chapter && m[5] <= *verse) {
467 				SWLog::getSystemLog()->logDebug("found mapping %i %i %i %i %i %i\n",m[1],m[2],m[3],m[4],m[5],m[6]);
468 				if (m[5] == *verse || (m[6] >= *verse && m[5] <= *verse)) {
469 					// inside of any mapping range
470 					*chapter = m[1];
471 					*verse = m[2];
472 					*verse_end = m[3];
473 					if (*m >= dstSys->p->books.size()) {
474 						SWLog::getSystemLog()->logWarning("map to extra books, possible bug source\n");
475 						*book = dstSys->getBook(m[7]-1)->getOSISName();
476 					}
477 					return;
478 				}
479 				// destination mapping can have duplicate items, use the last (by using <=)
480 				if (a == NULL || (a[5]>a[6]?a[5]:a[6]) <= (m[5]>m[6]?m[5]:m[6]))
481 					a = m;
482 			}
483 		}
484 		if (a != NULL) {
485 			SWLog::getSystemLog()->logDebug("set appropriate: %i %i %i %i %i %i\n",a[1],a[2],a[3],a[4],a[5],a[6]);
486 			(*chapter) = a[1];
487 			// shift verse
488 			const int d = (a[3]>a[2]?a[3]:a[2])-(a[6]>a[5]?a[6]:a[5]);
489 			if (*verse < *verse_end)
490 				*verse_end += d;
491 			else
492 				*verse_end = (*verse) + d;
493 			*verse += d;
494 			if (*a > dstSys->p->books.size()) {
495 				SWLog::getSystemLog()->logDebug("appropriate: %i %i %i %i %i %i %i %i\n",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);
496 				SWLog::getSystemLog()->logDebug("book: %s\n", dstSys->getBook(a[7]-1)->getOSISName());
497 				*book = dstSys->getBook(a[7]-1)->getOSISName();
498 			}
499 			return;
500 		}
501 		SWLog::getSystemLog()->logDebug("There is no mapping.\n");
502 	}
503 	else if (strcmp(dstSys->getName(),"KJVA") && strcmp(dstSys->getName(),"KJV")) {
504 		const System *kjva = getSystemVersificationMgr()->getVersificationSystem("KJVA");
505 		const int src_verse = *verse;
506 
507 		translateVerse(kjva, book, chapter, verse, verse_end);
508 
509 		int interm_verse = *verse, interm_range = *verse_end, interm_chapter = *chapter;
510 		const char *interm_book = *book;
511 
512 		kjva->translateVerse(dstSys, book, chapter, verse, verse_end);
513 
514 		// contraction->expansion fix
515 		if (verse < verse_end && !(interm_verse < interm_range)) {
516 			kjva->translateVerse(this, &interm_book, &interm_chapter, &interm_verse, &interm_range);
517 			if (interm_verse < interm_range) {
518 				*verse += src_verse - interm_verse;
519 				if (*verse > *verse_end)
520 					*verse = *verse_end;
521 				else
522 					*verse_end = *verse;
523 			}
524 		}
525 	}
526 	else {
527 		SWLog::getSystemLog()->logDebug("Perform forward mapping.\n");
528 		const int b = getBookNumberByOSISName(*book)-1;
529 		if (b >= (int)p->mappings.size())
530 			return;
531 		// forward mapping should use reversed search for item
532 		for (int i = (int)p->mappings[b].size()-1; i>=0; --i) {
533 			const unsigned char *m = p->mappings[b][i];
534 			if (m[1] < *chapter) {
535 				SWLog::getSystemLog()->logWarning("There is no mapping for this chapter.\n");
536 				return;
537 			}
538 			if (m[1] == *chapter && m[2] <= *verse) {
539 				SWLog::getSystemLog()->logDebug("found mapping %i %i %i %i %i %i\n",m[1],m[2],m[3],m[4],m[5],m[6]);
540 				if (m[2] == *verse || (m[3] >= *verse && m[2] <= *verse)) {
541 					*chapter = m[4];
542 					*verse = m[5];
543 					*verse_end = m[6];
544 				}
545 				else {
546 					*chapter = m[4];
547 					// shift verse
548 					const int d = (m[6]>m[5]?m[6]:m[5])-(m[3]>m[2]?m[3]:m[2]);
549 					if (*verse < *verse_end)
550 						*verse_end += d;
551 					else
552 						*verse_end = (*verse) + d;
553 					*verse += d;
554 				}
555 				if (*m > p->books.size())
556 					*book = p->mappingsExtraBooks[m[0]-p->books.size()-1];
557 				return;
558 			}
559 		}
560 		SWLog::getSystemLog()->logDebug("No mapping.\n");
561 	}
562 }
563 
564 SWORD_NAMESPACE_END
565 
566