The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
versekey.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * versekey.cpp - code for class 'VerseKey'- a standard Biblical
4  * verse key
5  *
6  * $Id: versekey.cpp 3822 2020-11-03 18:54:47Z 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 
41 
42 static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0};
43 static const SWClass 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 
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 
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 
83 VerseKey::VerseKey(const SWKey &ikey) : SWKey(ikey)
84 {
85  init();
86  copyFrom(ikey);
87 }
88 
89 
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 
105 VerseKey::VerseKey(const char *ikeyText) : SWKey(ikeyText)
106 {
107  init();
108  if (ikeyText)
109  parse();
110 }
111 
112 
114 {
115  init();
116  copyFrom(k);
117 }
118 
119 
120 /******************************************************************************
121  * VerseKey::setFromOther - Positions this VerseKey to another VerseKey
122  */
123 
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 //dbg_mapping SWLOGD("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;
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);
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);
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 
183 void VerseKey::positionFrom(const SWKey &ikey) {
184  error = 0;
185  const SWKey *fromKey = &ikey;
186  const ListKey *tryList = SWDYNAMIC_CAST(const ListKey, fromKey);
187  if (tryList) {
188  const SWKey *k = tryList->getElement();
189  if (k) fromKey = k;
190  }
191  const VerseKey *tryVerse = SWDYNAMIC_CAST(const 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) {
206  }
207  if (_compare(getLowerBound()) < 0) {
210  }
211 }
212 
213 
214 /******************************************************************************
215  * VerseKey::copyFrom - Equates this VerseKey to another VerseKey
216  */
217 
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());
228  if (ikey.isBoundSet()) {
231  }
232 }
233 
234 
235 /******************************************************************************
236  * VerseKey::copyFrom - Equates this VerseKey to another SWKey
237  */
238 
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  const ListKey *tryList = SWDYNAMIC_CAST(const ListKey, fromKey);
244  if (tryList) {
245  const SWKey *k = tryList->getElement();
246  if (k) fromKey = k;
247  }
248  const VerseKey *tryVerse = SWDYNAMIC_CAST(const 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 
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 
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 
291 
292  delete tmpClone;
293 
294  --instance;
295 }
296 
297 
298 void VerseKey::setVersificationSystem(const char *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 
316 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 
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 
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 
396 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 
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  */
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++) {
477  if (bn != i+1) {
478  char *abbr = 0;
480  strstrip(abbr);
481  SWLog::getSystemLog()->logWarning("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 
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  SWLOGD("%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 
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;
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 
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 = fromRoman(&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 = (SW_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 = (SW_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 = (SW_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 = (SW_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 = fromRoman(&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 = (SW_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 = (SW_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 = (SW_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 = (SW_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 
1113 {
1114  initBounds();
1115 
1116  lowerBound = lb.getIndex();
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
1127  boundSet = true;
1128 }
1129 
1130 
1131 /******************************************************************************
1132  * VerseKey::setUpperBound - sets / gets the upper boundary for this key
1133  */
1134 
1136 {
1137  initBounds();
1138 
1139  upperBound = ub.getIndex();
1145 
1146  // see setLowerBound comment, above
1148  boundSet = true;
1149 }
1150 
1151 
1152 /******************************************************************************
1153  * VerseKey::getLowerBound - gets the lower boundary for this key
1154  */
1155 
1157 {
1158  initBounds();
1159  if (!isAutoNormalize()) {
1165  }
1166  else {
1169  }
1170 
1171  return (*tmpClone);
1172 }
1173 
1174 
1175 /******************************************************************************
1176  * VerseKey::getUpperBound - sets / gets the upper boundary for this key
1177  */
1178 
1180 {
1181  initBounds();
1182  if (!isAutoNormalize()) {
1188  }
1189  else {
1192  }
1193 
1194  return (*tmpClone);
1195 }
1196 
1197 
1198 /******************************************************************************
1199  * VerseKey::clearBounds - clears bounds for this VerseKey
1200  */
1201 
1203  delete tmpClone;
1204  tmpClone = 0;
1205  boundSet = false;
1206 }
1207 
1208 
1209 void VerseKey::initBounds() const {
1210  if (!tmpClone) {
1211  tmpClone = (VerseKey *)this->clone();
1212  tmpClone->setAutoNormalize(false);
1213  tmpClone->setIntros(true);
1214  tmpClone->setTestament((BMAX[1])?2:1);
1215  tmpClone->setBook(BMAX[(BMAX[1])?1:0]);
1224 
1225  lowerBound = 0;
1231 
1232  }
1233  else tmpClone->setLocale(getLocale());
1234 }
1235 
1236 
1237 /******************************************************************************
1238  * VerseKey::getText - refreshes keytext before returning if cast to
1239  * a (char *) is requested
1240  */
1241 
1242 const char *VerseKey::getText() const {
1243  freshtext();
1244  return keytext;
1245 }
1246 
1247 
1248 const char *VerseKey::getShortText() const {
1249  static char *stext = 0;
1250  char buf[2047];
1251  freshtext();
1252  if (book < 1) {
1253  if (testament < 1)
1254  sprintf(buf, "[ Module Heading ]");
1255  else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
1256  }
1257  else {
1258  sprintf(buf, "%s %d:%d", getBookAbbrev(), chapter, verse);
1259  }
1260  stdstr(&stext, buf);
1261  return stext;
1262 }
1263 
1264 
1265 const char *VerseKey::getBookName() const {
1266  return getPrivateLocale()->translate(refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getLongName());
1267 }
1268 
1269 
1270 const char *VerseKey::getOSISBookName() const {
1271  return refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getOSISName();
1272 }
1273 
1274 
1275 const char *VerseKey::getBookAbbrev() const {
1276  return getPrivateLocale()->translate((SWBuf("prefAbbr_")+refSys->getBook(((testament>1)?BMAX[0]:0)+book-1)->getPreferredAbbreviation()).c_str());
1277 }
1278 
1279 
1280 /******************************************************************************
1281  * VerseKey::setPosition(SW_POSITION) - Positions this key
1282  *
1283  * ENT: p - position
1284  *
1285  * RET: *this
1286  */
1287 
1289  switch (p) {
1290  case POS_TOP: {
1291  const VerseKey *lb = &getLowerBound();
1292  testament = (lb->getTestament() || intros) ? lb->getTestament() : 1;
1293  book = (lb->getBook() || intros) ? lb->getBook() : 1;
1294  chapter = (lb->getChapter() || intros) ? lb->getChapter() : 1;
1295  verse = (lb->getVerse() || intros) ? lb->getVerse() : 1;
1296  suffix = lb->getSuffix();
1297  break;
1298  }
1299  case POS_BOTTOM: {
1300  const VerseKey *ub = &getUpperBound();
1301  testament = (ub->getTestament() || intros) ? ub->getTestament() : 1;
1302  book = (ub->getBook() || intros) ? ub->getBook() : 1;
1303  chapter = (ub->getChapter() || intros) ? ub->getChapter() : 1;
1304  verse = (ub->getVerse() || intros) ? ub->getVerse() : 1;
1305  suffix = ub->getSuffix();
1306  break;
1307  }
1308  case POS_MAXVERSE:
1309  suffix = 0;
1310  verse = 1;
1311  normalize();
1312  verse = getVerseMax();
1313  suffix = 0;
1314  break;
1315  case POS_MAXCHAPTER:
1316  suffix = 0;
1317  verse = 1;
1318  chapter = 1;
1319  normalize();
1320  chapter = getChapterMax();
1321  break;
1322  }
1323  normalize(true);
1324  popError(); // clear error from normalize
1325 }
1326 
1328  if (book < 1) return 0;
1329  const VersificationMgr::Book *b = refSys->getBook(((testament>1)?BMAX[0]:0)+book-1);
1330  return (b) ? b->getChapterMax() : -1;
1331 }
1332 
1334  if (book < 1) return 0;
1335  const VersificationMgr::Book *b = refSys->getBook(((testament>1)?BMAX[0]:0)+book-1);
1336  return (b) ? b->getVerseMax(chapter) : -1;
1337 }
1338 
1339 
1340 /******************************************************************************
1341  * VerseKey::increment - Increments key a number of verses
1342  *
1343  * ENT: step - Number of verses to jump forward
1344  *
1345  * RET: *this
1346  */
1347 
1348 void VerseKey::increment(int step) {
1349  // if we're not autonormalizing and we're already not normalized
1350  if (!autonorm && chapter > 0 && verse > getVerseMax()) {
1351  verse += step;
1352  checkBounds();
1353  return;
1354  }
1355  char ierror = 0;
1356  setIndex(getIndex() + step);
1357  while ((!verse) && (!intros) && (!ierror)) {
1358  setIndex(getIndex() + 1);
1359  ierror = popError();
1360  }
1361 
1362  error = (ierror) ? ierror : error;
1363 }
1364 
1365 
1366 /******************************************************************************
1367  * VerseKey::decrement - Decrements key a number of verses
1368  *
1369  * ENT: step - Number of verses to jump backward
1370  *
1371  * RET: *this
1372  */
1373 
1374 void VerseKey::decrement(int step) {
1375  // if we're not autonormalizing and we're already not normalized
1376  if (!autonorm && chapter > 0 && verse > getVerseMax()) {
1377  verse -= step;
1378  checkBounds();
1379  return;
1380  }
1381  char ierror = 0;
1382  setIndex(getIndex() - step);
1383  while ((!verse) && (!intros) && (!ierror)) {
1384  setIndex(getIndex() - 1);
1385  ierror = popError();
1386  }
1387  if ((ierror) && (!intros))
1388  (*this)++;
1389 
1390  error = (ierror) ? ierror : error;
1391 }
1392 
1393 
1394 /******************************************************************************
1395  * VerseKey::normalize - checks limits and normalizes if necessary (e.g.
1396  * Matthew 29:47 = Mark 2:2). If last verse is
1397  * exceeded, key is set to last Book CH:VS
1398  * RET: *this
1399  */
1400 
1401 void VerseKey::normalize(bool autocheck)
1402 {
1403 
1404  if ((!autocheck || autonorm) // only normalize if we were explicitely called or if autonorm is turned on
1405  ) {
1406  error = 0;
1407 
1408  while ((testament < 3) && (testament > 0)) {
1409 
1410 
1411  if (book > BMAX[testament-1]) {
1412  book -= (BMAX[testament-1] + (intros?1:0));
1413  testament++;
1414  continue;
1415  }
1416  if (book < (intros?0:1)) {
1417  if (--testament > 0) {
1418  book += (BMAX[testament-1] + (intros?1:0));
1419  }
1420  continue;
1421  }
1422 
1423 
1424  if (chapter > getChapterMax()) {
1425  chapter -= (getChapterMax() + (intros?1:0));
1426  book++;
1427  continue;
1428  }
1429  if (chapter < (intros?0:1)) {
1430  --book;
1431  if (book < (intros?0:1)) {
1432  if (--testament > 0) {
1433  book += (BMAX[testament-1] + (intros?1:0));
1434  }
1435  }
1436  chapter += (getChapterMax() + (intros?1:0));
1437  continue;
1438  }
1439 
1440 
1441  if (chapter > 0 && verse > getVerseMax()) {
1442  verse -= (getVerseMax() + (intros?1:0));
1443  chapter++;
1444  continue;
1445  }
1446  if (verse < (intros?0:1)) {
1447  if (--chapter < (intros?0:1)) {
1448  --book;
1449  if (book < (intros?0:1)) {
1450  if (--testament > 0) {
1451  book += (BMAX[testament-1] + (intros?1:0));
1452  }
1453  }
1454  chapter += (getChapterMax() + (intros?1:0));
1455  }
1456  verse += (getVerseMax() + (intros?1:0));
1457  continue;
1458  }
1459 
1460  break; // If we've made it this far (all failure checks continue) we're ok
1461  }
1462 
1463  if (testament > (BMAX[1]?2:1)) {
1464  testament = BMAX[1]?2:1;
1465  book = BMAX[testament-1];
1466  chapter = getChapterMax();
1467  verse = getVerseMax();
1469  }
1470 
1471  if (testament < 1) {
1472  error = ((!intros) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0;
1473  testament = ((intros) ? 0 : 1);
1474  book = ((intros) ? 0 : 1);
1475  chapter = ((intros) ? 0 : 1);
1476  verse = ((intros) ? 0 : 1);
1477  }
1478 
1479  // should we always perform bounds checks? Tried but seems to cause infinite recursion
1480  if (_compare(getUpperBound()) > 0) {
1483  }
1484  if (_compare(getLowerBound()) < 0) {
1487  }
1488  }
1489 }
1490 
1491 
1492 /******************************************************************************
1493  * VerseKey::getTestament - Gets testament
1494  *
1495  * RET: value of testament
1496  */
1497 
1499 {
1500  return testament;
1501 }
1502 
1503 
1504 /******************************************************************************
1505  * VerseKey::getBook - Gets book
1506  *
1507  * RET: value of book
1508  */
1509 
1510 char VerseKey::getBook() const
1511 {
1512  return book;
1513 }
1514 
1515 
1516 /******************************************************************************
1517  * VerseKey::getChapter - Gets chapter
1518  *
1519  * RET: value of chapter
1520  */
1521 
1523 {
1524  return chapter;
1525 }
1526 
1527 
1528 /******************************************************************************
1529  * VerseKey::getVerse - Gets verse
1530  *
1531  * RET: value of verse
1532  */
1533 
1535 {
1536  return verse;
1537 }
1538 
1539 
1540 /******************************************************************************
1541  * VerseKey::setTestament - Sets/gets testament
1542  *
1543  * ENT: itestament - value which to set testament
1544  * [MAXPOS(char)] - only get
1545  *
1546  */
1547 
1548 void VerseKey::setTestament(char itestament)
1549 {
1550  suffix = 0;
1551  verse = (intros) ? 0 : 1;
1552  chapter = (intros) ? 0 : 1;
1553  book = (intros) ? 0 : 1;
1554  testament = itestament;
1555  normalize(true);
1556 }
1557 
1558 
1559 /******************************************************************************
1560  * VerseKey::setBook - Sets/gets book
1561  *
1562  * ENT: ibook - value which to set book
1563  */
1564 
1565 void VerseKey::setBook(char ibook)
1566 {
1567  suffix = 0;
1568  verse = (intros) ? 0 : 1;
1569  chapter = (intros) ? 0 : 1;
1570  book = ibook;
1571  normalize(true);
1572 }
1573 
1574 
1575 
1576 /******************************************************************************
1577  * VerseKey::setBookName - Sets/gets book by name
1578  *
1579  * ENT: bname - book name/abbrev
1580  */
1581 
1582 void VerseKey::setBookName(const char *bname)
1583 {
1584  int bnum = getBookFromAbbrev(bname);
1585  if (bnum > -1) {
1586  if (bnum > BMAX[0]) {
1587  bnum -= BMAX[0];
1588  testament = 2;
1589  }
1590  else testament = 1;
1591  setBook(bnum);
1592  }
1593  else error = KEYERR_OUTOFBOUNDS;
1594 }
1595 
1596 
1597 /******************************************************************************
1598  * VerseKey::setChapter - Sets/gets chapter
1599  *
1600  * ENT: ichapter - value which to set chapter
1601  */
1602 
1603 void VerseKey::setChapter(int ichapter)
1604 {
1605  suffix = 0;
1606  verse = (intros) ? 0 : 1;
1607  chapter = ichapter;
1608  normalize(true);
1609 }
1610 
1611 
1612 /******************************************************************************
1613  * VerseKey::setVerse - Sets/gets verse
1614  *
1615  * ENT: iverse - value which to set verse
1616  * [MAXPOS(int)] - only get
1617  *
1618  * RET: if unchanged -> value of verse
1619  * if changed -> previous value of verse
1620  */
1621 
1622 void VerseKey::setVerse(int iverse)
1623 {
1624  suffix = 0;
1625  verse = iverse;
1626  normalize(true);
1627 }
1628 
1629 
1630 char VerseKey::getSuffix() const {
1631  return suffix;
1632 }
1633 
1634 void VerseKey::setSuffix(char suf) {
1635  suffix = suf;
1636 }
1637 
1638 /******************************************************************************
1639  * VerseKey::isAutoNormalize - gets flag that tells VerseKey to auto-
1640  * matically normalize itself when modified
1641  */
1642 
1644 {
1645  return autonorm;
1646 }
1647 
1648 void VerseKey::setAutoNormalize(bool iautonorm)
1649 {
1650  autonorm = iautonorm?1:0;
1651  normalize(true);
1652 }
1653 
1654 
1655 /******************************************************************************
1656  * VerseKey::setIntros - Sets flag that tells VerseKey to include
1657  * chap/book/testmnt/module introductions
1658  *
1659  * ENT: val - value which to set intros
1660  *
1661  */
1662 
1663 void VerseKey::setIntros(bool val)
1664 {
1665  intros = val;
1666  normalize(true);
1667 }
1668 
1670 {
1671  return intros;
1672 }
1673 
1674 
1675 /******************************************************************************
1676  * VerseKey::getIndex - Gets index based upon current verse
1677  *
1678  * RET: offset
1679  */
1680 
1682 {
1683  long offset;
1684 
1685  if (!testament) { // if we want module heading
1686  offset = 0;
1687  }
1688  else if (!book) { // we want testament heading
1689  offset = ((testament == 2) ? refSys->getNTStartOffset():0) + 1;
1690  }
1691  else {
1692  offset = refSys->getOffsetFromVerse((((testament>1)?BMAX[0]:0)+book-1), chapter, verse);
1693  }
1694  return offset;
1695 }
1696 
1697 
1698 /******************************************************************************
1699  * VerseKey::getTestamentIndex - Gets index based upon current verse
1700  *
1701  * RET: offset
1702  */
1703 
1705 {
1706  long offset = getIndex();
1707  return (testament > 1) ? offset - refSys->getNTStartOffset() : offset;
1708 }
1709 
1710 
1711 /******************************************************************************
1712  * VerseKey::setIndex - Sets index based upon current verse
1713  *
1714  * ENT: iindex - value to set index to
1715  *
1716  */
1717 
1718 void VerseKey::setIndex(long iindex)
1719 {
1720  // assert we're sane
1721  if (iindex < 0) {
1723  return;
1724  }
1725 
1726  int b;
1727  error = refSys->getVerseFromOffset(iindex, &b, &chapter, &verse);
1728  book = (unsigned char)b;
1729  testament = 1;
1730  if (book > BMAX[0]) {
1731  book -= BMAX[0];
1732  testament = 2;
1733  }
1734  // special case for Module and Testament heading
1735  if (book < 0) { testament = 0; book = 0; }
1736  if (chapter < 0) { book = 0; chapter = 0; }
1737 
1738  checkBounds();
1739 }
1740 
1742 
1743  long i = getIndex();
1744 
1745  initBounds();
1746  if (i > upperBound) {
1748  i = getIndex();
1750  }
1751  if (i < lowerBound) {
1754  }
1755 }
1756 
1757 
1758 /******************************************************************************
1759  * VerseKey::compare - Compares another SWKey object
1760  *
1761  * ENT: ikey - key to compare with this one
1762  *
1763  * RET: >0 if this versekey is greater than compare versekey
1764  * <0 <
1765  * 0 =
1766  */
1767 
1768 int VerseKey::compare(const SWKey &ikey)
1769 {
1770  const SWKey *testKey = &ikey;
1771  const VerseKey *vkey = (const VerseKey *)SWDYNAMIC_CAST(const VerseKey, testKey);
1772  if (vkey) {
1773  return _compare(*vkey);
1774  }
1775  const VerseKey ivkey = (const char *)ikey;
1776  return _compare(ivkey);
1777 }
1778 
1779 
1780 /******************************************************************************
1781  * VerseKey::_compare - Compares another VerseKey object
1782  *
1783  * ENT: ikey - key to compare with this one
1784  *
1785  * RET: >0 if this versekey is greater than compare versekey
1786  * <0 <
1787  * 0 =
1788  */
1789 
1790 int VerseKey::_compare(const VerseKey &ivkey)
1791 {
1792  unsigned long keyval1 = 0;
1793  unsigned long keyval2 = 0;
1794 
1795  keyval1 += getTestament() * 1000000000;
1796  keyval2 += ivkey.getTestament() * 1000000000;
1797  keyval1 += getBook() * 10000000;
1798  keyval2 += ivkey.getBook() * 10000000;
1799  keyval1 += getChapter() * 10000;
1800  keyval2 += ivkey.getChapter() * 10000;
1801  keyval1 += getVerse() * 50;
1802  keyval2 += ivkey.getVerse() * 50;
1803  keyval1 += (int)getSuffix();
1804  keyval2 += (int)ivkey.getSuffix();
1805  keyval1 = (keyval1 != keyval2) ? ((keyval1 > keyval2) ? 1 : -1) : 0; // -1 | 0 | 1
1806  return (int)keyval1;
1807 }
1808 
1809 
1810 const char *VerseKey::getOSISRef() const {
1811  static char buf[5][254];
1812  static int loop = 0;
1813 
1814  if (loop > 4)
1815  loop = 0;
1816 
1817  if (getVerse())
1818  sprintf(buf[loop], "%s.%d.%d", getOSISBookName(), getChapter(), getVerse());
1819  else if (getChapter())
1820  sprintf(buf[loop], "%s.%d", getOSISBookName(), getChapter());
1821  else if (getBook())
1822  sprintf(buf[loop], "%s", getOSISBookName());
1823  else buf[loop][0] = 0;
1824  return buf[loop++];
1825 }
1826 
1827 
1828 /******************************************************************************
1829  * VerseKey::getRangeText - returns parsable range text for this key
1830  */
1831 
1832 const char *VerseKey::getRangeText() const {
1833  if (isBoundSet() && lowerBound != upperBound) {
1834  SWBuf buf = (const char *)getLowerBound();
1835  buf += "-";
1836  buf += (const char *)getUpperBound();
1837  stdstr(&rangeText, buf.c_str());
1838  }
1839  else stdstr(&rangeText, getText());
1840  return rangeText;
1841 }
1842 
1843 
1844 /******************************************************************************
1845  * VerseKey::getShortRangeText - returns short parsable range text for this key
1846  */
1847 
1848 const char *VerseKey::getShortRangeText() const {
1849  if (isBoundSet() && (lowerBound != upperBound)) {
1850  SWBuf buf = getLowerBound().getShortText();
1851  buf += "-";
1855  buf.appendFormatted("%d", getUpperBound().getVerse());
1856  }
1858  && getUpperBound().getBook() == getLowerBound().getBook()) {
1860  }
1861  else buf += getUpperBound().getShortText();
1862  stdstr(&rangeText, buf.c_str());
1863  }
1864  else stdstr(&rangeText, getShortText());
1865  return rangeText;
1866 }
1867 
1868 
1869 /******************************************************************************
1870  * VerseKey::getOSISRefRangeText - returns parsable range text for this key
1871  */
1872 
1873 const char *VerseKey::getOSISRefRangeText() const {
1874  if (isBoundSet() && (lowerBound != upperBound)) {
1875  SWBuf buf = getLowerBound().getOSISRef();
1876  buf += "-";
1877  buf += getUpperBound().getOSISRef();
1878  stdstr(&rangeText, buf.c_str());
1879  }
1880  else stdstr(&rangeText, getOSISRef());
1881  return rangeText;
1882 }
1883 
1884 
1885 // TODO: this is static so we have no context. We can only parse KJV v11n now
1886 // possibly add a const char *versification = KJV param?
1887 const char *VerseKey::convertToOSIS(const char *inRef, const SWKey *lastKnownKey) {
1888  static SWBuf outRef;
1889 
1890  outRef = "";
1891 
1892  VerseKey defLanguage;
1893  ListKey verses = defLanguage.parseVerseList(inRef, (*lastKnownKey), true);
1894  const char *startFrag = inRef;
1895  for (int i = 0; i < verses.getCount(); i++) {
1896  SWKey *element = verses.getElement(i);
1897 // VerseKey *element = SWDYNAMIC_CAST(VerseKey, verses.GetElement(i));
1898  SWBuf buf;
1899  // TODO: This code really needs to not use fixed size arrays
1900  char frag[800];
1901  char preJunk[800];
1902  char postJunk[800];
1903  memset(frag, 0, 800);
1904  memset(preJunk, 0, 800);
1905  memset(postJunk, 0, 800);
1906  while ((*startFrag) && (strchr(" {}:;,()[].", *startFrag))) {
1907  outRef += *startFrag;
1908  startFrag++;
1909  }
1910  memmove(frag, startFrag, (size_t)((const char *)element->userData - startFrag) + 1);
1911  frag[((const char *)element->userData - startFrag) + 1] = 0;
1912  int j;
1913  for (j = strlen(frag)-1; j && (strchr(" {}:;,()[].", frag[j])); j--);
1914  if (frag[j+1])
1915  strcpy(postJunk, frag+j+1);
1916  frag[j+1]=0;
1917  startFrag += ((const char *)element->userData - startFrag) + 1;
1918  buf = "<reference osisRef=\"";
1919  buf += element->getOSISRefRangeText();
1920  buf += "\">";
1921  buf += frag;
1922  buf += "</reference>";
1923  buf += postJunk;
1924 
1925  outRef += buf;
1926 
1927  }
1928  if (startFrag < (inRef + strlen(inRef)))
1929  outRef += startFrag;
1930  return outRef.c_str();
1931 }
virtual int getChapterMax() const
Definition: versekey.cpp:1327
virtual void setIndex(long iindex)
Definition: versekey.cpp:1718
virtual bool isBoundSet() const
Definition: swkey.h:189
#define TOP
Definition: swkey.h:68
#define SWORD_NAMESPACE_START
Definition: defs.h:39
SWBuf & appendFormatted(const char *format,...)
Definition: swbuf.cpp:81
virtual SWKey * clone() const
Definition: versekey.cpp:278
#define POS_MAXVERSE
Definition: versekey.h:39
signed int chapter
Definition: versekey.h:97
virtual int getCount() const
Definition: listkey.cpp:211
Definition: swbuf.h:47
virtual const char * getBookName() const
Definition: versekey.cpp:1265
virtual int compare(const SWKey &ikey)
Definition: versekey.cpp:1768
#define MAXVERSE
Definition: versekey.h:43
#define POS_MAXCHAPTER
Definition: versekey.h:40
virtual void setPosition(SW_POSITION newpos)
Definition: versekey.cpp:1288
static bool hasUTF8Support()
Definition: stringmgr.h:58
VerseComponents lowerBoundComponents
Definition: versekey.h:89
#define SW_toupper(c)
Definition: utilstr.h:67
signed char testament
Definition: versekey.h:95
virtual bool isIntros() const
Definition: versekey.cpp:1669
long getOffsetFromVerse(int book, int chapter, int verse) const
static SWLog * getSystemLog()
Definition: swlog.cpp:53
virtual void setBookName(const char *bname)
Definition: versekey.cpp:1582
virtual struct abbrev * getBookAbbrevs(int *retSize)
Definition: swlocale.cpp:178
const SWClass * myClass
Definition: swobject.h:55
VerseKey & getLowerBound() const
Definition: versekey.cpp:1156
char * keytext
Definition: swkey.h:102
virtual void setChapter(int ichapter)
Definition: versekey.cpp:1603
signed char suffix
Definition: versekey.h:99
virtual void copyFrom(const SWKey &ikey)
Definition: versekey.cpp:239
virtual bool isAutoNormalize() const
Definition: versekey.cpp:1643
const VersificationMgr::System * refSys
Definition: versekey.h:61
SWBuf v11n
Definition: osis2mod.cpp:107
virtual void positionFrom(const SWKey &ikey)
Definition: versekey.cpp:183
signed int verse
Definition: versekey.h:98
const char * ab
VerseKey(const char *ikey=0)
Definition: versekey.cpp:105
char intros
Definition: versekey.h:69
virtual void positionFrom(const SWKey &ikey)
Definition: swkey.h:180
static StringMgr * getSystemStringMgr()
Definition: stringmgr.cpp:197
virtual void setTestament(char itestament)
Definition: versekey.cpp:1548
int stricmp(const char *s1, const char *s2)
Definition: utilstr.cpp:194
virtual SWKey * getElement(int pos=-1)
Definition: listkey.cpp:270
virtual const char * getText() const
Definition: versekey.cpp:1242
else preg translate
Definition: regex.c:8111
void setLowerBound(const VerseKey &lb)
Definition: versekey.cpp:1112
bool boundSet
Definition: swkey.h:104
ListKey internalListKey
Definition: versekey.h:59
static SWORD_NAMESPACE_START const char * classes[]
Definition: versekey.cpp:42
static const char * convertToOSIS(const char *inRef, const SWKey *defaultKey)
Definition: versekey.cpp:1887
SWORD_NAMESPACE_START char * stdstr(char **ipstr, const char *istr, unsigned int memPadFactor=1)
Definition: utilstr.h:44
static int instance
Definition: versekey.h:58
virtual int _compare(const VerseKey &ikey)
Definition: versekey.cpp:1790
virtual char getSuffix() const
Definition: versekey.cpp:1630
void init()
Definition: swkey.cpp:68
char getVerseFromOffset(long offset, int *book, int *chapter, int *verse) const
const Book * getBook(int number) const
VerseComponents upperBoundComponents
Definition: versekey.h:89
virtual void setIntros(bool val)
Definition: versekey.cpp:1663
virtual int getChapter() const
Definition: versekey.cpp:1522
const char * c_str() const
Definition: swbuf.h:158
virtual char parse(bool checkNormalize=true)
Definition: versekey.cpp:326
virtual int getBookFromAbbrev(const char *abbr) const
Definition: versekey.cpp:396
virtual void setVersificationSystem(const char *name)
Definition: versekey.cpp:298
#define POS_TOP
Definition: swkey.h:65
virtual char * upperUTF8(char *text, unsigned int max=0) const
Definition: stringmgr.cpp:223
static const SWClass classdef(classes)
SWORD_NAMESPACE_START char isRoman(const char *str, int maxchars)
Definition: roman.cpp:31
virtual void setSuffix(char isuffix)
Definition: versekey.cpp:1634
virtual ListKey parseVerseList(const char *buf, const char *defaultKey=0, bool expandRange=false, bool useChapterAsVerse=false)
Definition: versekey.cpp:515
virtual void setVerse(int iverse)
Definition: versekey.cpp:1622
virtual const char * getVersificationSystem() const
Definition: versekey.cpp:316
virtual void copyFrom(const SWKey &ikey)
Definition: swkey.cpp:173
virtual const char * getShortRangeText() const
Definition: versekey.cpp:1848
virtual const char * getOSISRefRangeText() const
Definition: versekey.cpp:1873
virtual char getBook() const
Definition: versekey.cpp:1510
void translateVerse(const System *dstSys, const char **book, int *chapter, int *verse, int *verse_end) const
virtual void increment(int steps=1)
Definition: versekey.cpp:1348
virtual int getVerse() const
Definition: versekey.cpp:1534
void freshtext() const
Definition: versekey.cpp:357
void initBounds() const
Definition: versekey.cpp:1209
virtual const char * getOSISRef() const
Definition: versekey.cpp:1810
virtual const char * getOSISRefRangeText() const
Definition: swkey.cpp:203
int getBookNumberByOSISName(const char *bookName) const
VerseKey & getUpperBound() const
Definition: versekey.cpp:1179
#define SWDYNAMIC_CAST(className, object)
Definition: defs.h:47
void setUpperBound(const VerseKey &ub)
Definition: versekey.cpp:1135
#define KEYERR_OUTOFBOUNDS
Definition: swkey.h:35
const char * getOSISName() const
const char * getPreferredAbbreviation() const
virtual const char * translate(const char *text)
Definition: swlocale.cpp:106
virtual void normalize(bool autocheck=false)
Definition: versekey.cpp:1401
const char * getName() const
SWLocale * getPrivateLocale() const
Definition: swkey.cpp:111
virtual char popError()
Definition: swkey.cpp:147
const int * getBMAX() const
#define POS_BOTTOM
Definition: swkey.h:66
unsigned long long SW_u64
Definition: sysdata.h:56
virtual int getVerseMax() const
Definition: versekey.cpp:1333
void logWarning(const char *fmt,...) const
Definition: swlog.cpp:74
void clearBounds() const
Definition: versekey.cpp:1202
char * rangeText
Definition: swkey.h:103
char * strstrip(char *istr)
Definition: utilstr.cpp:118
virtual void setBook(char ibook)
Definition: versekey.cpp:1565
virtual char * upperLatin1(char *text, unsigned int max=0) const
Definition: stringmgr.cpp:327
virtual ~VerseKey()
Definition: versekey.cpp:290
int strnicmp(const char *s1, const char *s2, int len)
Definition: utilstr.cpp:180
int fromRoman(const char *str)
Definition: roman.cpp:39
virtual const char * getShortText() const
Definition: versekey.cpp:1248
virtual const char * getRangeText() const
Definition: versekey.cpp:1832
signed char book
Definition: versekey.h:96
char error
Definition: swkey.h:106
int getVerseMax(int chapter) const
virtual long getTestamentIndex() const
Definition: versekey.cpp:1704
SW_u64 userData
Definition: swkey.h:115
const char * getLongName() const
const char * osis
void setFromOther(const VerseKey &vk)
Definition: versekey.cpp:124
char autonorm
Definition: versekey.h:65
#define SWORD_NAMESPACE_END
Definition: defs.h:40
#define SWLOGD(...)
Definition: defs.h:187
Definition: swkey.h:77
#define MAXCHAPTER
Definition: versekey.h:44
void checkBounds()
Definition: versekey.cpp:1741
static VersificationMgr * getSystemVersificationMgr()
VerseKey * tmpClone
Definition: versekey.h:85
int BMAX[2]
Definition: versekey.h:120
long lowerBound
Definition: versekey.h:84
virtual void setAutoNormalize(bool iautonorm)
Definition: versekey.cpp:1648
virtual const char * getOSISBookName() const
Definition: versekey.cpp:1270
void setLocale(const char *name)
Definition: swkey.h:225
const System * getVersificationSystem(const char *name) const
virtual const char * getBookAbbrev() const
Definition: versekey.cpp:1275
virtual void decrement(int steps=1)
Definition: versekey.cpp:1374
long upperBound
Definition: versekey.h:84
void validateCurrentLocale() const
Definition: versekey.cpp:473
char * getLocale() const
Definition: swkey.h:224
virtual char getTestament() const
Definition: versekey.cpp:1498
static const char LOG_DEBUG
Definition: swlog.h:46
virtual long getIndex() const
Definition: versekey.cpp:1681