chiark / gitweb /
add LDADD (Ubuntu patch from Andreas Moog) (fix for #631606)
[bible-kjv.git] / bible.c
1 /* -*-C-*-
2 *******************************************************************************
3 *
4 * File:         bible.c
5 * RCS:          $Header: /home/matthew/cvs/bible-kjv-4.10/bible.c,v 2.7 2008/03/11 20:48:41 matthew Exp $
6 * Description:  Write Bible text to stdout.
7 * Author:       Chip Chapin, Hewlett Packard Company
8 * Created:      Jan 21 1989
9 * Modified:     Mon Apr 26 11:11:45 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 * Mon Apr 26 11:09:58 1993 (Chip Chapin) chip@hpclbis
19 *  Print welcome message when invoked interactively.
20 * Fri Apr 23 12:57:34 1993 (Chip Chapin) chip@hpclbis
21 *  Changed fflush( NULL ) to fflush( stdout ) to make SunOS happy.
22 * Thu Apr 22 11:20:56 1993 (Chip Chapin) chip@hpclbis
23 *  Support non-POSIX sprintf.
24 * Tue Jan  5 17:10:01 1993 (Chip Chapin) chip@hpclbis
25 *  Removed printverse(), might as well call brl_printverse().
26 *  get_verse() is now commented out.  Nobody uses it anymore.
27 *  Allow leading white space on input lines.
28 * Tue Jan  5 10:46:17 1993 (Chip Chapin) chip@hpclbis
29 *  Added line formatting to cmd_list output.
30 *  Added ?w command.
31 * Mon Jan  4 15:41:26 1993 (Chip Chapin) chip@hpclbis
32 *  Added ?or and ?in commands.  Some big changes here.
33 * Thu Dec 24 11:04:04 1992 (Chip Chapin) chip@hpclbis
34 *  Added prompter function.
35 * Wed Dec 23 13:19:22 1992 (Chip Chapin) chip@hpclbis
36 *  Add release version to help output.
37 *  Rewrite help text.
38 *  Fix bug in command processing "?".
39 *  Minor tweaks to eliminate compile warnings.
40 * Mon Dec 21 19:12:30 1992 (Chip Chapin) chip@hpclbis
41 *  Added interactive command processing and concordance.
42 *******************************************************************************
43 *
44 * $Log: bible.c,v $
45 * Revision 2.7  2008/03/11 20:48:41  matthew
46 * #include <string.h> (enables compilation on Mac OS) - closes Debian
47 *  bug 439735
48 *
49 * Revision 2.6  2005/01/23 11:14:25  matthew
50 * explicit casts
51 *
52 * Revision 2.5  2005/01/22 18:52:48  matthew
53 * No longer write over strings
54 *
55 * Revision 2.4  2005/01/21 18:55:33  matthew
56 * make pretty_print toggling a bit clearer.
57 * Remove unused variables.
58 * add undeclared functions to brl.h
59 * include readline/history.h
60 * make main return
61 *
62 * Revision 2.3  2005/01/21 18:32:25  matthew
63 * Remove #defines to bible.h and include that
64 *
65 * Revision 2.2  2005/01/21 18:24:03  matthew
66 * prototype all functions
67 *
68 * Revision 2.1  2003/01/08 15:50:53  matthew
69 * applied debian patch
70 *
71 * Revision 2.0  2003/01/08 15:29:51  matthew
72 * versions collected from the net
73 *
74  * Revision 1.19  93/04/26  11:17:27  11:17:27  chip (Chip Chapin)
75  * Release 4.00
76  * Public release of portable datafile version.
77  * 
78  * Revision 1.18  93/04/23  13:07:58  13:07:58  chip (Chip Chapin)
79  * PORTABILITY RELEASE
80  * This version supports portable data files, usable on machines with
81  * differing native byte-orders.
82  * Also, this version compiles and runs on non-HPUX systems.  It has been
83  * tested on SunOS 4.? and ULTRIX 4.?, using SPARC and DEC 3100 hardware
84  * respectively.  Note that the data file format has rolled again.
85  * 
86  * Revision 1.17  93/01/05  19:03:13  19:03:13  chip (Chip Chapin)
87  * Release 3.00: (not for distribution)
88  * Fixed errors (blank lines) in bible.data file.  Data file is not compatible
89  * with previous (1.x and 2.x) distributions.  Further changes pending.
90  * Rewrote context handling, and added "<" and ">" commands.
91  * Tools for building brl-index are now part of release.
92  * 
93  * Revision 1.16  93/01/05  11:31:34  11:31:34  chip (Chip Chapin)
94  * Release 2.2c: Minor tweaks.
95  * 
96  * Revision 1.15  93/01/05  11:19:02  11:19:02  chip (Chip Chapin)
97  * Release 2.2b: Another cmd fix.
98  * 
99  * Revision 1.14  93/01/05  11:02:01  11:02:01  chip (Chip Chapin)
100  * Release 2.2a: Fix command bug.
101  * 
102  * Revision 1.13  93/01/05  10:49:07  10:49:07  chip (Chip Chapin)
103  * Release 2.2, Added ?w command and line formatting to ?l output.
104  * 
105  * Revision 1.12  93/01/04  16:20:55  16:20:55  chip (Chip Chapin)
106  * Release 2.1, implements ?in and ?or commands.
107  * 
108  * Revision 1.11  92/12/24  11:09:11  11:09:11  chip (Chip Chapin)
109  * Release 2.04.  Include verse ref in prompt line.
110  * 
111  * Revision 1.10  92/12/23  14:10:14  14:10:14  chip (Chip Chapin)
112  * Release 2.03: minor tweaks and bug fixes.
113  * 
114  * Revision 1.9  92/12/22  18:17:04  18:17:04  chip (Chip Chapin)
115  * Minor tweaks for release 2.02.
116  * 
117  * Revision 1.8  92/12/22  11:28:30  11:28:30  chip (Chip Chapin)
118  * Minor release 2.01 -- fix a couple of bugs.
119  * 
120  * Revision 1.7  92/12/21  20:00:45  20:00:45  chip (Chip Chapin)
121  * Release 2.0.  This release adds the concordance, and some small fixes.
122  * 
123  * Revision 1.6  89/10/02  22:19:55  22:19:55  chip (Chip Chapin)
124  * Fix bug in processing "-l 2peter" sorts of things.
125  * 
126  * Revision 1.5  89/10/02  14:24:42  14:24:42  chip (Chip Chapin)
127  * Revised usage text.
128  * 
129  * Revision 1.4  89/09/14  20:33:35  20:33:35  chip (Chip Chapin)
130  * Release 1-2.  Supports -f and -l options for formatting the output.
131  * Updates primarily brl.c, bible.c, and bible.1.
132  * 
133  * Revision 1.3  89/09/13  21:48:26  21:48:26  chip (Chip Chapin)
134  * Implement -f and -l options for pretty-printing and linewidth limitation.
135  * 
136  * Revision 1.2  89/09/08  09:00:26  09:00:26  chip (Chip Chapin)
137  * Bug fix and simplification: send whole input lines or arguments to BRL,
138  * and let BRL worry about multiple references.  We don't care.
139  * 
140  * Revision 1.1  89/09/05  17:47:27  17:47:27  chip (Chip Chapin)
141  * Initial revision
142  * 
143 */
144
145
146 /*----------------------------------------------------------------------
147 |   NAME:
148 |       bible.c
149 |
150 |   PURPOSE:
151 |       Reads verse specs from the command line or from stdin and
152 |       writes Bible verses to stdout.  Uses the Bible Retrieval
153 |       Library for all text access functions.
154 |
155 |   FUNCTIONS:
156 |       main
157 |
158 |   HISTORY:
159 |       890121 cc Initial Creation
160 |       890824 cc Updated to use new brl_verse_text.
161 |       890829 cc Updated to think about buffer size.
162 |       921221 cc See Revision list above for further history...
163 |
164 \*----------------------------------------------------------------------*/
165
166 #include <ctype.h>
167 #include <stdio.h>
168 #include <stdlib.h>
169 #include <string.h>
170 #include <readline/readline.h>
171 #include <readline/history.h>
172 #include "tsl.h"
173 #include "brl.h"
174 #include "version.h"
175 #include "bible.h"
176
177 char help_text[]="\n\
178  -Bible verse specifications:\n\
179      Verses may be specified using various standard abbreviations and \n\
180      notations, including both single verses and verse ranges.\n\
181      E.g.  Jn3:16, jn3:16,17 ps1:1-6\n\
182      Partial specs are interpreted in the context of the previous verse.\n\
183      E.g.  \"Rev3:20\" followed by \"15\" prints Rev3:15.\n\
184  -Concordance (word search) commands:\n\
185       ??word     Find all verses containing \"word\".\n\
186                  Creates a \"ref list\" for subsequent use.\n\
187       ?list      List the references in ref list.       (abbrev ?l)\n\
188       ?view      View text of verses in ref list.       (abbrev ?v)\n\
189       ?and word  Combine ref list w/MATCHING refs for \"word\".    (abbrev ?a)\n\
190       ?or word   Combine ref list w/ALL refs for \"word\".    (abbrev ?o)\n\
191       ?in <verse range>       Limit ref list to range of verses.\n\
192       ?in all    Reset ref list limit.\n\
193      To get a list of refs, each containing multiple words, start with:\n\
194       ??word     for the first word, followed by\n\
195       ?and word  for each following word.The order of the words doesn't matter.\n\
196   -A few miscellaneous program control commands.\n\
197      ?, ?h, ?help              -- Prints this help text.\n\
198      <, >                      -- Change direction through text.\n\
199      ?w file                   -- Begin writing (appending) output to \"file\".\n\
200      ?w                        -- Stop writing output to a file.\n\
201      ?f                        -- Toggles output formatting modes.\n\
202      q, ?bye, ?exit, ?quit, ?q -- End this program.\n\
203   Note that a blank line will advance one verse in current direction.\n\
204 ";
205
206
207
208 int do_concordance(char *word,ref_t *buf )
209 /*----------------------------------------------------------------------
210 |   NAME:
211 |       do_concordance
212 |
213 |   ALGORITHM:
214 |       Utility/wrapper function for tsl_scan_concordance.
215 |       Prints results of search.  Called from all concordance
216 |       command functions.
217 |
218 |   HISTORY:
219 |       930104 cc Initial creation.
220 |
221 \*----------------------------------------------------------------------*/
222 {
223     int cnt;
224     char *ref_word;
225     char range_str[VSPECSZ];
226     char vref1[REFSZ], vref2[REFSZ];
227     
228     printf( "  Searching for '%s'...", word );
229     if (outf != NULL)
230         fprintf( outf, "  Searching for '%s'...", word );
231     fflush( stdout );
232     
233     if (inrange_start) {
234         sprintf( range_str, " in %s-%s",
235                 brl_num_to_ref(vref1, &inrange_start),
236                 brl_num_to_ref(vref2, &inrange_end) );
237     } else {
238         range_str[0] = '\0';
239     }
240     
241     if ((cnt=tsl_scan_concordance( word, buf, inrange_start, inrange_end )) == 0) {
242         printf( " not found%s.\n", range_str );
243         if (outf != NULL)
244             fprintf( outf, " not found%s.\n", range_str );
245     } else {
246         if (cnt == 1) ref_word = "ref";
247         else ref_word = "refs";
248         printf( " [%d %s%s]\n", cnt, ref_word, range_str );
249         if (outf != NULL)
250             fprintf( outf, " [%d %s%s]\n", cnt, ref_word, range_str );
251     }
252     return (cnt);
253 } /* do_concordance */
254
255
256
257 void cmd_ANDconcordance(char *word)
258 /*----------------------------------------------------------------------
259 |   NAME:
260 |       cmd_ANDconcordance
261 |
262 |   ALGORITHM:
263 |       Look up a word in the concordance, if found, combine ref list
264 |       with current ref list using logical AND.
265 |
266 |   HISTORY:
267 |       921218 cc Initial creation.
268 |
269 \*----------------------------------------------------------------------*/
270 {
271     int cnt;
272     int i, j, k;
273     ref_t sbuf[SELECTSZ];       /* List of selected verses */
274     ref_t tbuf[SELECTSZ];       /* temp buff of selected verses */
275
276     if (select_count < 1) {
277         fprintf( stderr, "No references.  Use '??word'.\n" );
278         return;
279     }
280     if (*word == '\0') {
281         fprintf( stderr, "To AND-search for a word use '?and word'\n" );
282         return;
283     }
284
285     if ((cnt = do_concordance( word, sbuf )) == 0) return;
286
287     /* AND with existing list */
288     i = j = k = 0;
289     while ((i < select_count) && (j < cnt)) {
290         if (selectbuf[i] == sbuf[j]) {
291             tbuf[k++] = sbuf[j++];
292             i++;
293         }
294         else if (selectbuf[i] > sbuf[j]) j++;
295         else if (selectbuf[i] < sbuf[j]) i++;
296     }
297     
298     /* Update global list */
299     select_count = k;
300     for (i=0; i<cnt; i++) selectbuf[i] = tbuf[i];
301     printf( "  [%d refs in combined list]\n", select_count );
302     if (outf != NULL)
303         fprintf( outf, "  [%d refs in combined list]\n", select_count );
304
305     if (list_always)
306         /* Go ahead and display the refs */
307         cmd_list();
308 } /* cmd_ANDconcordance */
309
310
311
312 void cmd_ORconcordance(char *word)
313 /*----------------------------------------------------------------------
314 |   NAME:
315 |       cmd_ORconcordance
316 |
317 |   ALGORITHM:
318 |       Look up a word in the concordance, if found, combine ref list
319 |       with current ref list using logical OR.
320 |
321 |   HISTORY:
322 |       930104 cc Initial creation.
323 |
324 \*----------------------------------------------------------------------*/
325 {
326     int cnt;
327     int i, j, k;
328     ref_t sbuf[SELECTSZ];       /* List of selected verses */
329     ref_t tbuf[SELECTSZ];       /* temp buff of selected verses */
330
331     if (*word == '\0') {
332         fprintf( stderr, "To OR-search for a word use '?OR word'\n" );
333         return;
334     }
335
336     if ((cnt = do_concordance( word, sbuf )) == 0) return;
337     
338     /* OR with existing list (i.e. merge the lists) */
339     i = j = k = 0;
340     while ((i < select_count) && (j < cnt)) {
341         if (selectbuf[i] < sbuf[j]) {
342             tbuf[k++] = selectbuf[i++];
343         } else if (selectbuf[i] == sbuf[j]) {
344             tbuf[k++] = selectbuf[i++];
345             j++;
346         } else if (selectbuf[i] > sbuf[j]) {
347             tbuf[k++] = sbuf[j++];
348         }
349     }
350     /* One of the lists probably has remaining elements.
351        Only ONE of the following loops will execute.
352      */
353     while (i < select_count) {
354         tbuf[k++] = selectbuf[i++];
355     }
356     while (j < cnt) {
357         tbuf[k++] = sbuf[j++];
358     }
359     
360     /* Update global list */
361     select_count = k;
362     for (i=0; i<select_count; i++) selectbuf[i] = tbuf[i];
363     printf( "  [%d refs in combined list]\n", select_count );
364     if (outf != NULL)
365         fprintf( outf, "  [%d refs in combined list]\n", select_count );
366
367     if (list_always)
368         /* Go ahead and display the refs */
369         cmd_list();
370 } /* cmd_ORconcordance */
371
372
373
374 void cmd_concordance(char *word)
375 /*----------------------------------------------------------------------
376 |   NAME:
377 |       cmd_concordance
378 |
379 |   ALGORITHM:
380 |       Look up a word in the concordance.
381 |
382 |   HISTORY:
383 |       921217 cc Initial creation.
384 |
385 \*----------------------------------------------------------------------*/
386 {
387     if (*word == '\0') {
388         fprintf( stderr, "To search for a word type '??word'\n" );
389         return;
390     }
391
392     select_count = do_concordance( word, selectbuf );
393     
394     if (list_always)
395         /* Go ahead and display the refs */
396         cmd_list();
397 } /* cmd_concordance */
398
399
400
401 void cmd_help(void)
402 /*----------------------------------------------------------------------
403 |   NAME:
404 |       cmd_help
405 |
406 |   ALGORITHM:
407 |       Print Help message for this program.
408 |
409 |   HISTORY:
410 |       921217 cc Initial creation.
411 |       921223 cc Added release_version.
412 |
413 \*----------------------------------------------------------------------*/
414
415 {
416     printf( "%s: %s\n", myname, release_version );
417     printf( "%s", help_text );
418     if (outf != NULL) {
419         fprintf( outf, "%s: %s\n", myname, release_version );
420         fprintf( outf, "%s", help_text );
421     }
422     fflush( stdout );
423 } /* cmd_help */
424
425
426
427 void cmd_inrange(char *range)
428 /*----------------------------------------------------------------------
429 |   NAME:
430 |       cmd_inrange
431 |
432 |   ALGORITHM:
433 |       Limit concordance ref list to a certain verse range.  The
434 |       limit range will be applied immediately to modify the
435 |       current ref list (if any), and will apply to all
436 |       subsequent searches.
437 |       
438 |       If the specified verse range is "all", then the limits are
439 |       removed.
440 |
441 |   HISTORY:
442 |       930104 cc Initial creation.
443 |
444 \*----------------------------------------------------------------------*/
445 {
446     ref_t start;                /* starting verse */
447     int count;                  /* number of verses */
448     ref_t tbuf[SELECTSZ];       /* temp buffer */
449     int i;
450     char vref1[REFSZ], vref2[REFSZ];
451
452     if (strcmp(range, "all") == 0) {
453         /* Reset limits */
454         inrange_start = inrange_end = 0;
455         return;
456     }
457     /*Assignment used as truth value*/
458     if ((start = brl_verse_spec( &range, &count ))) {
459         inrange_start = start;
460         inrange_end = start + count -1;
461
462         if (select_count) {
463             /* Edit current ref list */
464             for (count=i=0; i < select_count; i++) {
465                 if (selectbuf[i] > inrange_end) break;
466                 if (inrange_start <= selectbuf[i]) tbuf[count++]=selectbuf[i];
467             }
468             /* Update global list */
469             select_count = count;
470             for (i=0; i < count; i++) selectbuf[i] = tbuf[i];
471             printf( "  [%d refs in range %s-%s]\n", select_count,
472                    brl_num_to_ref(vref1, &inrange_start),
473                    brl_num_to_ref(vref2, &inrange_end) );
474             if (outf != NULL) {
475                 fprintf( outf, "  [%d refs in range %s-%s]\n", select_count,
476                         brl_num_to_ref(vref1, &inrange_start),
477                         brl_num_to_ref(vref2, &inrange_end) );
478             }
479         } else {
480             printf( "  [range is %s-%s]\n", 
481                    brl_num_to_ref(vref1, &inrange_start),
482                    brl_num_to_ref(vref2, &inrange_end) );
483             if (outf != NULL) {
484                 fprintf( outf, "  [range is %s-%s]\n", 
485                         brl_num_to_ref(vref1, &inrange_start),
486                         brl_num_to_ref(vref2, &inrange_end) );
487             }
488         }
489     }
490 } /* cmd_inrange */
491
492
493
494 void cmd_list(void)
495 /*----------------------------------------------------------------------
496 |   NAME:
497 |       cmd_list
498 |
499 |   ALGORITHM:
500 |       List references found in previous search.
501 |       List is formatted using line_width.
502 |
503 |   HISTORY:
504 |       921217 cc Initial creation.
505 |       930105 cc Format output into lines.
506 |
507 \*----------------------------------------------------------------------*/
508
509 {
510     char vref[REFSZ];           /* verse ref buffer */
511     int  cnt;
512     char lbuf[LINESZ];          /* line buffer */
513     int  lcnt;                  /* count of chars in line */
514     int  i;
515
516     if (select_count < 1) {
517         printf( "No references.  Use '??word' first.\n" );
518         return;
519     }
520     sprintf( lbuf, "  References [%d]: ", select_count );
521     /* Support non-POSIX sprintf */
522     lcnt = strlen(lbuf);
523
524     for (cnt=0; cnt < select_count; cnt++) {
525         brl_num_to_ref(vref, &selectbuf[cnt]);
526         i = strlen(vref);
527         if ((lcnt + i +1)> line_width) {
528             puts( lbuf );
529             if (outf != NULL) {
530                 fputs( lbuf, outf );
531                 putc( '\n', outf );
532             }
533             lbuf[0] = '\0'; lcnt = 0;
534         } else {
535             strcat( lbuf, " " );
536             lcnt++;
537         }
538         strcat( lbuf, vref );
539         lcnt += i;
540     }
541     puts( lbuf );
542     if (outf != NULL) {
543         fputs( lbuf, outf );
544         putc( '\n', outf );
545     }
546 } /* cmd_list */
547
548
549
550 void cmd_pretty_print(int verbose)
551 /*----------------------------------------------------------------------
552 |   NAME:
553 |       cmd_pretty_print
554 |
555 |   ALGORITHM:
556 |       Toggle pretty-printing modes
557 |
558 |   HISTORY:
559 |       921217 cc Initial creation.
560 |
561 \*----------------------------------------------------------------------*/
562
563 {
564     char *s;
565     /*invert the pretty_printing setting*/
566     pretty_printing=pretty_printing ? FALSE : TRUE;
567     
568     if (pretty_printing) {
569         if (line_width == 0) {
570             /*
571               This forces pretty_printing to be done with a
572               restricted line width.  If somebody doesn't like that,
573               they can use "-l999" or something.
574               */
575             if ((s=getenv("COLUMNS")) == NULL)
576                 line_width = 79;        /* Take a guess.  Oh well */
577             else {
578                 line_width = atoi(s) -1;
579                 if (line_width > LINESZ) line_width = LINESZ -1;
580             }
581         }
582     } else {
583         /* If pretty_printing is being turned off, they probably don't
584            want line breaks either */
585         line_width = 0;
586     }
587     if (verbose) {
588         printf( "Output formatting %s\n", pretty_printing?"ON":"OFF" );
589         if (outf != NULL)
590             fprintf( outf, "Output formatting %s\n", pretty_printing?"ON":"OFF" );
591     }
592 } /* cmd_pretty_print */
593
594
595
596 void cmd_view(void)
597 /*----------------------------------------------------------------------
598 |   NAME:
599 |       cmd_view
600 |
601 |   ALGORITHM:
602 |       View full text of refs found in previous search.
603 |
604 |   HISTORY:
605 |       921217 cc Initial creation.
606 |
607 \*----------------------------------------------------------------------*/
608
609 {
610     char vref[REFSZ];
611     int cnt;
612     
613     if (select_count < 1) {
614         fprintf( stderr, "No references.  Use '??word' first.\n" );
615         return;
616     }
617     printf( "Viewing References [%d]: \n", select_count );
618     if (outf != NULL)
619         fprintf( outf, "Viewing References [%d]: \n", select_count );
620
621     for (cnt=0; cnt < select_count; cnt++) {
622         /* Print refs without updating current context */
623         brl_num_to_ref( vref, &selectbuf[cnt] );
624         brl_printverse( vref, pretty_printing, line_width, outf );
625     }
626 } /* cmd_view */
627
628
629
630 void cmd_write(char *fname)
631 /*----------------------------------------------------------------------
632 |   NAME:
633 |       cmd_write
634 |
635 |   ALGORITHM:
636 |       Begin writing a copy of program output to a file.  If we
637 |       are already writing to a file, then close it, and open a
638 |       new one.  The file is opened in "append" mode.
639 |       
640 |         fname -- String with name of file.
641 |         
642 |       If fname is null then close current file (if any) and
643 |       stop writing. 
644 |
645 |   HISTORY:
646 |       930105 cc Initial creation.
647 |
648 \*----------------------------------------------------------------------*/
649 {
650     if (outf != NULL) {
651         fclose( outf );
652         outf = NULL;
653         printf( "%s: Output file closed.\n", myname );
654     }
655     if (*fname) {
656         if ((outf = fopen( fname, "a" )) == NULL) {
657             fprintf( stderr, "%s: Cannot append to file '%s'\n",
658                     myname, fname );
659         }
660         printf( "%s: Writing to %s.\n", myname, fname );
661     }
662 } /* cmd_write */
663
664
665
666 void do_command(char *cmd)
667 /*----------------------------------------------------------------------
668 |   NAME:
669 |       do_command
670 |
671 |   ALGORITHM:
672 |       Handle a control command.
673 |       For now, all control commands begin with "?".
674 |
675 |   HISTORY:
676 |       921217 cc Initial creation
677 |
678 \*----------------------------------------------------------------------*/
679 {
680     int n;
681     char w[VSPECSZ];
682     char *p;
683
684     /* Convert to lower case */
685     for (p=cmd; *p; p++) {
686         if (isupper((int)*p)) *p =tolower((int)*p);
687     }
688     n=sscanf(cmd, "?%s", w);
689
690     if ((n <= 0) ||
691         /* HELP */
692         (strcmp(w, "") == 0) ||
693         (strcmp(w, "h") == 0) ||
694         (strcmp(w, "help") == 0))
695     {
696         cmd_help();
697     } else if (*w == '?') {
698         /* Concordance (word search) */
699         if (w[1] == '\0') {
700             /* Support "?? word" (with space) */
701             w[0] = '\0';        /* ensure correctness for plain "??" */
702             p = cmd + 2;
703             n=sscanf(p, "%s", w);
704             cmd_concordance( w );
705         } else {
706             cmd_concordance( w+1 );
707         }
708     } else if ((strcmp(w, "a") == 0) ||
709                (strcmp(w, "and") == 0)) {
710         /* "and" word search */
711         p = cmd + strlen(w) +1;
712         n=sscanf(p, "%s", w);
713         cmd_ANDconcordance( w );
714     } else if ((strcmp(w, "f") == 0)) {
715         /* Toggle pretty-print modes */
716         cmd_pretty_print(TRUE);
717     } else if (strcmp(w, "in") == 0) {
718         /* IN -- limit ref list to a verse range */
719         p = cmd + strlen(w) +1;
720         n=sscanf(p, "%s", w);
721         cmd_inrange( w );
722     } else if ((strcmp(w, "l") == 0) ||
723                (strcmp(w, "list") == 0)) {
724         /* LIST refs from previous search */
725         cmd_list();
726     } else if ((strcmp(w, "o") == 0) ||
727                (strcmp(w, "or") == 0)) {
728         /* "or" word search */
729         p = cmd + strlen(w) +1;
730         n=sscanf(p, "%s", w);
731         cmd_ORconcordance( w );
732     } else if ((strcmp(w, "v") == 0) ||
733                (strcmp(w, "view") == 0)) {
734         /* VIEW text of refs from previous search */
735         cmd_view();
736     } else if (*w == 'w') {
737         /* WRITE to a file */
738         if (w[1] == '\0') {
739             /* Support "?w file" (with space) */
740             w[0] = '\0';        /* ensure correctness for plain "?w" */
741             p = cmd + 2;
742             n=sscanf(p, "%s", w);
743             cmd_write( w );
744         } else {
745             /* Support "?wfile" (no space) */
746             cmd_write( w+1 );
747         }
748     } else if ((strcmp(w, "bye") == 0) ||
749                (strcmp(w, "exit") == 0) ||
750                (strcmp(w, "q") == 0) ||
751                (strcmp(w, "quit") == 0)) {
752         /* Let's go home */
753         brl_close();
754         exit( 0 );
755     } else {
756         /* Hmmm... */
757         fprintf( stderr, "%s: Unrecognized command '%s'\n", myname, w);
758         cmd_help();
759     }
760 } /* do_command */
761
762
763
764 void user_input(char  *cmd)
765 /*----------------------------------------------------------------------
766 |   NAME:
767 |       user_input
768 |
769 |   ALGORITHM:
770 |       Process a user command.
771 |       Commands could be:
772 |          1) A Bible verse spec, e.g. "Jn3:16-18", or even "3"
773 |             (context sensitive).
774 |          2) A control command, e.g. "?"
775 |
776 |   HISTORY:
777 |       921217 cc Initial creation.
778 |
779 \*----------------------------------------------------------------------*/
780 {
781     char vs[REFSZ];
782     
783     get_nonblank(cmd);
784         
785     /* Is it a control command?
786        For now, all control commands start with "?". */
787     if (*cmd == '?') {
788         /* Control Command */
789         do_command( cmd );
790     } else if (*cmd == '>' || *cmd == '<' || *cmd == '\0') {
791         if (*cmd == '>') skip_inc = 1;
792         if (*cmd == '<') skip_inc = -1;
793
794         /* Print next verse and update context. */
795         brl_cur_vnum += skip_inc;
796         brl_printverse( brl_num_to_ref(vs, &brl_cur_vnum),
797                        pretty_printing, line_width, outf );
798     } else {
799         /* Verse Spec.
800            Print it and update context.
801          */
802         brl_cur_vnum = brl_printverse( cmd, pretty_printing, line_width, outf );
803     }
804 } /* user_input */
805
806
807
808 char *getprompt(void)
809 /*----------------------------------------------------------------------
810 |   NAME:
811 |       getprompt
812 |
813 |   ALGORITHM:
814 |       Get prompt for interactive user.
815 |
816 |   HISTORY:
817 |       921224 cc Initial creation.
818 |       020707 dm Change prompter to getprompt for readline.
819 |
820 \*----------------------------------------------------------------------*/
821
822 {
823     char vbuf[REFSZ];
824     static char promptbuf[LINESZ];
825     
826     sprintf(promptbuf, "%s(%s) [%s]%s ", myname,
827            brl_textname, brl_num_to_ref(vbuf, &brl_cur_vnum),
828            (skip_inc > 0 ? ">" : "<") );        
829     return promptbuf;
830 } /* getprompt */
831
832
833
834 void usage(void)
835 /*----------------------------------------------------------------------
836 |   NAME:
837 |       usage
838 |
839 |   ALGORITHM:
840 |       Print usage message to stderr, then exit program.
841 |
842 |   HISTORY:
843 |       890830 cc Created.
844 |       890912 cc Added -f, -l.
845 |       891002 cc Tinkered with help text.
846 |
847 \*----------------------------------------------------------------------*/
848
849 {
850     fprintf( stderr, "Usage: %s [-f][-l[cols]][-m mem][-p path][-d file][<verse spec>...]\n",
851             myname );
852     fprintf( stderr, "\n\
853     -d file  Override default datafile name\n\
854     -f       Format the output (implies -l)\n\
855     -l[cols] Set line width (default value: COLUMNS env. variable)\n\
856     -m mem   Specify maximum buffer memory usage\n\
857              in Kbytes.  Defaults to 1024K.\n\
858     -p path  Override default datafile search path.\n\
859 \n" );
860     exit(1);
861 } /* usage */
862
863
864
865 int main(int argc,char **argv)
866 /*----------------------------------------------------------------------
867 |   NAME:
868 |       main
869 |
870 |   ALGORITHM:
871 |       Main Program.
872 |       
873 |       Handle command line options.
874 |       Initialize the Bible Retrieval Library.
875 |       Read user commands from the command line or from stdin.
876 |
877 |   HISTORY:
878 |       890830 cc Added options processing.
879 |       890908 cc Send whole lines to printverse instead of
880 |               partially parsing them.
881 |       890912 cc Added -f, -l options.
882 |
883 \*----------------------------------------------------------------------*/
884 {
885   char *line;
886   char *dfname, *dfpath, *s;
887   char ch;
888   char def_verse[]="Gen1:1";
889
890   mem_limit = 1024;             /* Default 1024K of buffer space */
891   dfname = dfpath = NULL;       /* Use library's default values */
892     
893   myname = s = *argv++; argc--; /* Program name */
894   while (*s) {
895       if (*s == '/') myname = s+1;
896       s++;
897   }
898
899   cmd_pretty_print(FALSE);      /* Kind of hokey -- this calls turns it on */
900   
901 #define ARGVAL()  (*++(*argv) || (--argc && *++argv))
902   for (;argc && **argv == '-'; argc--, argv++) {
903       /* Got an option flag */
904       while (*++(*argv)) {
905           /* Process all flags in this argument */
906           switch (**argv) {
907             case 'd':
908               if (!ARGVAL()) {
909                   fprintf( stderr, "%s: -d Missing datafile-name\n", myname );
910                   usage();
911               }
912               dfname = *argv;
913               goto nextarg;
914             case 'f':
915               cmd_pretty_print(FALSE);
916               break;
917             case 'l':
918               if (isdigit((int)*(*argv+1))) {
919                   line_width = atoi(++(*argv));
920                   if (line_width > LINESZ) line_width = LINESZ -1;
921                   goto nextarg;
922               } else if (--argc
923                          && isdigit((int)*(argv[1]))
924                          && ((ch= *(argv[1]+1)) == '\0' || isdigit((int)ch))) {
925                   line_width = atoi(*++argv);
926                   if (line_width > LINESZ) line_width = LINESZ -1;
927                   goto nextarg;
928               } else {
929                   argc++;       /* hack-alert!  Fix error from above */
930                   /* Set line width to COLUMNS-1.  Avoids auto-newline. */
931                   if ((s=getenv("COLUMNS")) == NULL)
932                       line_width = 79;  /* Take a guess.  Oh well */
933                   else {
934                       line_width = atoi(s) -1;
935                       if (line_width > LINESZ) line_width = LINESZ -1;
936                   }
937               }
938               break;
939             case 'm':
940               if (!ARGVAL() || !isdigit((int)**argv)) {
941                   fprintf(stderr, "%s: -m Missing memory-limit\n", myname);
942                   usage();
943               }
944               mem_limit = atoi(*argv);
945               goto nextarg;
946             case 'p':
947               if (!ARGVAL()) {
948                   fprintf( stderr, "%s: -p Missing path-list\n", myname );
949                   usage();
950               }
951               dfpath = *argv;
952               goto nextarg;
953             default:
954               fprintf(stderr, "%s: Unknown flag: '%c'\n", myname, **argv);
955               usage();
956           } /* switch */
957       } /* while */
958     nextarg: continue;
959   } /* for */
960   
961   brl_init( dfname, dfpath, mem_limit );   /* Initialize Bible Retrieval Lib */
962
963   if (argc) {
964       /* read from command line */
965       list_always = TRUE;
966       while (argc--) {
967           user_input( *argv++ );
968       }
969   } else {
970       /* read from stdin */
971       printf( "%s: %s\n", myname, release_version );
972       printf( "Hit '?' for help.\n" );
973       rl_bind_key('\t', rl_insert);
974       brl_printverse( def_verse, pretty_printing, line_width, outf );
975       while((line = readline(getprompt())) != NULL) {
976           user_input( line );
977           if (*line != '\0') add_history(line);
978           free(line);
979       }
980       printf( "\n" );
981   }
982   
983   brl_close();
984   return(0);
985 } /* main */