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 
do_concordance(char * word,ref_t * buf)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 
cmd_ANDconcordance(char * word)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 
cmd_ORconcordance(char * word)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 
cmd_concordance(char * word)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 
cmd_help(void)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 
cmd_inrange(char * range)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 
cmd_list(void)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 
cmd_pretty_print(int verbose)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 
cmd_view(void)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 
cmd_write(char * fname)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 
do_command(char * cmd)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 
user_input(char * cmd)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 
getprompt(void)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 
usage(void)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 
main(int argc,char ** argv)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 */
986