chiark / gitweb /
Changelog for 4.38
[bible-kjv.git] / brl.c
1 /* -*-C-*-
2 *******************************************************************************
3 *
4 * File:         brl.c
5 * RCS:          $Header: /home/matthew/cvs/bible-kjv-4.10/brl.c,v 2.7 2005/01/23 11:19:58 matthew Exp $
6 * Description:  Bible Retrieval Library
7 * Author:       Chip Chapin, Hewlett Packard Company
8 * Created:      Jan 14 1989, Chip Chapin
9 * Modified:     Mon Apr 26 11:11:59 1993 (Chip Chapin) chip@hpclbis
10 * Language:     C
11 * Package:      Bible Retrieval System
12 * Status:       Experimental (Do Not Distribute)
13 *
14 *******************************************************************************
15 *
16 * Revisions:
17 *
18 * Thu Dec 30 1999 (Oliver Elphick) olly@lfix.co.uk
19 *  Increase VBSIZE from 512 to 550, because very long verses were
20 *  being truncated in pretty printing.
21 * Thu Apr 22 14:39:43 1993 (Chip Chapin) chip@hpclbis
22 *  Fix bad return value from brl_printverse when no errors occurred.
23 *  Support non-POSIX sprintf.
24 * Tue Jan  5 18:23:41 1993 (Chip Chapin) chip@hpclbis
25 *  Change how current context is handled.  Now use single var
26 *  "brl_cur_vnum", and make our caller responsible for updating it.
27 *  brl_getverse is now commented out.  Nobody uses it anymore.
28 *  Added file parameter to brl_printverse.
29 * Mon Jan  4 10:11:19 1993 (Chip Chapin) chip@hpclbis
30 *  Renamed verse_spec() as brl_verse_spec() and exposed it to callers.
31 * Thu Dec 24 11:04:42 1992 (Chip Chapin) chip@hpclbis
32 *  Added brl_cur_ref(), and converted all absolute verse refs to use
33 *  "ref_t", instead of "int" and "long".
34 * Tue Dec 22 17:11:41 1992 (Chip Chapin) chip@hpclbis
35 *  Fix bug parsing "ma.." books.
36 *  Added tolower fix submitted by Stephen North, AT&T.
37 * Mon Dec 21 19:13:10 1992 (Chip Chapin) chip@hpclbis
38 *  Fixed minor verse ref parsing bugs.
39 *  Added brl_num_to_ref to support concordance.
40 *******************************************************************************
41 *
42 * $Log: brl.c,v $
43 * Revision 2.7  2005/01/23 11:19:58  matthew
44 * explicit casts
45 *
46 * Revision 2.6  2005/01/22 17:47:40  matthew
47 * remove improper function declarations
48 *
49 * Revision 2.5  2005/01/22 16:40:34  matthew
50 * cast return value of strlen to signed int
51 *
52 * Revision 2.4  2005/01/21 19:38:25  matthew
53 * Code clean-up - initialise vref, remove unused variable.
54 *
55 * Revision 2.3  2005/01/21 19:07:31  matthew
56 * disambiguate an else
57 *
58 * Revision 2.2  2005/01/21 19:05:51  matthew
59 * Prototype all functions
60 *
61 * Revision 2.1  2003/01/08 15:50:53  matthew
62 * applied debian patch
63 *
64 * Revision 2.0  2003/01/08 15:29:52  matthew
65 * versions collected from the net
66 *
67  * Revision 1.16  93/04/26  11:18:12  11:18:12  chip (Chip Chapin)
68  * Release 4.00
69  * Public release of portable datafile version.
70  * 
71  * Revision 1.15  93/04/23  13:08:01  13:08:01  chip (Chip Chapin)
72  * PORTABILITY RELEASE
73  * This version supports portable data files, usable on machines with
74  * differing native byte-orders.
75  * Also, this version compiles and runs on non-HPUX systems.  It has been
76  * tested on SunOS 4.? and ULTRIX 4.?, using SPARC and DEC 3100 hardware
77  * respectively.  Note that the data file format has rolled again.
78  * 
79  * Revision 1.14  93/01/05  19:05:30  19:05:30  chip (Chip Chapin)
80  * Release 3.00: (not for distribution)
81  * Fixed errors (blank lines) in bible.data file.  Data file is not compatible
82  * with previous (1.x and 2.x) distributions.  Further changes pending.
83  * Rewrote context handling, and added "<" and ">" commands.
84  * Tools for building brl-index are now part of release.
85  * 
86  * Revision 1.13  93/01/05  10:49:09  10:49:09  chip (Chip Chapin)
87  * Release 2.2, Added ?w command and line formatting to ?l output.
88  * 
89  * Revision 1.12  93/01/04  16:20:57  16:20:57  chip (Chip Chapin)
90  * Release 2.1, implements ?in and ?or commands.
91  * 
92  * Revision 1.11  92/12/24  11:09:14  11:09:14  chip (Chip Chapin)
93  * Release 2.04.  Include verse ref in prompt line.
94  * 
95  * Revision 1.10  92/12/23  14:10:43  14:10:43  chip (Chip Chapin)
96  * Release 2.03: minor tweaks and bug fixes.
97  * 
98  * Revision 1.9  92/12/22  18:17:06  18:17:06  chip (Chip Chapin)
99  * Minor tweaks for release 2.02.
100  * 
101  * Revision 1.8  92/12/21  20:00:47  20:00:47  chip (Chip Chapin)
102  * Release 2.0.  This release adds the concordance, and some small fixes.
103  * 
104  * Revision 1.7  89/10/02  22:20:45  22:20:45  chip (Chip Chapin)
105  * Fix bugs: looping when lwidth very small,
106  * and blank-line after verse range not printing verse AFTER the last
107  * verse of the range.
108  * 
109  * Revision 1.6  89/09/14  20:33:45  20:33:45  chip (Chip Chapin)
110  * Release 1-2.  Supports -f and -l options for formatting the output.
111  * Updates primarily brl.c, bible.c, and bible.1.
112  * 
113  * Revision 1.5  89/09/13  21:49:13  21:49:13  chip (Chip Chapin)
114  * Implement -f and -l options for pretty-printing and linewidth limitation.
115  * 
116  * Revision 1.4  89/09/08  13:22:47  13:22:47  chip (Chip Chapin)
117  * Better error checking on verse syntax; automatic test suite.
118  * 
119  * Revision 1.3  89/09/08  09:01:22  09:01:22  chip (Chip Chapin)
120  * Bug fix and simplification: send whole input lines or arguments to BRL,
121  * and let BRL worry about multiple references.  We don't care.
122  * 
123  * Revision 1.2  89/09/05  20:22:59  20:22:59  chip (Chip Chapin)
124  * Workaround 6.2 C compiler enum gripes.
125  * 
126  * Revision 1.1  89/09/05  17:49:15  17:49:15  chip (Chip Chapin)
127  * Initial revision
128  * 
129 *******************************************************************************
130 */
131
132 /*----------------------------------------------------------------------
133 |   NAME:
134 |       brl.c
135 |
136 |   PURPOSE:
137 |       Provide a set of routines for retrieving Bible text from
138 |       the Text Storage Library (tsl).  All of these routines
139 |       should take a high-level view of the text itself; they may
140 |       know something about the structure of the Bible, but
141 |       should *not* depend in any way upon the manner in which
142 |       the text is stored.  Call on the TSL for that.
143 |
144 |   FUNCTIONS:
145 |       Grammar Functions: Used within the BRL to parse verse
146 |       specifications.
147 |               brl_verse_spec (also called from applications)
148 |               verse_id
149 |               verse_continuation
150 |               
151 |       Access Functions: Called from application programs to
152 |       retrieve Bible text.
153 |               brl_getverse
154 |               brl_printverse
155 |       
156 |       Utility Functions: Called from application programs.
157 |               brl_init
158 |               brl_close
159 |               brl_num_to_ref
160 |
161 |   HISTORY:
162 |       890114 cc Initial creation.
163 |       890824 cc Improved partitioning between BRL and TSL.  Created
164 |               brl_getverse to hide brl_verse_spec and verse numbers from
165 |               user programs.  Fix bug in verse_id parsing "jn".
166 |       921217 cc Added brl_num_to_ref.
167 |
168 \*----------------------------------------------------------------------*/
169
170
171 #include <stdio.h>
172 #include <stdlib.h>
173 #include <ctype.h>
174 #include <string.h>
175 #include "tsl.h"
176 #include "brl.h"
177
178 #define FALSE   (0)
179
180 char *booknamestr[] = { "Genesis",
181                            "Exodus",
182                            "Leviticus",
183                            "Numbers",
184                            "Deuteronomy",
185                            "Joshua",
186                            "Judges",
187                            "Ruth",
188                            "1 Samuel",
189                            "2 Samuel",
190                            "1 Kings",
191                            "2 Kings",
192                            "1 Chronicles",
193                            "2 Chronicles",
194                            "Ezra",
195                            "Nehemiah",
196                            "Esther",
197                            "Job",
198                            "Psalms",
199                            "Proverbs",
200                            "Ecclesiastes",
201                            "Song of Solomon",
202                            "Isaiah",
203                            "Jeremiah",
204                            "Lamentations",
205                            "Ezekiel",
206                            "Daniel",
207                            "Hosea",
208                            "Joel",
209                            "Amos",
210                            "Obadiah",
211                            "Jonah",
212                            "Micah",
213                            "Nahum",
214                            "Habakkuk",
215                            "Zephaniah",
216                            "Haggai",
217                            "Zechariah",
218                            "Malachi",
219                            "Matthew",
220                            "Mark",
221                            "Luke",
222                            "John",
223                            "Acts",
224                            "Romans",
225                            "1 Corinthians",
226                            "2 Corinthians",
227                            "Galatians",
228                            "Ephesians",
229                            "Philippians",
230                            "Colossians",
231                            "1 Thessalonians",
232                            "2 Thessalonians",
233                            "1 Timothy",
234                            "2 Timothy",
235                            "Titus",
236                            "Philemon",
237                            "Hebrews",
238                            "James",
239                            "1 Peter",
240                            "2 Peter",
241                            "1 John",
242                            "2 John",
243                            "3 John",
244                            "Jude",
245                            "Revelation",
246                            "BAD"
247 };
248
249 /* Standard abbreviations */
250 char *bookabbrvstr[] = { "Gen",
251                            "Ex",
252                            "Lev",
253                            "Num",
254                            "Dt",
255                            "Jsh",
256                            "Jdg",
257                            "Ru",
258                            "1Sa",
259                            "2Sa",
260                            "1Ki",
261                            "2Ki",
262                            "1Ch",
263                            "2Ch",
264                            "Ezr",
265                            "Ne",
266                            "Es",
267                            "Job",
268                            "Ps",
269                            "Pr",
270                            "Ec",
271                            "SoS",
272                            "Is",
273                            "Je",
274                            "Lam",
275                            "Ezk",
276                            "Da",
277                            "Ho",
278                            "Jl",
279                            "Am",
280                            "Ob",
281                            "Jon",
282                            "Mi",
283                            "Na",
284                            "Hab",
285                            "Zep",
286                            "Hag",
287                            "Zec",
288                            "Mal",
289                            "Mt",
290                            "Mk",
291                            "Lu",
292                            "Jn",
293                            "Ac",
294                            "Ro",
295                            "1Co",
296                            "2Co",
297                            "Ga",
298                            "Ep",
299                            "Php",
300                            "Co",
301                            "1Th",
302                            "2Th",
303                            "1Ti",
304                            "2Ti",
305                            "Ti",
306                            "Phm",
307                            "He",
308                            "Ja",
309                            "1Pe",
310                            "2Pe",
311                            "1Jn",
312                            "2Jn",
313                            "3Jn",
314                            "Jde",
315                            "Re",
316                            "BAD"
317 };
318
319
320 ref_t brl_cur_vnum;             /* Current context. */
321
322 /* Name of text.  This should really be initialized or provided by tsl */
323 char brl_textname[]="KJV";
324
325
326
327
328 /*----------------------------------------------------------------------
329 |
330 |       Verse Specification Grammar
331 |       
332 |       The following routines implement the grammar by which
333 |       verses may be specified.
334 |       See each routine, starting with brl_verse_spec, for the grammar.
335 |
336 \*----------------------------------------------------------------------*/
337
338 #define getnum(s,n)     n=0; get_nonblank(s); \
339     while (isdigit((int)*s)) n = 10 * n + (*s++ - '0');
340
341
342
343 int get_book(char **s,int book)
344 /*----------------------------------------------------------------------
345 |   NAME:
346 |       get_book
347 |
348 |   ALGORITHM:
349 |       Parse a bible book name or abbreviation.  Defaults to the
350 |       book number passed in "book" if no book is specified.
351 |       
352 |       Returns the number of the book, 0..65, -1 if an
353 |       unrecognizable book name is given, or -2 if no book name
354 |       is given (needs a bit of cleanup!).
355 |
356 |   HISTORY:
357 |       890902 cc Extracted from verse_id.
358 |       890904 cc Revised to allow non-fatal errors.
359 |
360 \*----------------------------------------------------------------------*/
361 {
362     int c;
363     char *s1;
364
365     book = -2;          /* assume no book is given */
366     get_nonblank(*s);
367     s1 = *s;
368     if (*s1 && (isalpha((int)*s1) || isalpha((int)*(s1+1)))) {
369         switch (c = *s1++) {
370           case '1':     /* 1Sa, 1Ki, 1Ch, 1Co, 1Th, 1Ti, 1Pe, 1Jn */
371           case '2':     /* 2Sa, 2Ki, 2Ch, 2Co, 2Th, 2Ti, 2Pe, 2Jn */
372           case '3':     /* 3Jn */
373             get_nonblank(s1);
374             switch (*s1++) {
375               case 'c': 
376                 if (*s1=='h') book= (int)CHRON1;
377                 else if (*s1=='o') book= (int)COR1;
378                 else {
379                     tsl_error( FALSE, BADBOOK, *s );
380                     return -1;
381                 }
382                 break;
383               case 'j':
384                 book= (int)JOHN1;
385                 break;
386               case 'k':
387                 book= (int)KINGS1;
388                 break;
389               case 'p':
390                 book= (int)PET1;
391                 break;
392               case 's':
393                 book= (int)SAM1;
394                 break;
395               case 't':
396                 if (*s1=='h') book= (int)THESS1;
397                 else if (*s1=='i') book= (int)TIM1;
398                 else {
399                     tsl_error( FALSE, BADBOOK, *s );
400                     return -1;
401                 }
402                 break;
403               default:
404                 tsl_error( FALSE, BADBOOK, *s );
405                 return -1;
406             }
407             if (c != '1') book++;
408             if (c == '3'){ 
409               if (book == (int)JOHN2) book++;
410               else {
411                 tsl_error( FALSE, BADBOOK, *s );
412                 return -1;
413               }
414             }
415             break;
416           case 'a': /* amos, acts */
417             c = *s1++;
418             if (c == 'm') book = (int)AMOS;
419             else if (c == 'c') book = (int)ACTS;
420             else {
421                 tsl_error( FALSE, BADBOOK, *s );
422                 return -1;
423             }
424             break;
425           case 'c': /* col */
426             book = (int)COLOS;
427             break;
428           case 'd': /* deut, dan */
429             c = *s1++;
430             if (c == 'e' || c == 't') book = (int)DEUT; /* de, dt */
431             else if (c == 'a') book = (int)DANIEL;
432             else {
433                 tsl_error( FALSE, BADBOOK, *s );
434                 return -1;
435             }
436             break;
437           case 'e': /* exodus, ezra, esther, eccl, ezekiel, eph */
438             c = *s1++;
439             if (c == 'x') book = (int)EXODUS;   /* ex */
440             else if (c == 's') book = (int)ESTHER;      /* es */
441             else if (c == 'c') book = (int)ECCL;        /* ec */
442             else if (c == 'p') book = (int)EPH; /* ep */
443             else {
444                 if (c == 'z') c = *s1++;
445                 if (c == 'r') book = (int)EZRA; /* ezr, er */
446                 else if (c == 'e' || c == 'k')
447                     book = (int)EZEKIEL; /* eze,ee,ezk,ek */
448                 else {
449                     tsl_error( FALSE, BADBOOK, *s );
450                     return -1;
451                 }
452             }
453             break;
454           case 'g': /* gen, gal */
455             c = *s1++;
456             if (c == 'e' || c == 'n') book = (int)GENESIS;
457             else if (c == 'a' || c == 'l') book = (int)GAL;
458             else {
459                 tsl_error( FALSE, BADBOOK, *s );
460                 return -1;
461             }
462             break;
463           case 'h': /* hebrews, hosea, habakkuk, haggai */
464             c = *s1++;
465             if (c == 'o') book = (int)HOSEA;            /* ho */
466             else if (c == 'e' || c == 'b')
467                 book = (int)HEBREWS;    /* he, hb (more likely than Hab.) */
468             else {
469                 if (c == 'a') c = *s1++;
470                 if (c == 'b') book = (int)HABAK;        /* hab */
471                 else if (c == 'g') book = (int)HAGGAI; /* hag, hg */
472                 else {
473                     tsl_error( FALSE, BADBOOK, *s );
474                     return -1;
475                 }
476             }
477             break;
478           case 'i': /* isaiah */
479             book = (int)ISAIAH;
480             break;
481           case 'j': /* josh, judges, job, jer, joel, jonah, john, jam, jude */
482             c = *s1++;
483             if (c == 'a') book = (int)JAMES;            /* ja */
484             else if (c == 'e' || c == 'r') book = (int)JEREM;   /* je, jr */
485             else if (c == 'b') book = (int)JOB; /* jb */
486             else if (c == 'd') {
487                 if ((c = *s1++) == 'g') book = (int)JUDGES;     /* jdg */
488                 else if (c == 'e') book = (int)JUDE;    /* jde */
489                 else {
490                     tsl_error( FALSE, BADBOOK, *s );
491                     return -1;
492                 }
493             }
494             else if (c == 'g') book = (int)JUDGES;      /* jg */
495             else if (c == 'l') book = (int)JOEL;        /* jl */
496             else if (c == 'n') book = (int)JOHN;        /* jn */
497             else if (c == 's') {
498                 if ((c = *s1++) == 'h') book = (int)JOSHUA;     /* jsh */
499                 else {
500                     tsl_error( FALSE, BADBOOK, *s );
501                     return -1;
502                 }
503             }
504             else if (c == 'u') {
505                 if ((c = *s1++) == 'd') c = *s1++;
506                 if (c == 'g') book = (int)JUDGES;       /* judg, jug (ha-ha) */
507                 else if (c == 'e') book = (int)JUDE;    /* jude, jue (ha-ha) */
508                 else {
509                     tsl_error( FALSE, BADBOOK, *s );
510                     return -1;
511                 }
512             }
513             else if (c == 'o') {
514                 if ((c = *s1++) == 's') book = (int)JOSHUA;     /* jos */
515                 else if (c == 'b') book = (int)JOB;             /* job */
516                 else if (c == 'e') book = (int)JOEL;            /* joe */
517                 else if (c == 'n') book = (int)JONAH;           /* jon */
518                 else if (c == 'h') book = (int)JOHN;            /* joh */
519                 else {
520                     tsl_error( FALSE, BADBOOK, *s );
521                     return -1;
522                 }
523             }
524             else {
525                 tsl_error( FALSE, BADBOOK, *s );
526                 return -1;
527             }
528             break;
529           case 'l': /* lev, lam, luke */
530             c = *s1++;
531             if (c == 'e' || c == 'v') book = (int)LEVIT;
532             else if (c == 'a' || c == 'm') book = (int)LAMENT;
533             else if (c == 'u' || c == 'k') book = (int)LUKE;
534             else {
535                 tsl_error( FALSE, BADBOOK, *s );
536                 return -1;
537             }
538             break;
539           case 'm': /* micah, malachi, matt, mark */
540             c = *s1++;
541             if (c == 'i' || c == 'c') book = (int)MICAH;
542             else {
543                 /* mal, ml, mat, mt, mar, mr, mk (and "mak"!) */
544                 if (c == 'a') c = *s1++;
545                 if (c == 'l') book = (int)MALACHI;
546                 else if (c == 't') book = (int)MATT;
547                 else if (c == 'r' || c == 'k') book = (int)MARK;
548                 else {
549                     tsl_error( FALSE, BADBOOK, *s );
550                     return -1;
551                 }
552             }
553             break;
554           case 'n': /* num, neh, nahum */
555             c = *s1++;
556             if (c == 'u' || c == 'm') book = (int)NUM;
557             else if (c == 'e') book = (int)NEHEM;
558             else if (c == 'a') book = (int)NAHUM;
559             else {
560                 tsl_error( FALSE, BADBOOK, *s );
561                 return -1;
562             }
563             break;
564           case 'o': /* obadiah */
565             book = (int)OBADIAH;
566             break;
567           case 'p': /* psalms, proverbs, philippians, philemon */
568             c = *s1++;
569             if (c == 's') book = (int)PSALMS;
570             else if (c == 'r') book = (int)PROV;
571             else {
572                 if (c == 'h') c = *s1++;
573                 if (c == 'i') c = *s1++;
574                 if (c == 'l') c = *s1++;
575                 if (c == 'i' || c == 'p' || !isalpha(c)) {
576                     /* phili, philp, phil, phi, ph, php, pp, etc. */
577                     /* This gives preference to Philippians over Philemon */
578                     book = (int)PHILIP;
579                          if (!isalpha(c)) s1--; /* point back to the chapter number */
580                 } else if (c == 'e' || c == 'm') book = (int)PHILEM;
581                 else {
582                     tsl_error( FALSE, BADBOOK, *s );
583                     return -1;
584                 }
585             }
586             break;
587           case 'q': /* Not a book, but it makes typing easier */
588             brl_close();
589             exit( 0 );
590             break;
591           case 'r': /* ruth, romans, revelation */
592             c = *s1++;
593             if (c == 'u' || c == 't') book = (int)RUTH;
594             else if (c == 'o' || c == 'm') book = (int)ROMANS;
595             else if (c == 'e' || c == 'v') book = (int)REV;
596             else {
597                 tsl_error( FALSE, BADBOOK, *s );
598                 return -1;
599             }
600             break;
601           case 's': /* song */
602             book = (int)SONG;
603             break;
604           case 't': /* titus */
605             book = (int)TITUS;
606             break;
607           case 'z': /* zeph, zech */
608             if ((c = *s1++) == 'e') c = *s1++;
609             if (c == 'p') book = (int)ZEPH;
610             else if (c == 'c') book = (int)ZECH;
611             else {
612                 tsl_error( FALSE, BADBOOK, *s );
613                 return -1;
614             }
615             break;
616           default:
617             tsl_error( FALSE, BADBOOK, *s );
618             return -1;
619         } /* switch on first letter of book name */
620         
621         /* Skip any remaining letters in book name */
622         while (isalpha((int)*s1)) s1++;
623         get_nonblank(s1);
624     }
625     *s = s1;
626     return book;
627 } /* get_book */
628
629
630
631 int verse_continuation(char **s,int book,int chapter,int verse,ref_t absverse)
632 /*----------------------------------------------------------------------
633 |   NAME:
634 |       verse_continuation
635 |
636 |   ALGORITHM:
637 |       <verse continuation> ::= <null>
638 |                            ::= - <verse id>
639 |                            
640 |       "s" is pointing to the first char of the <verse
641 |       continuation>.  If it is a null continuation we leave it
642 |       where it is, otherwise on exit we update it to point to
643 |       the first non-blank after the continuation.
644 |               
645 |       book     -- Number of the book in which the verse spec began.
646 |       chapter  -- Number of the chapter ...
647 |       absverse -- ABSOLUTE VERSE NUMBER where verse spec began
648 |               (this is more useful than the relative num).
649 |               
650 |       Returns the total number of verses to be fetched, unless
651 |       there was a problem.  Returns zero if there was a problem. 
652 |               
653 |
654 |   HISTORY:
655 |       890829 cc Initial implementation (formerly always returned 1). 
656 |       890908 cc Error return.
657 |
658 \*----------------------------------------------------------------------*/
659 {
660     int n;
661     char *s1;
662
663     s1 = *s;    /* For easier handling.  Be sure to update it */
664     n = 1;      /* Assume NO following verses */
665
666     if (*s1++ == '-') {
667         /* yup, there's a continuation */
668         get_nonblank(s1);
669         if (*s1 == '\0') {
670             /* special case - end of chapter */
671             n = verse_num( book, chapter, 1000);
672             *s = s1;
673         } else {
674             if (verse_id( &s1, &book, &chapter, &verse ) == 0)
675                 return 0;
676             *s = s1;
677
678             /* Determine absolute verse number of ending verse */
679             n = verse_num( book, chapter, verse );
680         }
681
682         /* how many verses is that? */
683         n = n - absverse +1;
684     }
685     
686     return n;
687 } /* verse_continuation */
688
689
690
691 ref_t verse_id(char **s,int *bookp,int *chapterp,int *versep )
692 /*----------------------------------------------------------------------
693 |   NAME:
694 |       verse_id
695 |
696 |   ALGORITHM:
697 |       <verse id>  ::= <book> <chapter> : <verse>
698 |                   ::= <book> <chapter>        # implies vs. 1
699 |                   ::= <book>                  # implies 1:1
700 |                   ::= <chapter> : <verse>     # implies current book
701 |                   ::= <verse>                 # implies current book/chapter
702 |       
703 |       "s" is pointing to the first character of the <verse id>.
704 |       On exit, we update it to be pointing to the next non-blank
705 |       after what we've parsed.
706 |       
707 |       The values of *bookp, and *chapterp are used as
708 |       defaults in case of a partial verse spec.  For example,
709 |       "15" would mean verse 15 in the default book/chapter.
710 |           
711 |       We return an absolute verse number (Gen 1:1 == 1) for the
712 |       referenced verse.  Also update *bookp, *chapterp*, and
713 |       *versep with the book, chapter, and verse numbers.
714 |       
715 |       If a problem occurs, we return 0.
716 |
717 |   HISTORY:
718 |       890902 cc Revised to handle incomplete verse specs.
719 |       890908 cc Fix to blank line handling.  Revised error return.
720 |
721 \*----------------------------------------------------------------------*/
722 {
723     short book, chapter, verse;
724     int   num;
725     ref_t vn;
726     char  *s1;
727
728     s1 = *s;            /* For easier handling.  Be sure to update it */
729     if ((book = get_book( &s1, *bookp )) == -1) return 0;
730     getnum(s1, num);
731     if (num < 1) {
732         /* <verse spec> ::= <book> */
733         if (book < 0) {
734             /* Special case: if this is a blank line, then print
735                the very next verse.  But if there's still junk on
736                the line, then what we have here is an error.
737                */
738             if (*s1) {
739                 /* Bad news, gang */
740                 tsl_error( FALSE, "Extra garbage on line: '%s'", s1 );
741                 return 0;
742             }
743
744             /* Get the last verse printed, increment it,
745                and translate that back into book/chapter/verse,
746                while making sure it stays in the proper range.
747                */
748             vn = verse_num( *bookp, *chapterp, *versep );
749             vn++;
750             vn = brl_extract_num( vn, bookp, chapterp, versep );
751             *s = s1;
752             return( vn );
753         } else {
754             chapter = 1;
755             verse   = 1;
756         }
757     } else {
758         get_nonblank(s1);
759         if (*s1 != ':') {
760             if (book < 0) {
761                 /* <verse spec> ::= <verse> */
762                 book    = *bookp;
763                 chapter = *chapterp;
764                 verse   = num;
765             } else {
766                 /* <verse spec> ::= <book> <chapter> */
767                 chapter = num;
768                 verse   = 1;
769             }
770         } else {
771             /* <verse spec> ::= <book> <chapter> : <verse> */
772             /* <verse spec> ::= <chapter> : <verse> */
773             s1++;       /* skip past the ':' */
774             if (book < 0)
775                 book = *bookp;          /* default book */
776             chapter = num;
777             getnum(s1, verse);
778             if (verse < 1) {
779                 tsl_error( FALSE, NO_VERSE, *s );
780             } else {
781                 get_nonblank(s1);
782             }
783         }
784     }
785
786     *s = s1;            /* Update the pointer we were passed */
787     *bookp    = book;
788     *chapterp = chapter;
789     *versep   = verse;
790     /* return absolute verse number */
791     return( verse_num( book, chapter, verse ) );
792 } /* verse_id */
793
794
795
796 ref_t brl_extract_num(ref_t absverse,int *bp,int *cp,int *vp)
797 /*----------------------------------------------------------------------
798 |   NAME:
799 |       brl_extract_num
800 |
801 |   ALGORITHM:
802 |       Extract the book, chapter, [relative] verse corresponding
803 |       to the absolute verse number passed in "absverse", if
804 |       "absverse" is valid.  If it is not valid, then coerce it
805 |       into something reasonable.  Update book, chapter, and
806 |       verse through their pointers "bp", "cp", and "vp",
807 |       respectively.  Return the [possibly changed] value of
808 |       absverse. 
809 |
810 |   HISTORY:
811 |       890904 cc Created (in a hurry)
812 |
813 \*----------------------------------------------------------------------*/
814 {
815     int bk, chp;
816
817     if (absverse < 1)
818         absverse = 1;   /* cheap insurance */
819     
820     for (bk= (int)GENESIS; bk <= (int)REV; bk++)
821         if (absverse <= start_verse[start_chapter[bk+1]+1])
822             /* we've got the right book */
823             for (chp=start_chapter[bk]+1; chp <= start_chapter[bk+1]; chp++)
824                 if (absverse <= start_verse[chp+1]) {
825                     /* we've got the right chapter */
826                     *bp = bk;
827                     *cp = chp - start_chapter[bk];
828                     *vp = absverse - start_verse[chp];
829                     return absverse;
830                 }
831     /* if we got here, then things are messed up.
832      Assume that the verse is off the back.
833      */
834     *bp = (int)REV;
835     *cp = start_chapter[ (int)REV+1 ] - start_chapter[(int)REV];
836     *vp = start_verse[(*cp)+1] - start_verse[*cp];
837     return verse_num( *bp, *cp, *vp );
838 } /* brl_extract_num */
839
840
841
842 ref_t verse_num(int b,int c,int v)
843 /*----------------------------------------------------------------------
844 |   NAME:
845 |       verse_num
846 |
847 |   ALGORITHM:
848 |       Return the absolute verse number, given the book, chapter
849 |       and [relative] verse number.
850 |       
851 |       Look up the starting absolute verse number of the
852 |       specified book/chapter, using the "start_verse" and
853 |       "start_chapter" tables, then add the [relative] verse
854 |       number. 
855 |       
856 |       Error checking: Ensure chapter is not too big for the
857 |       book, and that verse is not too big for the chapter.
858 |
859 |   HISTORY:
860 |       890830 cc Created.  The original lookup didn't do any
861 |               error checking.
862 |
863 \*----------------------------------------------------------------------*/
864 {
865     int abschapter;
866     ref_t absverse;
867
868     /* force book into proper range */
869     if (b < (int)GENESIS) b = (int)GENESIS;
870     else if (b > (int)REV) b = (int)REV;
871     
872     if ((abschapter = start_chapter[b] + c) > start_chapter[b+1])
873         abschapter = start_chapter[b+1];
874     if ((absverse = start_verse[abschapter] + v) > start_verse[abschapter+1])
875         absverse = start_verse[abschapter+1];
876     return absverse;
877 } /* verse_num */
878
879
880
881 ref_t brl_verse_spec(char **s,int *n)
882 /*----------------------------------------------------------------------
883 |   NAME:
884 |       brl_verse_spec
885 |
886 |   ALGORITHM:
887 |          
888 |           <verse spec> ::= <verse id> <verse continuation>
889 |       
890 |       Translate a <verse specifier> string s into a starting
891 |       absolute verse number, returned as the function result,
892 |       and a count n of verses to read.  Gen. 1:1 == verse 1.
893 |       
894 |       Returns 0 if something went wrong, leaving n undefined.
895 |
896 |   HISTORY:
897 |
898 \*----------------------------------------------------------------------*/
899 {
900     ref_t av;
901     char *s1;
902     int book, chapter, verse;
903
904     /* Munge off leading spaces,
905        convert string to lower case,
906        convert commas into blanks, and
907        dispose of possible yucky '\n'.
908      */
909     get_nonblank(*s);
910     s1 = *s;
911     while (*s1) {
912         if (isupper((int)*s1)) *s1 = tolower((int)*s1);
913         if (*s1 == ',')
914             *s1 = ' ';
915         else if (*s1 == '\n')
916             *s1 = '\0';
917         s1++;
918     }
919
920     brl_cur_vnum = brl_extract_num( brl_cur_vnum, &book, &chapter, &verse );
921     if ((av = verse_id( &(*s), &book, &chapter, &verse )) == 0)
922         return 0;
923     if (**s) {
924         if ((*n = verse_continuation( &(*s), book, chapter, verse, av )) == 0)
925             return 0;
926     } else {
927         *n = 1;
928     }
929     return av;
930 } /* brl_verse_spec */
931
932
933
934 #if 0 /* COMMENTED OUT -- NOT USED ANYMORE */
935 int brl_getverse( vs, vb, vbsize, pretty, lwidth )
936 /*----------------------------------------------------------------------
937 |   NAME:
938 |       brl_getverse
939 |
940 |   ALGORITHM:
941 |       Stuff buffer "vb" with text of verses specified by string
942 |       "vs", a <verse spec>.
943 |       
944 |       "pretty" and "lwidth" are formatting options passed to
945 |       tsl_gettext.  (NOT IMPLEMENTED!)
946 |
947 |   HISTORY:
948 |       890824 cc Created to hide internal functions
949 |               tsl_gettext and brl_verse_spec from user programs.
950 |       890912 cc Added pretty and lwidth.
951 |
952 \*----------------------------------------------------------------------*/
953
954 char *vs;
955 char *vb;
956 int  vbsize;
957 int  pretty, lwidth;
958 {
959     ref_t vn;
960     int vc, bytecount;
961
962     bytecount = 0;
963     while (*vs && (vn = brl_verse_spec( &vs, &vc )))
964            bytecount += tsl_gettext( vn, vc, vb, vbsize );
965
966     return bytecount;
967 } /* brl_getverse */
968 #endif
969
970
971 ref_t brl_printverse(char *vs,int pretty,int lwidth,FILE *outf)
972 /*----------------------------------------------------------------------
973 |   NAME:
974 |       brl_printverse
975 |
976 |   ALGORITHM:
977 |       Print text to stdout of verses specified by string "vs".
978 |       
979 |       pretty -- If true, then we want special output formatting.
980 |       lwidth -- If non-zero, then insert new-lines if necessary
981 |               between words to prevent lines from being longer
982 |               than lwidth.
983 |       outf   -- If non-NULL, then copy output to this file in
984 |               addition to stdout.
985 |
986 |       Returns absolute verse number of last verse printed.
987 |
988 |   HISTORY:
989 |       890902 cc Created as alternative to brl_getverse.
990 |       890912 cc Added pretty and lwidth.
991 |       921221 cc Print bookname with chapter.
992 |       921223 cc Print chapter heading anytime v.1 is printed.
993 |       930105 cc Added outf functionality & return verse number.
994 |       930422 cc Return correct versenum when errors occur.
995 |       991230 oe Increase VBSIZE to avoid verse truncation.
996 |
997 \*----------------------------------------------------------------------*/
998 {
999     ref_t vn, vref=0;
1000     int vc, vcount;
1001     int len, indent;
1002 /* Length of the longest line is 535, so original length of 512 was not enough */
1003 #define VBSIZE 550
1004     char vb1[VBSIZE], vb2[VBSIZE];
1005     char *srcp, *dstp, *endp;
1006     int  curbook, curchapter, curverse;
1007     int  oldbook, oldchapter, oldverse;
1008
1009     if (pretty || lwidth) {
1010         /* Get current context info to help with pretty printing */
1011         brl_cur_vnum = brl_extract_num( brl_cur_vnum,
1012                                        &oldbook, &oldchapter, &oldverse );
1013
1014         /* Process all verse specs that we're given */
1015         while (*vs && (vref = brl_verse_spec( &vs, &vcount ))) {
1016             brl_cur_vnum = vref +vcount -1; /* Update context.
1017                                                Won't be used unless next loop
1018                                                gets an error. */
1019             for (vc=vcount, vn=vref; vc; vc--) {
1020                 /* get text for a single verse */
1021                 len=tsl_gettext( vn++, 1, vb1, VBSIZE );
1022                 srcp = vb1;
1023                 dstp = vb2;
1024                 indent = 0;
1025                 if (pretty) {
1026                     brl_extract_num( vn-1, &curbook, &curchapter, &curverse );
1027                     /* print book/chapter heading?? */
1028                     if (curbook != oldbook ||
1029                         curchapter != oldchapter ||
1030                         curverse == 1) {
1031                         /* print chapter heading */
1032                         sprintf( dstp, "\n%s %d\n\n",
1033                                  booknamestr[curbook], curchapter );
1034                         /* Advance dstp (supports non-POSIX sprintf) */
1035                         while (*dstp) dstp++;
1036                     }
1037                     oldbook = curbook;
1038                     oldchapter = curchapter;
1039                     oldverse = curverse;
1040                     
1041                     /* advance to the verse number in source */
1042                     while (*srcp != ':') srcp++;
1043                     srcp++;
1044
1045                     /* insert indentation in dest */
1046                     indent = 2;         /* two for verse */
1047                     *dstp++ = ' '; *dstp++ = ' ';
1048                 }
1049
1050                 if (lwidth) {
1051                     /* Line width limitation. */
1052                     while ((int)strlen(srcp) > (lwidth-indent)) {
1053                         /* split this line.  But where? */
1054                         endp=srcp+lwidth-indent;
1055                         while (*endp!=' ' && endp>srcp) endp--;
1056                         if (endp <= srcp) {
1057                             /* oops.  Not enough room for even one word.
1058                              * Print it anyway.
1059                              */
1060                             endp=srcp+1;
1061                             while (*endp!=' ' && *endp!='\n' && *endp) endp++;
1062                         }
1063
1064                         /* style is to indent only the first line of the
1065                          * verse, before the verse number.  Subsequent lines
1066                          * are *not* indented.
1067                          */
1068                         indent=0;
1069                         
1070                         len = endp-srcp;
1071                         strncpy(dstp, srcp, len);
1072                         dstp += len;
1073                         *dstp++ = '\n';
1074                         srcp = ++endp;          /* past the ' ' */
1075                     }
1076                     /* last line, get the rest */
1077                     strcpy(dstp, srcp);
1078                 } else {
1079                     /* No line length limit.
1080                        Just copy the rest of the verse line.
1081                        */
1082                     strcpy(dstp, srcp);
1083                 }
1084                 /* now print out the verse */
1085                 printf( "%s", vb2 );
1086                 if (outf != NULL)
1087                     fprintf( outf, "%s", vb2 );
1088             } /* for */
1089         } /* while */
1090     } else {
1091         /* Raw output.  Not pretty or line-wrapped */
1092         while (*vs && (vref = brl_verse_spec( &vs, &vcount )))
1093             tsl_printtext( vref, vcount );
1094     }
1095     if (vref) return vref + vcount-1;
1096     else      return brl_cur_vnum;
1097 } /* brl_printverse */
1098
1099
1100
1101 char* brl_num_to_ref(char *vbuf,ref_t *absversep)
1102 /*----------------------------------------------------------------------
1103 |   NAME:
1104 |       brl_num_to_ref
1105 |
1106 |   ALGORITHM:
1107 |       Accepts an absolute verse number and returns
1108 |       a string containing a legal verse reference.
1109 |       
1110 |       This is useful with some concordance functions.
1111 |
1112 |         vbuf -- Points to buffer to use for string.
1113 |
1114 |       Returns a copy of vbuf pointer.
1115 |
1116 |   HISTORY:
1117 |       921217 cc Initial creation.
1118 |
1119 \*----------------------------------------------------------------------*/
1120 {
1121     int bk, chp, v;
1122     
1123     *absversep = brl_extract_num( *absversep, &bk, &chp, &v);
1124     sprintf(vbuf, "%s%d:%d", bookabbrvstr[bk], chp, v);
1125     return( vbuf );
1126 } /* brl_num_to_ref */
1127
1128
1129
1130 void brl_init(char *dfname,char *dfpath,int memlimit)
1131 /*----------------------------------------------------------------------
1132 |   NAME:
1133 |       brl_init
1134 |
1135 |   ALGORITHM:
1136 |       Initialize the library.
1137 |       
1138 |       memlimit        Limit (in Kbytes) on buffer space used by TSL.
1139 |       dfname          Name of the data file.
1140 |       dfpath          List of paths to use in searching for the
1141 |                       data file.
1142 |
1143 |   HISTORY:
1144 |       890830 cc Added memlimit.
1145 |       890905 cc Added filename and paths.
1146 |
1147 \*----------------------------------------------------------------------*/
1148 {
1149     if (dfname == NULL)
1150         dfname = "bible.data";
1151     if (dfpath == NULL)
1152         dfpath = "./ " DESTLIB "/"; /* /usr/lib/; */
1153     tsl_init( dfname, dfpath, memlimit );
1154
1155     /* Set (low) illegal value for current context.
1156        Making it low means that it will be coerced to Gen1:1 if
1157        the user gives a null input.
1158      */
1159     brl_cur_vnum = 0;
1160 } /* brl_init */
1161
1162
1163 void brl_close(void)
1164 /*----------------------------------------------------------------------
1165 |   NAME:
1166 |       brl_close
1167 |
1168 |   ALGORITHM:
1169 |       Close the library.
1170 |
1171 |   HISTORY:
1172 |
1173 \*----------------------------------------------------------------------*/
1174 {
1175     tsl_close();
1176 } /* brl_close */
1177
1178