summaryrefslogtreecommitdiff
path: root/biblio/bibtex/utils/bibindex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /biblio/bibtex/utils/bibindex
Initial commit
Diffstat (limited to 'biblio/bibtex/utils/bibindex')
-rw-r--r--biblio/bibtex/utils/bibindex/Makefile189
-rw-r--r--biblio/bibtex/utils/bibindex/README47
-rw-r--r--biblio/bibtex/utils/bibindex/bibindex.c1062
-rw-r--r--biblio/bibtex/utils/bibindex/bibindex.man112
-rw-r--r--biblio/bibtex/utils/bibindex/bibindex.txt132
-rw-r--r--biblio/bibtex/utils/bibindex/biblook.c1245
-rw-r--r--biblio/bibtex/utils/bibindex/biblook.h113
-rw-r--r--biblio/bibtex/utils/bibindex/biblook.man122
-rw-r--r--biblio/bibtex/utils/bibindex/biblook.txt132
9 files changed, 3154 insertions, 0 deletions
diff --git a/biblio/bibtex/utils/bibindex/Makefile b/biblio/bibtex/utils/bibindex/Makefile
new file mode 100644
index 0000000000..619c92a0af
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/Makefile
@@ -0,0 +1,189 @@
+#=======================================================================
+# Makefile for bibindex and biblook.
+#
+# These programs are written in ISO/ANSI Standard C. They must be
+# compiled with a Standard C compiler, or with a C++ compiler.
+#
+# Current target list:
+# all make bibindex and biblook
+# bibindex.txt ascii text file from UNIX man pages
+# bibindex make indexing program
+# bibindex.tar UNIX tar archive file for bibindex
+# distribution
+# bibindex.tar.z compressed UNIX tar archive file for bibindex
+# bibindex.uue uuencoded bibindex.tar.z archive
+# bibindex.zip InfoZip archive file for bibindex
+# distribution
+# bibindex.zoo zoo archive file for bibindex distribution
+# bibindex.tar-lst listing of UNIX tar archive file
+# bibindex.zip-lst listing of InfoZip archive file
+# bibindex.zoo-lst listing of zoo archive file
+# biblook.txt ascii text file from UNIX man pages
+# biblook make lookup program
+# clean remove all recreatable files, except
+# executables
+# clobber remove all recreatable files
+# install install executables and manual pages
+# uninstall uninstall executables and manual pages
+#
+# [01-Jun-1993]
+#=======================================================================
+
+DEST = /usr/local
+
+BINDIR = $(DEST)/bin
+
+CATDIR = $(DEST)/man/cat1
+
+# Compilation with a C++ compiler is preferable. SunOS 4.1 CC cannot be
+# used, however, because of its erroneous function prototypes in stdlib.h
+# which use char* instead of void* in many places. There is no such
+# problem with Sun Solaris 2.1 CC, which works fine.
+CC = CC ## some UNIX C++ compilers
+CC = CC -I/usr/CC/incl ## C++ on SunOS 4.1.1
+CC = acc -Dsun ## SunOS Standard C compiler
+CC = c89 -D_POSIX_SOURCE ## HP 9000/850 HP-UX A.08.00 D
+CC = c89 -D_POSIX_SOURCE ## IBM RS/6000 Standard C
+CC = xlC ## IBM RS/6000 C++
+CC = cc ## many UNIX systems
+CC = cc $(GCCFLAGS) ## NeXT
+CC = lcc -A -A -n ## Princeton/AT&T Standard C compiler
+CC = g++ $(GCCFLAGS) ## UNIX systems with GNU C++
+CC = gcc $(GCCFLAGS) ## GNU C
+CC = g++ $(GCCFLAGS) ## UNIX systems with GNU C++
+CC = g++ $(GCCFLAGS) -Dtemplate=Template ## NeXT systems with GNU C++
+CC = cc -ObjC ## NeXT systems with Objective C
+CC = gcc $(GCCFLAGS) ## GNU C
+
+CFLAGS = $(OPT) ## most cases
+
+GCCFLAGS = -Wall -Wshadow -Wcast-qual -Wpointer-arith \
+ -Wwrite-strings
+
+COL = col -b
+
+COMPRESS = compress
+
+CP = /bin/cp
+
+FTPDIR = /usr/spool/ftp/pub/tex/bib
+
+LDFLAGS =
+
+# Use /usr/lib/debug/malloc.o on Sun systems for malloc debugging
+# with acc, gcc, or CC
+LIBS = /usr/lib/debug/malloc.o
+LIBS =
+
+MANDIR = $(DEST)/man/man1
+
+MANEXT = 1
+
+NROFF = nroff -man
+
+# Define DEBUG_MALLOC on Sun systems for debugging memory allocation
+OPT = -g -DDEBUG_MALLOC
+OPT = -g
+
+RM = /bin/rm -f
+
+SHELL = /bin/sh
+
+TAR = tar
+
+TARFILES = README Makefile bibindex.c bibindex.man bibindex.txt \
+ biblook.c biblook.h biblook.man biblook.txt
+
+UNZIP = unzip
+
+UUENCODE = uuencode
+
+ZIP = zip
+
+ZOO = zoo
+
+#=======================================================================
+
+all: bibindex biblook bibindex.txt biblook.txt
+
+bibindex: bibindex.o
+ $(CC) $(CFLAGS) -o bibindex bibindex.o $(LDFLAGS) $(LIBS)
+
+bibindex.txt: bibindex.man
+ $(NROFF) $? | $(COL) >$@
+
+bibindex.tar: $(TARFILES)
+ $(TAR) cf $@ $(TARFILES)
+
+bibindex.tar-lst: bibindex.tar
+ $(TAR) tvf $? >$@
+
+bibindex.tar.z: bibindex.tar
+ $(COMPRESS) <$? >$@
+
+bibindex.uue: bibindex.tar.z
+ $(UUENCODE) $? $? >$@
+
+bibindex.zip: $(TARFILES)
+ -$(RM) $@
+ $(ZIP) $@ $(TARFILES)
+
+bibindex.zip-lst: bibindex.zip
+ $(UNZIP) -v $? >$@
+
+bibindex.zoo: $(TARFILES)
+ -$(RM) $@
+ $(ZOO) a $@ $(TARFILES)
+
+bibindex.zoo-lst: bibindex.zoo
+ $(ZOO) v $? >$@
+
+biblook: biblook.o
+ $(CC) $(CFLAGS) -o biblook biblook.o $(LDFLAGS) $(LIBS)
+
+biblook.txt: biblook.man
+ $(NROFF) $? | $(COL) >$@
+
+bibindex.o biblook.o: biblook.h
+
+clean mostlyclean:
+ -$(RM) \#*
+ -$(RM) *~
+ -$(RM) core
+ -$(RM) *.o
+ -$(RM) bibindex.tar bibindex.tar.z bibindex.tar-lst
+ -$(RM) bibindex.uue
+ -$(RM) bibindex.zip bibindex.zip-lst
+ -$(RM) bibindex.zoo bibindex.zoo-lst
+
+clobber distclean realclean reallyclean: clean
+ -$(RM) biblook bibindex
+ -$(RM) biblook.txt bibindex.txt
+
+install: bibindex biblook
+ -$(CP) bibindex $(BINDIR)
+ -chmod 775 $(BINDIR)/bibindex
+ -$(CP) biblook $(BINDIR)
+ -chmod 775 $(BINDIR)/biblook
+ -$(CP) bibindex.man $(MANDIR)/bibindex.$(MANEXT)
+ -chmod 774 $(MANDIR)/bibindex.$(MANEXT)
+ -$(CP) biblook.man $(MANDIR)/biblook.$(MANEXT)
+ -chmod 774 $(MANDIR)/biblook.$(MANEXT)
+
+install-ftp: bibindex.tar.z bibindex.zip bibindex.zoo \
+ bibindex.tar-lst bibindex.zip-lst bibindex.zoo-lst
+ -for f in $? ; \
+ do \
+ $(CP) $$f $(FTPDIR) ; \
+ chmod 774 $(FTPDIR)/$$f ; \
+ done
+
+uninstall:
+ -$(RM) $(BINDIR)/bibindex
+ -$(RM) $(BINDIR)/biblook
+ -$(RM) $(MANDIR)/bibindex.$(MANEXT)
+ -$(RM) $(MANDIR)/biblook.$(MANEXT)
+ -$(RM) $(CATDIR)/bibindex.$(MANEXT)
+ -$(RM) $(CATDIR)/biblook.$(MANEXT)
+
+#=======================================================================
diff --git a/biblio/bibtex/utils/bibindex/README b/biblio/bibtex/utils/bibindex/README
new file mode 100644
index 0000000000..7009c644cc
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/README
@@ -0,0 +1,47 @@
+/u/sy/beebe/tex/bibindex/2-4/README, Tue Jun 1 17:00:51 1993
+Edit by Nelson H. F. Beebe <beebe@plot79.math.utah.edu>
+
+This directory contains bibindex and biblook, programs for fast lookup
+in BibTeX bibliography data bases. They were written by Jeff
+Erickson, now at the University of California, Berkeley.
+
+bibindex converts a .bib file to a .bix file, which is a compact
+binary representation of the .bib file containing hash tables for fast
+lookup, as well as byte offset positions into the corresponding .bib
+file.
+
+biblook provides an interactive lookup facility using the .bix and
+.bib files. It verifies that the file version number and bibindex
+version number match its own values, and also compares the file time
+stamps so that it can detect whether the .bix file is out-of-date with
+respect to the .bib file. In either case, execution terminates.
+
+biblook may provide a more convenient, and faster, way of searching
+.bib files than text editors or pattern search utilities like the grep
+programs, particularly since it supports boolean operations between
+pairs of patterns.
+
+Both programs are documented in UNIX man pages, and the *.txt files
+are the output of nroff+col processing of the *.man files, so that
+documentation can read even if nroff is unavailable (e.g. non-UNIX
+systems, or UNIX systems where it is an extra-cost option).
+
+The programs so far run only under UNIX. Plans are to investigate the
+possibility of making them work on other operating systems.
+
+For very large bibliography files, it may be necessary to change the
+type Index_t in biblook.h from "unsigned short" to "unsigned int".
+The choice in version 2.4 and earlier of "unsigned short" is suitable
+for the SIGGRAPH and TeX User Group bibliography collections
+(SIGGRAPH: 6.7MB with 15,400 entries; TUG: 3.9MB with 14,600 entries).
+as of June 1993. Using "unsigned int" increases the size of the .bix
+files by 55% (TUG) to 68% (SIGGRAPH).
+
+Author of README and *.man files:
+ Nelson H. F. Beebe
+ Center for Scientific Computing
+ Department of Mathematics
+ University of Utah
+ Salt Lake City, UT 84112
+ USA
+ Email: beebe@math.utah.edu (Internet)
diff --git a/biblio/bibtex/utils/bibindex/bibindex.c b/biblio/bibtex/utils/bibindex/bibindex.c
new file mode 100644
index 0000000000..af7870896a
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/bibindex.c
@@ -0,0 +1,1062 @@
+/* ================================================================= *\
+
+ bibindex -- a program to index bibtex files, used in conjunction
+ with biblook
+
+ This program was specifically developed for use with the
+ computational geometry bibliographic database, and assumes the
+ rules thereof. The database may be obtained by anonymous ftp
+ from cs.usask.ca in the file "pub/geometry/geombib.tar.Z".
+
+ Version 1.0 written by Jeff Erickson <jeff@ics.uci.edu>, 27 Mar 92
+ Version 2.0 written by Jeff Erickson <jeff@ics.uci.edu>, 17 Jun 92
+
+ This program is in the public domain. You may use it or modify
+ it to your heart's content, at your own risk. Bouquets, brickbats,
+ and bug fixes may be sent to Jeff Erickson, jeffe@cs.berkeley.edu.
+
+ %Make% gcc -O -o bibindex bibindex.c
+
+ Usage: bibindex bibfile [-i field ...]
+
+ -----------------------------------------------------------------
+
+ HOW IT WORKS:
+
+ The bibtex file is read field by field. The file offset beginning
+ each record and each record's citation key are recorded. A list of
+ words is extracted from each field. These words are placed into
+ tables, which remember which records contain them in their
+ respective fields. Once the file has been completely read, the
+ hash tables are compacted and sorted.
+
+ The hash tables are extensible, since we have to maintain one for
+ each possible field type, and static tables would be way too big.
+ Initially, each table holds 1K entries, and the tables are doubled
+ whenever they reach 75% capacity. Each table entry is at least 24
+ bytes. If the resulting hash tables use too much memory, the
+ entries should be changed to pointers, allocated on the fly.
+
+ The entry lists associated with each word are implemented as
+ extensible arrays. Initially, each list holds eight entries. If a
+ new entry is inserted into a full list, the list is doubled first.
+
+ The index file has the following format (loosely):
+
+ version info
+ # entries
+ array of offsets into bib file -- one per entry
+ # field types
+ array of field names -- one per field type
+ array of -- one per field type
+ # words
+ array of -- one per word
+ word -- in alphabetical order
+ # locations
+ array of entry #s -- one per location
+
+ There are advantages and disadvantages of having multiple hash
+ tables instead of a single table. I am starting with the premise
+ that the lookup program should be very fast. Consequently, I can't
+ make it determine which fields contain a given word. Doing so
+ would require putting ALL of the entry-parsing code into the lookup
+ program. It would also mean potentially parsing a lot of
+ extraneous entries to find relatively common words in relatively
+ uncommon places (eg, "title edelsbrunner").
+
+ If there were a single word table, each word list would have to
+ include bitmasks to indicate the appropriate fields along with the
+ entry numbers. Assuming between 16 and 32 field types (the CG bib
+ uses about 24), this would triple the size of each entry. On the
+ average, each word occurs in less than two field types. The
+ bitmask approach would also require knowledge of the field names in
+ advance; the multiple table approach does not.
+
+ -----------------------------------------------------------------
+ VERSION HISTORY:
+
+ 1.0 <jge> 3/26/92 Initial version completed
+ 1.1 <jge> 3/27/92 Words restricted to letters only; special
+ rules added for apostrophes, so that words
+ like "didn't" and "O'Rourke" can be handled
+ correctly.
+ 1.2 <jge> 3/30/92 Faster hash function; now compressing hash
+ tables before sorting them. Execution time on
+ the CG bib reduced by a factor of thirty-five.
+ 1.3 <jge> 4/2/92 Toyed with the hash function some more, trying
+ to reduce collisions, with limited success.
+ 1.4 <jge> 4/17/92 Added exit(0) at the end of main() [I thought
+ that was supposed to be automatic!]
+
+ 2.0 <jge> 6/12/92 First major revision completed.
+ 1. Major change in file format -- word tables for every
+ field instead of just author, title, and keywords.
+ 2. Word hash tables made extensible.
+ 3. Fixed bug in GetNextWord that would cause the function
+ to return inside nested braces.
+ 4. Fixed bug in MungeField that would kill everything in an
+ entry after an abbreviation. Abbreviations now go into
+ the appropriate hash table with the other words.
+ 5. Made GetNextWord consider numbers and math expressions
+ to be words.
+ 6. Added WriteWord, resulting in 40% savings in disk space.
+ 7. Added -i flag and black holes. Ignoring "oldlabel"
+ gives another 15% savings (6/92 version of CGbib).
+ 2.1 <jge> 7/9/92 Minor bug fixes.
+ 2.2 Nelson H. F. Beebe <beebe@math.utah.edu> 03-Oct-1992
+ Testing with >100K lines of .bib files led to these changes:
+ 1. Add support for complete BibTeX keyword name syntax with
+ iskeychar() function.
+ 2. Add support for trailing comma after last key = "value" entry.
+ 3. Record input line number for display in error messages.
+ 4. Record initial line number of each BibTeX entry for
+ display in error messages to better localize error.
+ 5. Add test for word buffer overflow in MungeField() to prevent
+ run-time core dumps, and increase supported word size from
+ 15 to 31 (a word-length histogram of a 116921-word dictionary
+ found words up to 28 characters long, with 1% longer than 15).
+ File version increased to 2 because of word size change.
+ 6. Add typecasts in calls to qsort() and comparisons of
+ unsigned short with short, change main() from void to int,
+ remove variable from initializer of msg[2], and add void to
+ IndexBibFile() definition to allow compilation with C++ as
+ well as C for better compile-time checking.
+ 7. In MungeEntry(), do an ungetc() after key name collection.
+ Otherwise, a key="value" pair without a blank before the =
+ will not be recognized because the = read on lookahead has
+ not been put back.
+ 8. Revise table compression code in OutputTables() and
+ code in FreeTables() to avoid duplicate frees, which is
+ a fatal error on many systems, and specified to result
+ in undefined behavior by the 1989 ANSI/ISO C Standard.
+ 9. Define bcopy() as a macro to invoke standard memcpy()
+ instead.
+ 10. Include time.h, unistd.h, and malloc.h to get proper
+ function declarations for library routines needed here.
+ 11. Add DEBUG_MALLOC support.
+ 12. Change several char* types to const char*.
+ 13. Correct some typographical errors in comment.
+ 14. Write UNIX manual pages for bibindex and biblook.
+ 15. Allow command-line to specify a filename with .bib extension.
+ 16. Add help support to biblook.
+ 17. Correct error in FreeTables() in biblook.c; inner loop
+ incremented i instead of j.
+ 2.3 Bill Jones <jones@cs.usask.ca> 93/01/29
+ 1. Declarations common to bibindex.c and biblook.c factored out
+ to new file biblook.h.
+ 2. Index type of (signed) short overflows early; created typedef
+ Index_t, defined as unsigned short.
+ 3. Changed hash tables to extend at 75% capacity rather than 50%.
+ 2.4 Nelson H. F. Beebe <beebe@math.utah.edu> [01-Jun-1993]
+ 1. Remove some mixed-mode arithmetic.
+ 2. Increase MAXFIELDS from 64 to 127 to deal with TeX User Group
+ bibliography collection
+ 3. Correct error in GetHashTable(); older versions got into an
+ infinite loop if MAXFIELDS field names were already stored, and
+ a new one was encountered.
+
+\* ================================================================= */
+
+
+#include "biblook.h"
+
+static long line_number = 1L; /* for debug messages */
+static long initial_line_number = 1L;
+
+
+/* ======================= UTILITY FUNCTIONS ======================= */
+
+/* ----------------------------------------------------------------- *\
+| void die(const char *msg1, const char *msg2) -- print an error message and die
+\* ----------------------------------------------------------------- */
+void die(const char *msg1, const char *msg2)
+{
+ fprintf(stderr,
+ "\nError:\tin BibTeX entry starting at line %ld, \
+error detected at line %ld:\n\t%s %s\n",
+ initial_line_number, line_number, msg1, msg2);
+ exit(1);
+}
+
+/* ----------------------------------------------------------------- *\
+| char safegetc(FILE *fp, const char *where)
+|
+| Get the next character safely. Used by routines that assume that
+| they won't run into the end of file.
+\* ----------------------------------------------------------------- */
+char safegetc(FILE *fp, const char *where)
+{
+ char c;
+
+ if (feof(fp))
+ die("Unexpected end of file in", where);
+
+ c = getc(fp);
+ if (c == '\n')
+ line_number++;
+ return (c);
+}
+
+/* ----------------------------------------------------------------- *\
+| void *safemalloc(unsigned howmuch, const char *msg1, const char *msg2)
+|
+| Allocate memory safely. Used by routines that assume they won't
+| run out of memory.
+\* ----------------------------------------------------------------- */
+void *safemalloc(unsigned howmuch, const char *msg1, const char *msg2)
+{
+ register void *tmp = NULL;
+
+ tmp = malloc(howmuch);
+ if (tmp == NULL)
+ die(msg1, msg2);
+
+ return tmp;
+}
+
+
+/* ====================== HASH TABLE FUNCTIONS ===================== *\
+
+ The hash tables start small and double whenever they reach 75%
+ capacity. Hashing is performed by going through the string one
+ character at a time, multiplying by a constant and adding in the
+ new character value each time. The constant is defined to give
+ the same even spread (about size/sqrt(2)) between successive
+ multiples, as long as the hash table size is a power of two.
+
+ Collisions are resolved by double hashing. Since the hash table
+ size is always a power of two, the secondary hash value has to be
+ odd to avoid loops.
+
+ The field tables are non-extensible hash tables, otherwise handled
+ the same way. It is probably well worth the effort to fine tune
+ the field table hash function in order to avoid collisions.
+
+ The field tables associated with ignored fields are black holes.
+ Everything is the same, except that InsertEntry doesn't actually
+ DO anything.
+
+\* ================================================================= */
+
+#define MAXFIELDS 127 /* intentionally way too much */
+ /* NB: limited by char type of numfields */
+
+#define INIT_HASH_SIZE 256
+#define HASH_CONST 1482907 /* prime close to 2^{20.5} */
+
+typedef struct /* Hash table entry */
+{
+ Word theword; /* the hashed word */
+ Index_t number; /* number of references in the list */
+ Index_t size; /* real size of reference list */
+ Index_t *refs; /* actual list of references */
+} HashCell, *HashPtr;
+
+typedef struct /* Extensible hash table */
+{
+ Word thefield; /* the field type */
+ Index_t number; /* number of words in the hash table */
+ Index_t size; /* real size of the hash table */
+ HashPtr words; /* the actual hash table */
+} ExHashTable;
+
+static ExHashTable fieldtable[MAXFIELDS]; /* the field tables */
+static char numfields; /* number of fields */
+
+
+/* ----------------------------------------------------------------- *\
+| void InitTables(void)
+|
+| Initialize the field tables
+\* ----------------------------------------------------------------- */
+void InitTables(VOID)
+{
+ register unsigned short i;
+
+ numfields = 0;
+ for (i=0; i<MAXFIELDS; i++)
+ {
+ fieldtable[i].thefield[0] = 0;
+ fieldtable[i].number = 0;
+ fieldtable[i].size = 0;
+ fieldtable[i].words = NULL;
+ }
+}
+
+/* ----------------------------------------------------------------- *\
+| void InitOneField(ExHashTable *htable)
+|
+| Initialize one field's hash table
+\* ----------------------------------------------------------------- */
+void InitOneField(register ExHashTable *htable)
+{
+ Index_t i;
+
+ htable->number = 0;
+ htable->size = INIT_HASH_SIZE;
+ htable->words = (HashPtr) safemalloc(INIT_HASH_SIZE*sizeof(HashCell),
+ "Can't create hash table for",
+ htable->thefield);
+ for (i=0; i<INIT_HASH_SIZE; i++)
+ {
+ htable->words[i].theword[0] = 0;
+ htable->words[i].number = 0;
+ htable->words[i].size = 0;
+ htable->words[i].refs = NULL;
+ }
+}
+
+/* ----------------------------------------------------------------- *\
+| void FreeTables(void)
+|
+| Free the tables
+\* ----------------------------------------------------------------- */
+void FreeTables(VOID)
+{
+ register unsigned short i;
+ Index_t j;
+
+ for (i=0; i < (unsigned short)numfields; i++)
+ {
+ if (fieldtable[i].words)
+ {
+ for (j=0; j < fieldtable[i].number; j++)
+ {
+ if (fieldtable[i].words[j].refs)
+ free(fieldtable[i].words[j].refs);
+ }
+ free(fieldtable[i].words);
+ }
+ }
+}
+
+
+/* ----------------------------------------------------------------- *\
+| ExHashTable *GetHashTable(char *field)
+|
+| Get the hash table associated with the given field. If the table
+| is unclaimed, claim it and initialize it.
+\* ----------------------------------------------------------------- */
+ExHashTable *GetHashTable(char *field)
+{
+ register unsigned long hash=0; /* primary hash value */
+ register unsigned long skip=1; /* secondary hash value */
+ register int i;
+
+ for (i=0; field[i]; i++)
+ {
+ hash = (hash*HASH_CONST + field[i]) % MAXFIELDS;
+ skip += 2*hash;
+ }
+
+ while (fieldtable[hash].thefield[0] && /* cell not empty and */
+ strcmp(fieldtable[hash].thefield, field)) /* not the right field */
+ {
+ hash = (hash+skip) % MAXFIELDS;
+ }
+
+ if (!fieldtable[hash].thefield[0])
+ {
+ strcpy(fieldtable[hash].thefield, field);
+ InitOneField(fieldtable+hash);
+ numfields++;
+ if (numfields >= MAXFIELDS) /* NB: NOT > because that produces */
+ /* an infinite while() loop above on */
+ /* next entry! */
+ die("too many field names",field);
+ }
+
+ return fieldtable+hash;
+}
+
+/* ----------------------------------------------------------------- *\
+| void InitBlackHole(char *field)
+|
+| Intitialize a black hole for the given field.
+\* ----------------------------------------------------------------- */
+void InitBlackHole(char *field)
+{
+ ExHashTable *hole;
+
+ hole = GetHashTable(field);
+ free(hole->words);
+ hole->words = NULL;
+}
+
+/* ----------------------------------------------------------------- *\
+| HashPtr GetHashCell(ExHashTable *table, char *word)
+|
+| Get the hash table cell associated with the given word. If the cell
+| is unclaimed, claim it, initialize it, and update the table's word
+| count.
+\* ----------------------------------------------------------------- */
+HashPtr GetHashCell(ExHashTable *htable, char *word)
+{
+ register HashPtr table, cell;
+ register unsigned long hash=0; /* primary hash value */
+ register unsigned long skip=1; /* secondary hash value */
+ register int i;
+
+ table = htable->words;
+
+ for (i=0; word[i]; i++)
+ {
+ hash = (hash*HASH_CONST + word[i]) % htable->size;
+ skip += 2*hash;
+ }
+
+ while (table[hash].theword[0] && /* cell not empty and */
+ strcmp(table[hash].theword, word)) /* not the right word */
+ {
+ hash = (hash+skip) % htable->size;
+ }
+ cell = table+hash;
+
+ if (!cell->theword[0])
+ {
+ strcpy(cell->theword, word);
+ cell->size = 8;
+ cell->refs = (Index_t *) safemalloc(cell->size * sizeof(Index_t),
+ "Can't create entry list for",
+ word);
+ htable->number++;
+ }
+ return cell;
+}
+
+/* ----------------------------------------------------------------- *\
+| void ExtendHashTable(ExHashTable *htable)
+|
+| Double the size of the hash table and rehash everything.
+\* ----------------------------------------------------------------- */
+void ExtendHashTable(ExHashTable *htable)
+{
+ register HashPtr newcell;
+ register HashPtr oldtable;
+ Index_t i;
+ Index_t oldsize;
+
+ oldsize = htable->size;
+ oldtable = htable->words;
+
+ htable->number = 0;
+ htable->size *= 2;
+ htable->words = (HashPtr) safemalloc(sizeof(HashCell)*htable->size,
+ "Can't extend hash table for",
+ htable->thefield);
+
+ for (i=0; i < htable->size; i++)
+ {
+ htable->words[i].theword[0] = 0;
+ htable->words[i].number = 0;
+ htable->words[i].size = 0;
+ htable->words[i].refs = NULL;
+ }
+
+ for (i=0; i< oldsize; i++)
+ {
+ if (oldtable[i].theword[0])
+ {
+ newcell = GetHashCell(htable, oldtable[i].theword);
+ *newcell = oldtable[i];
+ }
+ }
+
+ free(oldtable);
+}
+
+/* ----------------------------------------------------------------- *\
+| void InsertEntry(ExHashTable *htable, char *word, Index_t entry)
+|
+| Insert the word/entry pair into the hash table, unless it's
+| already there.
+\* ----------------------------------------------------------------- */
+void InsertEntry(ExHashTable *htable, char *word, Index_t entry)
+{
+ register HashPtr cell;
+ Index_t *newlist;
+
+ if (htable->words == NULL) /* Is it a black hole? */
+ return;
+
+ if ((Index_t)(htable->number * 4) > (Index_t)(htable->size * 3))
+ ExtendHashTable(htable);
+
+ cell = GetHashCell(htable, word);
+
+ if (cell->number && (cell->refs[cell->number-1] == entry))
+ return;
+
+ if (cell->number == cell->size) /* expand the array */
+ {
+ cell->size *= 2;
+ newlist = (Index_t *) safemalloc(cell->size * sizeof(Index_t),
+ "Can't extend entry list for",
+ word);
+
+ bcopy(cell->refs, newlist, cell->number * sizeof(Index_t));
+ free(cell->refs);
+ cell->refs = newlist;
+ }
+ cell->refs[cell->number++] = entry;
+ if (cell->number <= 0)
+ die("hash type overflow", htable->thefield);
+}
+
+
+/* ================================================================= */
+
+/* ----------------------------------------------------------------- *\
+| void WriteWord(FILE *ofp, Word word)
+|
+| Output the word in "Pascal" string format -- length byte followed
+| by characters. This saves some disk space over writing MAXWORD+1 bytes
+| in all cases.
+\* ----------------------------------------------------------------- */
+void WriteWord(FILE *ofp, Word word)
+{
+ unsigned char length = strlen(word);
+ fwrite((void *) &length, sizeof(char), 1, ofp);
+ fwrite((void *) word, sizeof(char), length, ofp);
+}
+
+/* ----------------------------------------------------------------- *\
+| void OutputTables(FILE *ofp)
+|
+| Compress and output the tables, with lots of user feedback.
+| KLUDGE -- Passing strcmp to qsort assumes intimate knowledge of
+| the HashCell and ExhashTable structs.
+\* ----------------------------------------------------------------- */
+void OutputTables(FILE *ofp)
+{
+ register HashPtr words;
+ register int i, k, count;
+ Index_t m, n;
+
+ printf("Writing index tables...");
+ fflush(stdout);
+
+ numfields = 0; /* recount, ignoring black holes */
+ for (i=0; i<MAXFIELDS; i++)
+ {
+ if (fieldtable[i].words)
+ {
+ if (i > numfields)
+ {
+ fieldtable[numfields] = fieldtable[i]; /* copy i-th table */
+ fieldtable[i].number = 0; /* then clear i-th table */
+ fieldtable[i].size = 0; /* to avoid duplicate free() later */
+ fieldtable[i].words = NULL;
+ }
+ numfields++;
+ }
+ }
+ qsort(fieldtable, (size_t)numfields, sizeof(ExHashTable),
+ (int (*)(const void*,const void*))strcmp);
+
+ fwrite((void *) &numfields, sizeof(char), 1, ofp);
+ for (i=0; i<numfields; i++)
+ WriteWord(ofp, fieldtable[i].thefield);
+
+ printf("[%d fields]\n", numfields);
+
+ for (k=0; k<numfields; k++)
+ {
+ printf("%2d: %s...", k+1, fieldtable[k].thefield);
+ fflush(stdout);
+
+ words = fieldtable[k].words;
+
+ for (m=0, n=0; m<fieldtable[k].size; m++)
+ {
+ if (words[m].theword[0])
+ {
+ if (m > n)
+ {
+ words[n] = words[m]; /* copy m-th table to n-th */
+ words[m].number = 0; /* then clear m-th table */
+ words[m].size = 0; /* to avoid duplicate free() later */
+ words[m].refs = (Index_t*)0;
+ }
+ n++;
+ }
+ }
+ qsort(words, (size_t)fieldtable[k].number, sizeof(HashCell),
+ (int (*)(const void*,const void*))strcmp);
+
+ fwrite((void *) &(fieldtable[k].number), sizeof(Index_t), 1, ofp);
+
+ count = 0;
+ for (m=0; m<fieldtable[k].number; m++)
+ {
+ WriteWord(ofp, words[m].theword);
+ fwrite((void *) &(words[m].number), sizeof(Index_t), 1, ofp);
+ fwrite((void *) words[m].refs, sizeof(Index_t),
+ words[m].number, ofp);
+ count += words[m].number;
+ }
+
+ printf("[%d words, %d references]\n", fieldtable[k].number, count);
+ }
+}
+
+
+/* ============================= INPUT ============================= *\
+
+ I'd like this to work with more than just the CG bib, so I can't
+ assume very much about the input. In particular, all white space
+ (blanks, tabs, and newlines) is identical most of the time. On the
+ other hand, it would be nice to include any "comments" that
+ obviously go with an entry as part of that entry. Consequently, two
+ newlines in a row (possibly separated by other white space) is
+ considered a break between entries. This will give us bogus entries
+ for stray "comments", but we can take care that easily enough -- an
+ entry is only real if it contains a @ character.
+
+\* ================================================================= */
+
+/* ----------------------------------------------------------------- *\
+| unsigned long FindNextEntry(FILE *ifp)
+|
+| Return the file offset to the next entry in the bib file. On exit,
+| the file pointer is left at the first comma. Entries containing no
+| commas, such as @string, are skipped. The entry begins after the
+| most recent blank line, the end of the last entry, or the beginning
+| of the file. Returns "-1" if there is no next entry.
+\* ----------------------------------------------------------------- */
+unsigned long FindNextEntry(FILE *ifp)
+{
+ char ch;
+ char blank = 0; /* 1 if current line is blank so far */
+ char braces = 0;
+ char quotes = 0;
+ unsigned long offset;
+
+ offset = ftell(ifp);
+ ch = getc(ifp);
+ if (ch == '\n')
+ line_number++;
+
+ while (1)
+ {
+ if (feof(ifp))
+ return (unsigned long) -1;
+
+ if (ch == '@') /* got a potential entry */
+ {
+ initial_line_number = line_number; /* record start of entry */
+ blank = 0;
+ while ((ch != '{') && (ch != '('))
+ ch = safegetc(ifp, "FindNextEntry 1"); /* EOF is an */
+ /* error here */
+ quotes = 0;
+ braces = 0;
+ ch = safegetc(ifp, "FindNextEntry 2");
+
+ while (quotes || braces ||
+ ((ch != ',') && (ch != '}') && (ch != ')')))
+ {
+ if (ch == '{')
+ braces++;
+ else if (ch == '}')
+ braces--;
+ else if ((ch == '"') && !braces)
+ quotes = !quotes;
+
+ ch = safegetc(ifp, "FindNextEntry 3");
+ }
+ if (ch == ',')
+ {
+ ungetc(ch, ifp);
+ return offset;
+ }
+ else
+ offset = ftell(ifp);
+ }
+ else if (ch == '\n')
+ {
+ if (blank)
+ offset = ftell(ifp);
+ blank = 1;
+ }
+ else if (!isspace(ch))
+ blank = 0;
+ ch = getc(ifp);
+ if (ch == '\n')
+ line_number++;
+ }
+}
+
+/* ----------------------------------------------------------------- *\
+| int GetNextWord(FILE *ifp, char *word)
+|
+| Get the next word in the current field. A word is any contiguous
+| set of letters and numbers, AFTER the following steps:
+| 1. Letters are folded to lower case. Thus, "Voronoi" is
+| returned as "voronoi"
+| 2. All TeX commands, except those in math expressions, are
+| removed, but their arguments are left behind. Thus,
+| "Erd{\H o}ss" is returned as "erdos".
+| 3. All other non-word characters are removed. Non-word
+| characters inside {{possibly} nested} braces or dollar
+| signs do not delimit words, so they may cause unexpected
+| results. Thus, "{this example}" is returned as
+| "thisexample".
+| 4. TeX commands in math expressions are considered normal
+| text. Thus, "$O(n\log^2 n)$" is returned as "onlog2n"
+| instead of "onn". This occasionally gives unexpected or
+| unreadable results. For example, "$\bigcup_1^n[a_i,b_i]$"
+| is returned as "bigcup1naibi".
+| 5. Apostrophes do not delimit words. Thus, "didn't" is
+| returned as "didnt", and "{\'O}'D{\'u}nlaing" is returned
+| as "odunlaing".
+|
+| Returns 1 if a word was actually found, 0 otherwise.
+|
+| The file pointer is left at the character immediately following the
+| word. The input is assumed to be syntactically correct: unbalanced
+| braces, math delimiters, and quotation marks will cause errors.
+\* ----------------------------------------------------------------- */
+int GetNextWord(FILE *ifp, char *word)
+{
+ char braces = 0; /* levels of indented braces */
+ char math = 0; /* 1 if in math expression */
+ char inword = 0; /* 1 if word has started */
+ char incmd = 0; /* 1 if reading TeX command */
+ char done = 0; /* 1 if word is complete */
+ char ch = ' ';
+ int nchars = 0; /* how many characters stored in word */
+
+ while (!done)
+ {
+ ch = safegetc(ifp, "GetNextWord 1");
+
+ if (isalpha(ch)) /* letters */
+ {
+ if (!incmd) /* may be part of TeX command */
+ {
+ inword = 1;
+ if (++nchars <= MAXWORD) /* ignore overflow in word buffer */
+ *word++ = tolower(ch);
+ }
+ }
+ else if (isdigit(ch))
+ {
+ incmd = 0;
+ inword = 1;
+ if (++nchars <= MAXWORD) /* ignore overflow in word buffer */
+ *word++ = ch;
+ }
+ else if (ch == '\'') /* ignore apostrophes entirely */
+ incmd = 0;
+ else if (ch == '\\') /* beginning of TeX command */
+ {
+ if (!math) /* if math, pretend it's text */
+ {
+ ch = safegetc(ifp, "GetNextWord 2");
+ if (isalpha(ch))
+ incmd = 1;
+ }
+ }
+ else if (ch == '{') /* left brace */
+ {
+ incmd = 0;
+ braces++;
+ }
+ else if (ch == '}') /* right brace */
+ {
+ incmd = 0;
+ if (!braces)
+ {
+ ungetc(ch, ifp);
+ done = 1;
+ }
+ else
+ braces--;
+ }
+ else if (ch == '"') /* double quote */
+ {
+ incmd = 0;
+ if (!braces)
+ {
+ ungetc(ch, ifp);
+ done = 1;
+ }
+ }
+ else if (ch == '$') /* math */
+ {
+ incmd = 0;
+ math = !math;
+ if (math) /* treat $s as braces */
+ braces++;
+ else
+ braces--;
+ }
+ else if (incmd) /* other characters */
+ incmd = 0;
+ else if (inword && !braces)
+ done = 1;
+ }
+
+ *word = 0;
+ return inword;
+}
+
+const char *badwords[] = BADWORDS;
+
+/* ----------------------------------------------------------------- *\
+| void MungeField(FILE *ifp, ExHashTable *htable, Index_t entry)
+|
+| Munge the current field. Dump all words into the given hash table,
+| with the given entry number. On exit, the file pointer is on the
+| comma or closing character for the entry.
+\* ----------------------------------------------------------------- */
+void MungeField(FILE *ifp, ExHashTable *htable, Index_t entry)
+{
+ int i;
+ char ch, bad;
+ char nextword[MAXWORD+1];
+
+ ch = safegetc(ifp, "MungeField");
+ while (isspace(ch))
+ ch = safegetc(ifp, "MungeField");
+
+ if (ch != '=')
+ die("= expected in bibliography field", htable->thefield);
+
+ ch = safegetc(ifp, "MungeField");
+ while (isspace(ch))
+ ch = safegetc(ifp, "MungeField");
+
+ if (ch != '{' && ch != '"') /* handle abbreviations */
+ {
+ for (i=0; !strchr(NONABBREV, ch); i++)
+ {
+ if (i >= MAXWORD)
+ die("word buffer overflow", htable->thefield);
+ nextword[i] = tolower(ch);
+ ch = safegetc(ifp, "MungeField");
+ }
+ nextword[i] = 0;
+ nextword[MAXWORD] = 0;
+ InsertEntry(htable, nextword, entry);
+ }
+ else
+ {
+ while (GetNextWord(ifp, nextword))
+ {
+ if (nextword[1] == 0) /* ignore single letters */
+ continue;
+ for (bad=0, i=0; !bad && badwords[i]; i++)
+ if (!strcmp(nextword, badwords[i]))
+ bad = 1;
+ if (!bad)
+ {
+ nextword[MAXWORD] = 0; /* truncate to fit */
+ InsertEntry(htable, nextword, entry);
+ }
+ }
+ ch = safegetc(ifp, "MungeField"); /* close quote/brace */
+ ch = safegetc(ifp, "MungeField"); /* comma (maybe) */
+ }
+
+ while (isspace(ch))
+ ch = safegetc(ifp, "MungeField");
+ ungetc(ch, ifp); /* comma or close paren/brace */
+}
+
+/* ----------------------------------------------------------------- *\
+| int iskeychar(char c,int first_char)
+|
+| Return 1 if c is a valid keyword character, and 0 otherwise.
+| The rules are different for the first character, so first_char
+| must be set non-zero to select that case.
+|
+| The code in bibtex.web in the section on character set dependencies
+| creates a character classification array, id_class[], which permits
+| the following characters (verified by experiment too) in field
+| names:
+|
+| A-Z a-z 0-9
+| ! $ & + - . / : ; < > ? @ [ \ ] ^ _ ` | ~ <DELete>
+|
+\* ----------------------------------------------------------------- */
+
+int iskeychar(char c,int first_char)
+{
+ if (first_char)
+ return (isalpha(c) ? 1 : 0);
+ else if (isalpha(c) || isdigit(c))
+ return (1);
+ else if (strchr("!$&+-./:;<>?@[\\]^_`|~\177",(int)c) != (char*)NULL)
+ return (1);
+ else
+ return (0);
+}
+
+/* ----------------------------------------------------------------- *\
+| void MungeEntry(FILE *ifp, Index_t entry)
+|
+| Wander through the entry, extracting words and depositing them in
+| the appropriate hash tables.
+\* ----------------------------------------------------------------- */
+void MungeEntry(FILE *ifp, Index_t entry)
+{
+ register char ch;
+ Word thefield;
+ short i;
+
+ ch = safegetc(ifp, "MungeEntry");
+ while (ch == ',')
+ {
+ ch = safegetc(ifp, "MungeEntry");
+ while (isspace(ch))
+ ch = safegetc(ifp, "MungeEntry");
+
+ if ((ch == '}') || (ch == ')')) /* allow trailing comma after */
+ return; /* last key = "value" entry */
+
+ if (!isalpha(ch))
+ {
+ char msg[2];
+ msg[0] = ch;
+ msg[1] = 0;
+ die("Nonalpha character in field descriptor:", msg);
+ }
+
+ for (i=0; iskeychar(ch,i==0); i++)
+ {
+ if (i >= MAXWORD)
+ {
+ thefield[i] = 0;
+ die("field name buffer overflow", thefield);
+ }
+ thefield[i] = tolower(ch);
+ ch = safegetc(ifp, "MungeEntry");
+ }
+ ungetc(ch, ifp); /* put back lookahead char */
+ thefield[i] = 0;
+
+ MungeField(ifp, GetHashTable(thefield), entry);
+
+ ch = safegetc(ifp, "MungeEntry");
+ }
+}
+
+
+/* ============================= OUTPUT ============================ */
+
+/* ----------------------------------------------------------------- *\
+| IndexBibFile(FILE *ifp, FILE *ofp, char *filename)
+|
+| Index a bibtex file. Input comes from ifp; output goes to ofp.
+| Filename is the name of the bibliography, with no prefix.
+\* ----------------------------------------------------------------- */
+void
+IndexBibFile(FILE *ifp, FILE *ofp, char *filename)
+{
+ Index_t count=0;
+ long curoffset;
+ long *offsets;
+ long *oldoff;
+ Index_t offsize;
+ time_t now = time(0);
+
+ printf("Indexing %s.bib...", filename);
+ fflush(stdout);
+
+ offsize = 128; /* MINIMUM OFFSET LIST SIZE */
+ offsets = (long *) malloc(offsize * sizeof(long));
+
+ while (!feof(ifp))
+ {
+ curoffset = FindNextEntry(ifp);
+ if (curoffset == (unsigned long) -1)
+ break;
+
+ offsets[count] = curoffset;
+ MungeEntry(ifp, count++);
+
+ if (count == offsize) /* expand full offset array */
+ {
+ oldoff = offsets;
+ offsize *= 2;
+ offsets = (long *) malloc(offsize * sizeof(long));
+ bcopy(oldoff, offsets, count * sizeof(long));
+ free(oldoff);
+ }
+
+ if (!(count % 500)) /* feedback */
+ {
+ printf("%d...", count);
+ fflush(stdout);
+ }
+ }
+
+ printf("done.\n");
+
+ fprintf(ofp, "bibindex %d %d %d %s", FILE_VERSION, MAJOR_VERSION,
+ MINOR_VERSION, ctime(&now));
+
+ printf("Writing file offsets...");
+ fflush(stdout);
+ fwrite((void *) &count, sizeof(Index_t), 1, ofp);
+ fwrite((void *) offsets, sizeof(long), count, ofp);
+ free(offsets);
+ printf("[%d entries]\n", count);
+
+ OutputTables(ofp);
+
+ printf("All done!\n");
+}
+
+/* ----------------------------------------------------------------- *\
+| The main program
+\* ----------------------------------------------------------------- */
+
+int main(int argc, char **argv)
+{
+ FILE *ifp;
+ FILE *ofp;
+ char infile[256];
+ char outfile[256];
+ char *p;
+ int i;
+
+#if DEBUG_MALLOC
+ malloc_debug(2);
+#endif /* DEBUG_MALLOC */
+
+ if (argc < 2)
+ die("Usage: bibindex bib [-i field...]", "");
+
+ if (((p = strrchr(argv[1],'.')) != (char*)NULL) &&
+ (strcmp(p, ".bib") == 0))
+ *p = '\0'; /* remove any .bib extension */
+
+ sprintf(infile, "%s.bib", argv[1]);
+ sprintf(outfile, "%s.bix", argv[1]);
+
+ ifp = fopen(infile, "r");
+ if (!ifp)
+ die("Can't read", infile);
+
+ ofp = fopen(outfile, "w");
+ if (!ofp)
+ die("Can't write", outfile);
+
+ InitTables();
+
+ if ((argc > 2) && (!strcmp(argv[2], "-i")))
+ for (i=3; i<argc; i++)
+ InitBlackHole(argv[i]);
+
+ IndexBibFile(ifp, ofp, argv[1]);
+
+ FreeTables();
+ fclose(ifp);
+ fclose(ofp);
+
+ exit(0); /* Argh! */
+ return (0); /* keep compilers happy */
+}
diff --git a/biblio/bibtex/utils/bibindex/bibindex.man b/biblio/bibtex/utils/bibindex/bibindex.man
new file mode 100644
index 0000000000..6993a45c50
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/bibindex.man
@@ -0,0 +1,112 @@
+.\" ====================================================================
+.\" @Troff-man-file{
+.\" author = "Nelson H. F. Beebe",
+.\" version = "1.02",
+.\" date = "03 October 1992",
+.\" time = "14:49:29 MDT",
+.\" filename = "bibindex.man",
+.\" address = "Center for Scientific Computing
+.\" Department of Mathematics
+.\" University of Utah
+.\" Salt Lake City, UT 84112
+.\" USA
+.\" Tel: +1 801 581 5254
+.\" FAX: +1 801 581 4148",
+.\" checksum = "39992 112 591 4491",
+.\" email = "beebe@math.utah.edu (Internet)",
+.\" codetable = "ISO/ASCII",
+.\" keywords = "bibliography, BibTeX",
+.\" supported = "yes",
+.\" docstring = "This file is the UNIX nroff/troff manual page
+.\" documentation for bibindex, a BibTeX bibliography
+.\" indexing program.
+.\"
+.\" The checksum field above contains a CRC-16
+.\" checksum as the first value, followed by the
+.\" equivalent of the standard UNIX wc (word
+.\" count) utility output of lines, words, and
+.\" characters. This is produced by Robert
+.\" Solovay's checksum utility.",
+.\" }
+.\" ====================================================================
+.if t .ds Bi B\s-2IB\s+2T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Bi BibTeX
+.if t .ds Te T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Te TeX
+.TH BIBINDEX 1 "03 October 1992" "Version 2.2"
+.SH NAME
+bibindex \- create a bibliography index file for \fBbiblook\fP(1)
+.SH SYNOPSIS
+.B "bibindex \fIbasename\fP [[\-i] word] [[\-i] word] .\|.\|.
+.SH DESCRIPTION
+.I bibindex
+creates a compact binary index file from a \*(Bi\& bibliography file
+to permit fast lookup by \fIbiblook\fP(1). The \fIbasename\fP
+argument can optionally omit the \fI.bib\fP extension. The output
+index file will be named with the same basename, and extension
+\fI.bix\fP.
+.PP
+For indexing purposes, a word is any contiguous set of letters and
+numbers, \fIafter\fP the following steps:
+.RS
+.TP \w'1.'u+2n
+1.
+Letters are folded to lower case. Thus, "Voronoi" is
+returned as "voronoi"
+.TP
+2.
+All \*(Te\& commands, except those in math expressions, are
+removed, but their arguments are left behind. Thus,
+"Erd{\\H o}ss" is returned as "erdos".
+.TP
+3.
+All other non-word characters are removed. Non-word
+characters inside {{possibly} nested} braces or dollar
+signs do not delimit words, so they may cause unexpected
+results. Thus, "{this example}" is returned as
+"thisexample".
+.TP
+4.
+\*(Te\& commands in math expressions are considered normal
+text. Thus, "$O(n\\log^2 n)$" is returned as "onlog2n"
+instead of "onn". This occasionally gives unexpected or
+unreadable results. For example, "$\\bigcup_1^n[a_i,b_i]$"
+is returned as "bigcup1naibi".
+.TP
+5.
+Apostrophes do not delimit words. Thus, "didn't" is
+returned as "didnt", and "{\\'O}'D{\\'u}nlaing" is returned
+as "odunlaing".
+.RE
+.PP
+\fIbibindex\fP examines the contents of all value strings, and expects
+them to be well-formed \*(Te\& input. In particular, braces and
+dollar signs should be balanced.
+.PP
+Errors detected result in a message giving the line number of the
+\*(Bi\& entry in which the error was detected, and the line number at
+the point of the error. Unbalanced braces or dollar signs can result
+in large differences between these line numbers; in such a case, the
+error is somewhere in the entry indicated by the first line number.
+.SH OPTIONS
+.TP \w'\-i'u+2n
+.B \-i \fIkeyword\fP
+Add \fIkeyword\fP to the list of \*(Bi\& keywords that are to be
+ignored, along with their string values, in preparing the index. By
+default, all \fIkeyword = "value"\fP pairs are indexed. Any number
+of \fB\-i\fP switches and keywords may be specified.
+.SH "SEE ALSO"
+bibclean(1), biblook(1), bibtex(1), latex(1), tex(1)
+.SH AUTHOR
+.nf
+Jeff Erickson
+Computer Science Division
+University of California
+Berkeley, CA 94720
+USA
+Email: <jeff@cs.berkeley.edu>, <jeffe@melody.berkeley.edu>
+.fi
+.PP
+This program is in the public domain. You may use it or modify it to
+your heart's content, at your own risk.
+.\" =======================[End of bibindex.man]========================
diff --git a/biblio/bibtex/utils/bibindex/bibindex.txt b/biblio/bibtex/utils/bibindex/bibindex.txt
new file mode 100644
index 0000000000..ea32c5d5db
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/bibindex.txt
@@ -0,0 +1,132 @@
+
+
+
+BIBINDEX(1) User Commands BIBINDEX(1)
+
+
+
+NAME
+ bibindex - create a bibliography index file for biblook(1)
+
+SYNOPSIS
+ bibindex basename [[-i] word] [[-i] word] ...
+
+DESCRIPTION
+ bibindex creates a compact binary index file from a BibTeX
+ bibliography file to permit fast lookup by biblook(1). The
+ basename argument can optionally omit the .bib extension.
+ The output index file will be named with the same basename,
+ and extension
+
+ For indexing purposes, a word is any contiguous set of
+ letters and numbers, after the following steps:
+
+ 1. Letters are folded to lower case. Thus, "Voronoi"
+ is returned as "voronoi"
+
+ 2. All TeX commands, except those in math expressions,
+ are removed, but their arguments are left behind.
+ Thus, "Erd{\H o}ss" is returned as "erdos".
+
+ 3. All other non-word characters are removed. Non-
+ word characters inside {{possibly} nested} braces
+ or dollar signs do not delimit words, so they may
+ cause unexpected results. Thus, "{this example}"
+ is returned as "thisexample".
+
+ 4. TeX commands in math expressions are considered
+ normal text. Thus, "$O(n\log^2 n)$" is returned as
+ "onlog2n" instead of "onn". This occasionally
+ gives unexpected or unreadable results. For exam-
+ ple, "$\bigcup_1^n[a_i,b_i]$" is returned as
+ "bigcup1naibi".
+
+ 5. Apostrophes do not delimit words. Thus, "didn't"
+ is returned as "didnt", and "{\'O}'D{\'u}nlaing" is
+ returned as "odunlaing".
+
+ bibindex examines the contents of all value strings, and
+ expects them to be well-formed TeX input. In particular,
+ braces and dollar signs should be balanced.
+
+ Errors detected result in a message giving the line number
+ of the BibTeX entry in which the error was detected, and the
+ line number at the point of the error. Unbalanced braces or
+ dollar signs can result in large differences between these
+ line numbers; in such a case, the error is somewhere in the
+ entry indicated by the first line number.
+
+
+
+
+
+Version 2.2 Last change: 03 October 1992 1
+
+
+
+
+
+
+BIBINDEX(1) User Commands BIBINDEX(1)
+
+
+
+OPTIONS
+ -i keyword
+ Add keyword to the list of BibTeX keywords that are to
+ be ignored, along with their string values, in preparing
+ the index. By default, all keyword = "value" pairs are
+ indexed. Any number of -i switches and keywords may be
+ specified.
+
+SEE ALSO
+ bibclean(1), biblook(1), bibtex(1), latex(1), tex(1)
+
+AUTHOR
+ Jeff Erickson
+ Computer Science Division
+ University of California
+ Berkeley, CA 94720
+ USA
+ Email: <jeff@cs.berkeley.edu>, <jeffe@melody.berkeley.edu>
+
+ This program is in the public domain. You may use it or
+ modify it to your heart's content, at your own risk.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Version 2.2 Last change: 03 October 1992 2
+
+
+
diff --git a/biblio/bibtex/utils/bibindex/biblook.c b/biblio/bibtex/utils/bibindex/biblook.c
new file mode 100644
index 0000000000..3f7ae6ac14
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/biblook.c
@@ -0,0 +1,1245 @@
+/* ================================================================= *\
+
+ biblook -- look up references in a bibindexed BibTeX file
+
+ This program was specifically developed for use with the
+ computational geometry bibliographic database. The database
+ can be obtained by anonymous ftp from cs.usask.ca in the file
+ `pub/geometry/geombib.tar.Z'.
+
+ Version 1.0 written by Jeff Erickson <jeff@ics.uci.edu>, 27 Mar 92
+ Version 2.0 written by Jeff Erickson <jeff@ics.uci.edu>, 17 Jun 92
+
+ This program is in the public domain. You may use it or modify
+ it to your heart's content, at your own risk. Bouquets, brickbats,
+ and bug fixes may be sent to Jeff Erickson, jeffe@cs.berkeley.edu.
+
+ %Make% gcc -O -o biblook biblook.c
+
+ Usage: biblook bibfile [savefile]
+
+ -----------------------------------------------------------------
+
+ HOW IT WORKS:
+
+ The user can enter any of the following commands:
+
+ f[ind] [not] <field> <words>
+ Find the entries containing the given words in any field
+ with a prefix matching the <field> argument. For example,
+ `a' matches both `author' and `address', and `au' matches
+ `author' only. If the <field> argument is `-' (or any
+ string with no letters or numbers), match any field.
+
+ If `not' appears before the <field>, the sense of the search
+ is reversed. The symbols `~' and `!' can be used in place
+ of `not'.
+
+ Each word is a contiguous sequence of letters and digits.
+ Case is ignored; accents should be omitted; apostrophes are
+ not required. Single characters and a few common words are
+ also ignored. Any word ending with an asterisk is treated
+ as a prefix. Thus, `point*' matches `point', `points',
+ `pointer', etc.
+
+ and [not] <field> <words>
+ or [not] <field> <words>
+ Intersect (resp. union) the results of the given search
+ with the previous search. Several of these commands may be
+ combined on a single line. Commands are handled in the order
+ in which they appear; there is no precedence. Unlike other
+ commands, and like `not', these must be spelled out
+ completely. `&' can be used in place of `and', and `|' can
+ be used in place of `or'.
+
+ d[isplay]
+ Display the results of the previous search.
+
+ s[ave] [<filename>]
+ Save the results of the previous results into the specified
+ file. If <filename> is omitted, the previous save file is
+ used. If no save file has ever been specified, results are
+ saved in the file specified on the command line. If no such
+ file is specified, `save.bib' is used. If the save file
+ exists, results are appended to it.
+
+ q[uit]/EOF
+ Quit.
+
+ Several commands can be combined on a single line by separating
+ them with semicolons. For example, the following command displays
+ all STOC papers cowritten by Erdo"s without `Voronoi diagrams' in
+ the title:
+
+ f b stoc* | b symp* theory comp* & au erdos & ~t voronoi diagrams ; d
+
+ -----------------------------------------------------------------
+ Version history
+
+ 1.0 <jge> 3/29/92 Initial version complete
+ 1.1 <jge> 4/3/92 Fixed GetToken bug.
+ Prompts and feedback messages sent to stderr
+ instead of stdout, so results can be
+ redirected to a file.
+
+ 2.0 <jge> 6/17/92 Major change in file format and commands.
+ 1. Allow searching on any field or all fields.
+ 2. More extensive boolean queries (and, or, not)
+ 3. New command to save results to a file
+ 4. New command to display results, rather than displaying
+ them automatically.
+ 5. Allow searching for prefixes
+ 6. Pipe display results through $PAGER or /usr/ucb/more
+ 2.1 <jge> 7/8/92 Minor bug fixes.
+ 2.3 Bill Jones <jones@cs.usask.ca> 93/01/29
+ 1. Declarations common to bibindex.c and biblook.c factored out
+ to new file biblook.h.
+ 2. Index type of (signed) short overflows early; created typedef
+ Index_t, defined as unsigned short.
+ 2.4 Nelson H. F. Beebe <beebe@math.utah.edu> [01-Jun-1993]
+ 1. Remove some mixed-mode arithmetic.
+ 2. Add cast to return value of fork().
+ 3. Correct use and type of numoffsets so that code works
+ if Index_t is "unsigned int" or "unsigned long".
+
+\* ================================================================= */
+
+
+#include "biblook.h"
+
+/* ======================= UTILITY FUNCTIONS ======================= */
+
+/* ----------------------------------------------------------------- *\
+| void die(const char *msg1, const char *msg2) -- print an error message and die
+\* ----------------------------------------------------------------- */
+void die(const char *msg1, const char *msg2)
+{
+ fprintf(stderr, "Error: %s %s\n", msg1, msg2);
+ exit(1);
+}
+
+/* ----------------------------------------------------------------- *\
+| void safefread(void *ptr, size_t size, size_t num, FILE *fp)
+|
+| Read from the file, but die if there's an error.
+\* ----------------------------------------------------------------- */
+void safefread(void *ptr, size_t size, size_t num, FILE *fp)
+{
+ if (fread(ptr, size, num, fp) < num)
+ die("Unexpected EOF in bix file.", "");
+}
+
+/* ----------------------------------------------------------------- *\
+| char safegetc(FILE *fp)
+|
+| Get the next character safely. Used by routines that assume that
+| they won't run into the end of file.
+\* ----------------------------------------------------------------- */
+char safegetc(FILE *fp)
+{
+ if (feof(fp))
+ die("Unexpected EOF in bib file.", "");
+ return getc(fp);
+}
+
+
+/* ========================== INDEX TABLES ========================= */
+
+typedef struct
+{
+ Word theword;
+ Index_t numindex;
+ Index_t *index;
+} Index, *IndexPtr;
+
+typedef struct
+{
+ Word thefield;
+ Index_t numwords;
+ IndexPtr words;
+} IndexTable;
+
+char numfields;
+IndexTable *fieldtable;
+
+Index_t numoffsets;
+long *offsets;
+
+/* ----------------------------------------------------------------- *\
+| void ReadWord(FILE *ifp, Word word)
+|
+| Read a "pascal" string into the given buffer
+\* ----------------------------------------------------------------- */
+void ReadWord(FILE *ifp, Word word)
+{
+ char length;
+
+ safefread((void *) &length, sizeof(char), 1, ifp);
+ if (length > MAXWORD)
+ die("Index file is corrupt", "(word too long).");
+
+ safefread((void *) word, sizeof(char), length, ifp);
+ word[length] = 0;
+}
+
+/* ----------------------------------------------------------------- *\
+| void GetOneTable(FILE *ifp, IndexTable *table)
+|
+| Get one index table from the file
+\* ----------------------------------------------------------------- */
+void GetOneTable(FILE *ifp, register IndexTable *table)
+{
+ Index_t i, num;
+
+ safefread((void *) &table->numwords, sizeof(Index_t), 1, ifp);
+ table->words = (IndexPtr) malloc(table->numwords * sizeof(Index));
+
+ for (i=0; i<table->numwords; i++)
+ {
+ ReadWord(ifp, table->words[i].theword);
+ safefread((void *) &num, sizeof(Index_t), 1, ifp);
+ table->words[i].numindex = num;
+ table->words[i].index = (Index_t *) malloc(num * sizeof(Index_t));
+ safefread((void *) table->words[i].index, sizeof(Index_t), num,
+ ifp);
+ }
+}
+
+/* ----------------------------------------------------------------- *\
+| void GetTables(char *filename)
+|
+| Get the tables from the index file.
+\* ----------------------------------------------------------------- */
+void GetTables(char *filename)
+{
+ int version, i;
+ FILE *ifp;
+
+ ifp = fopen(filename, "r");
+ if (!ifp)
+ die("Can't read", filename);
+ if (fscanf(ifp, "bibindex %d %*[^\n]%*c", &version) < 1)
+ die(filename, "is not a bibindex file!");
+ if (version != FILE_VERSION)
+ die(filename, "is the wrong version. Please rerun bibindex.");
+
+ safefread((void *) &numoffsets, sizeof(Index_t), 1, ifp);
+ offsets = (long *) malloc(numoffsets * sizeof(long));
+ safefread((void *) offsets, sizeof(long), numoffsets, ifp);
+
+ safefread((void *) &numfields, sizeof(char), 1, ifp);
+ fieldtable = (IndexTable *) malloc(numfields * sizeof(IndexTable));
+
+ for (i=0; i<numfields; i++)
+ ReadWord(ifp, fieldtable[i].thefield);
+
+ for (i=0; i<numfields; i++)
+ GetOneTable(ifp, fieldtable+i);
+
+ fclose(ifp);
+}
+
+/* ----------------------------------------------------------------- *\
+| void FreeTables(void)
+|
+| Free the index tables.
+\* ----------------------------------------------------------------- */
+void FreeTables(VOID)
+{
+ register int i;
+ register Index_t j;
+
+ for (i=0; i<numfields; i++)
+ {
+ for (j=0; j<fieldtable[i].numwords; j++)
+ free(fieldtable[i].words[j].index);
+ free(fieldtable[i].words);
+ }
+ free(fieldtable);
+ free(offsets);
+}
+
+
+/* ----------------------------------------------------------------- *\
+| Index_t FindIndex(IndexTable table, char *word, char prefix)
+|
+| Find the index of a word in a table. Return INDEX_NAN if the word isn't
+| there. If prefix is true, return the index of the first matching
+| word.
+\* ----------------------------------------------------------------- */
+Index_t FindIndex(IndexTable table, char *word, char prefix)
+{
+ register IndexPtr words = table.words;
+ register int hi, lo, mid; /* must be signed */
+ register int cmp;
+
+ hi = table.numwords-1;
+ lo = 0;
+
+ while (hi>=lo)
+ {
+ mid = (hi+lo)/2;
+ cmp = strcmp(word, words[mid].theword);
+
+ if (cmp == 0)
+ return mid;
+ else if (cmp < 0)
+ hi = mid-1;
+ else if (cmp > 0)
+ lo = mid+1;
+ }
+
+ if (prefix && !strncmp(word, words[lo].theword, strlen(word)))
+ return lo;
+ else
+ return INDEX_NAN;
+}
+
+
+/* =================== SET MANIPULATION ROUTINES =================== */
+
+#define SETSCALE (sizeof(unsigned long)*8)
+
+static Index_t setsize;
+static unsigned long setmask; /* used to erase extra bits */
+typedef unsigned long *Set;
+
+/* ----------------------------------------------------------------- *\
+| Set NewSet(void)
+|
+| Get a new variable to hold sets of integers in the range
+| [0, numoffsets]. Set setsize and setmask.
+\* ----------------------------------------------------------------- */
+Set NewSet(VOID)
+{
+ setsize = (numoffsets + SETSCALE - 1)/SETSCALE; /* HACK */
+ setmask = (1<<(numoffsets%SETSCALE)) - 1; /* KLUDGE */
+
+ return (Set) malloc(setsize * SETSCALE);
+}
+
+/* ----------------------------------------------------------------- *\
+| void EmptySet(Set theset)
+|
+| Empty the set.
+\* ----------------------------------------------------------------- */
+void EmptySet(Set theset)
+{
+ register Index_t i;
+ for (i=0; i<setsize; i++)
+ theset[i] = 0L;
+}
+
+/* ----------------------------------------------------------------- *\
+| void SetUnion(Set src1, Set src2, Set result)
+|
+| Get the union of two sets
+\* ----------------------------------------------------------------- */
+void SetUnion(Set src1, Set src2, Set result)
+{
+ register Index_t i;
+ for (i=0; i<setsize; i++)
+ result[i] = src1[i] | src2[i];
+}
+
+/* ----------------------------------------------------------------- *\
+| void SetInsersection(Set src1, Set src2, Set result)
+|
+| Get the intersection of two sets
+\* ----------------------------------------------------------------- */
+void SetIntersection(Set src1, Set src2, Set result)
+{
+ register Index_t i;
+ for (i=0; i<setsize; i++)
+ result[i] = src1[i] & src2[i];
+}
+
+/* ----------------------------------------------------------------- *\
+| void SetComplement(Set src, Set result)
+|
+| Get the complement of a set
+\* ----------------------------------------------------------------- */
+void SetComplement(Set src, Set result)
+{
+ register Index_t i;
+ for (i=0; i<setsize; i++)
+ result[i] = ~src[i];
+ result[setsize-1] &= setmask; /* clear those last few bits */
+}
+
+/* ----------------------------------------------------------------- *\
+| void CopySet(Set src, Set result)
+|
+| Copy one set into another
+\* ----------------------------------------------------------------- */
+void CopySet(Set src, Set result)
+{
+ register Index_t i;
+ for (i=0; i<setsize; i++)
+ result[i] = src[i];
+}
+
+
+/* ----------------------------------------------------------------- *\
+| int CountSet(Set theset)
+|
+| Get the cardinality of the set
+\* ----------------------------------------------------------------- */
+int CountSet(Set theset)
+{
+ register unsigned i, j, count;
+
+ count = 0;
+ for (i=0; i<(unsigned)setsize; i++)
+ for (j=0; j<(unsigned)SETSCALE; j++)
+ if (theset[i] & (1<<j))
+ count++;
+
+ return count;
+}
+
+/* ----------------------------------------------------------------- *\
+| void BuildSet(Set theset, Index_t *thelist, Index_t length)
+|
+| Build a set out of a list of integers
+\* ----------------------------------------------------------------- */
+void BuildSet(Set theset, Index_t *thelist, Index_t length)
+{
+ register unsigned i;
+
+ EmptySet(theset);
+ for (i=0; i<(unsigned)length; i++)
+ theset[thelist[i]/SETSCALE] |= 1 << (thelist[i] % SETSCALE);
+}
+
+/* ----------------------------------------------------------------- *\
+| void DoForSet(Set theset, void (*action)(int, void *), void *arg)
+|
+| Do something to every element in a set
+\* ----------------------------------------------------------------- */
+void DoForSet(Set theset, void (*action)(int, void *), void *arg)
+{
+ register unsigned i, j;
+
+ for (i=0; i<(unsigned)setsize; i++)
+ for (j=0; j<(unsigned)SETSCALE; j++)
+ if (theset[i] & (1<<j))
+ (*action)(SETSCALE*i + j, arg);
+}
+
+
+/* ======================== SEARCH ROUTINES ======================== */
+
+Set results, oldresults, oneword, onefield;
+short firstfield, lastfield; /* indices into fieldtable */
+
+/* ----------------------------------------------------------------- *\
+| void InitSearch(void)
+|
+| Initialize the search lists
+\* ----------------------------------------------------------------- */
+void InitSearch(VOID)
+{
+ results = NewSet();
+ oldresults = NewSet();
+ oneword = NewSet();
+ onefield = NewSet();
+ firstfield = lastfield = -1;
+}
+
+/* ----------------------------------------------------------------- *\
+| void FreeSearch(void)
+|
+| Free the search list
+\* ----------------------------------------------------------------- */
+void FreeSearch(VOID)
+{
+ free(results);
+ free(oldresults);
+ free(oneword);
+ free(onefield);
+}
+
+/* ----------------------------------------------------------------- *\
+| void ClearResults(void)
+|
+| Clear the current and old results
+\* ----------------------------------------------------------------- */
+void ClearResults(VOID)
+{
+ EmptySet(results);
+ SetComplement(results, results);
+ CopySet(results, oldresults);
+}
+
+/* ----------------------------------------------------------------- *\
+| void SaveResults(void)
+|
+| Save and clear the current results
+\* ----------------------------------------------------------------- */
+void SaveResults(VOID)
+{
+ CopySet(results, oldresults);
+ EmptySet(results);
+ SetComplement(results, results);
+}
+
+/* ----------------------------------------------------------------- *\
+| void CombineResults(char invert, char intersect)
+|
+| Combine current results with old results
+\* ----------------------------------------------------------------- */
+void CombineResults(char invert, char intersect)
+{
+ if (invert)
+ SetComplement(results, results);
+ if (intersect)
+ SetIntersection(results, oldresults, results);
+ else
+ SetUnion(results, oldresults, results);
+}
+
+
+/* ----------------------------------------------------------------- *\
+| char SetUpField(char *field)
+|
+| Set up the search fields. Return the number of searchable fields.
+\* ----------------------------------------------------------------- */
+char SetUpField(char *field)
+{
+ int i, len;
+
+ firstfield = -1;
+ len = strlen(field);
+
+ for (i=0; i<numfields; i++)
+ {
+ if (!strncmp(field, fieldtable[i].thefield, len))
+ {
+ if (firstfield == -1)
+ firstfield = i;
+ lastfield = i;
+ }
+ }
+
+ if (firstfield == -1)
+ {
+ printf("\tNo searchable fields matching \"%s\".\n", field);
+ return 0;
+ }
+ else
+ return lastfield - firstfield + 1;
+
+}
+
+const char *badwords[] = BADWORDS;
+
+/* ----------------------------------------------------------------- *\
+| void FindWord(char *word, char prefix)
+|
+| Find a word in the currently active field and update `results'.
+| If the prefix flag is set, find all words having the given prefix.
+\* ----------------------------------------------------------------- */
+void FindWord(register char *word, char prefix)
+{
+ register IndexPtr words;
+ Index_t win, len;
+ int i;
+
+ if (!prefix)
+ {
+ if (!word[0])
+ {
+ printf("\t[ignoring empty string]\n");
+ return;
+ }
+ if (!word[1])
+ {
+ printf("\t[ignoring \"%s\"]\n", word);
+ return;
+ }
+ for (i=0; badwords[i]; i++)
+ {
+ if (!strcmp(badwords[i], word))
+ {
+ printf("\t[ignoring \"%s\"]\n", word);
+ return;
+ }
+ }
+ }
+
+ EmptySet(oneword);
+ len = strlen(word);
+
+ for (i=firstfield; i<=lastfield; i++)
+ {
+ words = fieldtable[i].words;
+ win = FindIndex(fieldtable[i], word, prefix);
+
+ if (win != INDEX_NAN)
+ {
+ if (prefix)
+ {
+ while ((win < fieldtable[i].numwords) &&
+ !strncmp(words[win].theword, word, len))
+ {
+ BuildSet(onefield, words[win].index,
+ words[win].numindex);
+ SetUnion(oneword, onefield, oneword);
+ win++;
+ }
+ }
+ else
+ {
+ BuildSet(onefield, words[win].index,
+ words[win].numindex);
+ SetUnion(oneword, onefield, oneword);
+ }
+ }
+ }
+
+ SetIntersection(oneword, results, results);
+}
+
+
+/* ============================= OUTPUT ============================ */
+FILE *bibfp;
+
+/* ----------------------------------------------------------------- *\
+| void ReportResults(void)
+|
+| Report the results of the previous search.
+\* ----------------------------------------------------------------- */
+void ReportResults(VOID)
+{
+ int numresults;
+
+ numresults = CountSet(results);
+
+ if (numresults == 0)
+ printf("\tNo matches found.\n");
+ else if (numresults == 1)
+ printf("\t1 match found.\n");
+ else
+ printf("\t%d matches found.\n", numresults);
+}
+
+/* ----------------------------------------------------------------- *\
+| void PrintEntry(int entry, FILE *ofp)
+|
+| Print the entry.
+\* ----------------------------------------------------------------- */
+void PrintEntry(int entry, FILE *ofp)
+{
+ char ch;
+ char braces;
+ char quotes;
+
+ if (entry >= (int)numoffsets) /* extra bits might be set */
+ return;
+
+ putc('\n', ofp);
+ if (fseek(bibfp, offsets[entry], 0))
+ die("Index file is corrupt.", "");
+
+ ch = safegetc(bibfp);
+
+ while (ch != '@')
+ {
+ putc(ch, ofp);
+ ch = safegetc(bibfp);
+ }
+ while ((ch != '{') && (ch != '('))
+ {
+ putc(ch, ofp);
+ ch = safegetc(bibfp);
+ }
+
+ braces = quotes = 0;
+
+ putc(ch, ofp);
+ ch = safegetc(bibfp);
+ while (braces || quotes || ((ch != '}') && (ch != ')')))
+ {
+ if (ch == '{')
+ braces++;
+ else if (ch == '}')
+ braces--;
+ else if ((ch == '"') && !braces)
+ quotes = !quotes;
+ putc(ch, ofp);
+ ch = safegetc(bibfp);
+ }
+
+ putc(ch, ofp);
+ putc('\n', ofp);
+}
+
+/* ----------------------------------------------------------------- *\
+| void PrintResults(char *filename)
+|
+| Print the current search results into the given file. If the
+| filename is NULL, pipe the output through $PAGER.
+\* ----------------------------------------------------------------- */
+void PrintResults(char *filename)
+{
+ int numresults;
+ FILE *ofp;
+ char *pager;
+ char *the_tmpfile = (char*)NULL;
+ int childpid;
+
+ numresults = CountSet(results);
+ if (numresults == 0)
+ printf("\tNothing to display!\n");
+ else if (numresults == numoffsets)
+ printf("\tI can't display the entire bibliography!\n");
+ else
+ {
+ if (filename)
+ {
+ ofp = fopen(filename, "a");
+ if (!ofp)
+ {
+ printf("\tCan't write to \"%s\"!\n", filename);
+ return;
+ }
+ }
+ else
+ {
+ the_tmpfile = (char*)tempnam(NULL, "bibl.");
+ ofp = fopen(the_tmpfile, "w");
+ if (!ofp)
+ {
+ printf("\tCan't write to \"%s\"!\n", the_tmpfile);
+ return;
+ }
+ }
+
+ if (filename)
+ {
+ time_t now = time(0);
+ fprintf(ofp, "%% Retrieved by biblook %d.%d at %s",
+ MAJOR_VERSION, MINOR_VERSION, ctime(&now));
+ }
+
+ DoForSet(results, (void (*)(int, void *)) PrintEntry,
+ (void *) ofp);
+
+ fclose(ofp);
+ if (filename)
+ printf("\tResults saved in \"%s\"\n", filename);
+ else
+ {
+ pager = (char*)getenv("PAGER");
+
+ if ((childpid = (int)fork()) != 0)
+ waitpid(childpid, (int *) 0, 0);
+ else if (pager)
+ {
+ execlp(pager, pager, the_tmpfile, (char *) 0);
+ perror(pager); /* should never get here! */
+ exit(0);
+ }
+ else
+ {
+ execl("/usr/ucb/more", "more", the_tmpfile, (char *) 0);
+ perror("/usr/ucb/more");
+ exit(0);
+ }
+
+ unlink(the_tmpfile);
+ free(the_tmpfile); /* malloc'ed by tempnam() */
+ putchar('\n');
+ }
+ }
+}
+
+
+/* ======================== USER INTERFACE ========================= */
+
+typedef enum {
+ T_Find, T_Display, T_Save, T_Quit, T_Word,
+ T_And, T_Or, T_Not, T_Semi, T_Return, T_Help
+} Token;
+
+/* ----------------------------------------------------------------- *\
+| Token GetToken(char *tokenstr)
+|
+| Get the next input token.
+\* ----------------------------------------------------------------- */
+Token GetToken(char *tokenstr)
+{
+ static char line[256];
+ static short pos;
+ static char neednew = 1;
+ short tlen = 0;
+
+ *tokenstr = 0;
+
+ if (neednew)
+ {
+ printf("biblook: ");
+ if (!fgets(line, 254, stdin))
+ return T_Quit;
+
+ pos = 0;
+ neednew = 0;
+ }
+
+ while ((line[pos] == ' ') || (line[pos] == '\t'))
+ pos++;
+
+ switch (line[pos])
+ {
+ case '\n':
+ pos++;
+ neednew = 1;
+ return T_Return;
+
+ case '&':
+ pos++;
+ return T_And;
+
+ case '|':
+ pos++;
+ return T_Or;
+
+ case '~':
+ case '!':
+ pos++;
+ return T_Not;
+
+ case ';':
+ pos++;
+ return T_Semi;
+
+ default:
+ tokenstr[tlen++] = tolower(line[pos++]);
+ while (!isspace(line[pos]) && (line[pos] != ';') &&
+ (line[pos] != '&') && (line[pos] != '|'))
+ {
+ tokenstr[tlen++] = tolower(line[pos++]);
+ }
+ tokenstr[tlen] = 0;
+
+ /* I really ought to use a hash table here. */
+
+ if (!strncmp(tokenstr, "find", tlen))
+ return T_Find;
+ else if (!strncmp(tokenstr, "display", tlen))
+ return T_Display;
+ else if (!strncmp(tokenstr, "help", tlen))
+ return T_Help;
+ else if (!strncmp(tokenstr, "?", tlen))
+ return T_Help;
+ else if (!strncmp(tokenstr, "save", tlen))
+ return T_Save;
+ else if (!strncmp(tokenstr, "quit", tlen))
+ return T_Quit;
+ else if (!strcmp(tokenstr, "and"))
+ return T_And;
+ else if (!strcmp(tokenstr, "or"))
+ return T_Or;
+ else if (!strcmp(tokenstr, "not"))
+ return T_Not;
+ else
+ return T_Word;
+ }
+}
+
+
+/* ----------------------------------------------------------------- *\
+| char Strip(char *string)
+|
+| Strip all but alphanumeric characters out of the string. Return
+| true if the original string ended with the prefix character '*'.
+\* ----------------------------------------------------------------- */
+char Strip(char *string)
+{
+ char prefix = 0;
+ char *src = string;
+
+ while (*src)
+ {
+ prefix = (*src == '*');
+ if (isalnum(*src))
+ *string++ = *src;
+ src++;
+ }
+ *string = 0;
+ return prefix;
+}
+
+/* ----------------------------------------------------------------- *\
+| void CmdError(void)
+|
+| Print syntax error message
+\* ----------------------------------------------------------------- */
+void CmdError(VOID)
+{
+ printf("?? Syntax error ??\n");
+}
+
+static const char* helplines[] =
+{
+ "biblook permits rapid lookup in a BibTeX bibliography data",
+ "base, using a compact binary index file prepared by bibindex(1).",
+ "",
+ "Available commands:",
+ "? or h[elp]",
+ " Display this help message.",
+ "",
+ "f[ind] [not] <field> <words>",
+ " Find the entries containing the given words in any",
+ " field with a prefix matching the <field> argument. For",
+ " example, `a' matches both `author' and `address', and",
+ " `au' matches `author' only. If the <field> argument is",
+ " `-' (or any string with no letters or numbers), match",
+ " any field.",
+ "",
+ " If `not' appears before the <field>, the sense of the",
+ " search is reversed. The symbols `~' and `!' can be",
+ " used in place of `not'.",
+ "",
+ " Each word is a contiguous sequence of letters and",
+ " digits. Case is ignored; accents should be omitted;",
+ " apostrophes are not required. Single characters and a",
+ " few common words are also ignored. Any word ending",
+ " with an asterisk is treated as a prefix. Thus,",
+ " `point*' matches `point', `points', `pointer', etc.",
+ "",
+ "and [not] <field> <words>",
+ "or [not] <field> <words>",
+ " Intersect (resp. union) the results of the given search",
+ " with the previous search. Several of these commands",
+ " may be combined on a single line. Commands are handled",
+ " in the order in which they appear; there is no pre-",
+ " cedence. Unlike other commands, and like `not', these",
+ " must be spelled out completely. `&' can be used in",
+ " place of `and', and `|' can be used in place of `or'.",
+ "",
+ "d[isplay]",
+ " Display the results of the previous search.",
+ "",
+ "s[ave] [<filename>]",
+ " Save the results of the previous results into the",
+ " specified file. If <filename> is omitted, the previous",
+ " save file is used. If no save file has ever been",
+ " specified, results are saved in the file specified on",
+ " the command line. If no such file is specified,",
+ " `save.bib' is used. If the save file exists, results",
+ " are appended to it.",
+ "",
+ "q[uit]/EOF",
+ " Quit.",
+ "",
+ "Several commands can be combined on a single line by",
+ "separating them with semicolons. For example, the following",
+ "command displays all STOC papers cowritten by Erdo\"s",
+ "without `Voronoi diagrams' in the title:",
+ "",
+ "f b stoc* | b symp* theory comp* & au erdos & ~t voronoi diagrams ; d",
+ "",
+ (const char*)NULL,
+};
+
+/* ----------------------------------------------------------------- *\
+| void GiveHelp(void)
+|
+| Print a help message. Lines are stored as separate strings to
+| avoid hitting compiler limits.
+\* ----------------------------------------------------------------- */
+
+void GiveHelp(VOID)
+{
+ int k;
+
+ for (k = 0; helplines[k]; ++k)
+ printf("\t%s\n",helplines[k]);
+}
+
+/* ----------------------------------------------------------------- *\
+| States for Lookup()
+\* ----------------------------------------------------------------- */
+typedef enum {
+ Wait, Find, FindN, FindF, FindW,
+ Display, Save, SaveF, Error
+} CmdState;
+
+/* ----------------------------------------------------------------- *\
+| void Lookup(const char *defsave)
+|
+| Execute commands until the user quits. Defsave is the default
+| save file name. This is one big finite state machine. It's long
+| and boring, but that's interface code for ya!
+\* ----------------------------------------------------------------- */
+void Lookup(const char *defsave)
+{
+ char tokenstr[256];
+ char savefile[256];
+ CmdState state = Wait;
+ Token thetoken;
+ char intersect = 1; /* 1 = intersect, 0 = union */
+ char invert = 0; /* 1 = invert */
+ char prefix; /* 1 = word is really a prefix */
+
+ ClearResults();
+ strcpy(savefile, defsave);
+
+ while (1)
+ {
+ thetoken = GetToken(tokenstr);
+
+ if ((thetoken == T_Quit) && !tokenstr[0])
+ return;
+ else if (thetoken == T_Help)
+ {
+ GiveHelp();
+ continue;
+ }
+
+ switch (state)
+ {
+ case Wait:
+ switch (thetoken)
+ {
+ case T_Quit:
+ return;
+ case T_Find:
+ state = Find;
+ ClearResults();
+ break;
+ case T_And:
+ state = Find;
+ SaveResults();
+ break;
+ case T_Or:
+ state = Find;
+ intersect = 0;
+ SaveResults();
+ break;
+ case T_Display:
+ state = Display;
+ break;
+ case T_Save:
+ state = Save;
+ break;
+ case T_Return:
+ case T_Semi:
+ break;
+ default:
+ state = Error; CmdError(); break;
+ }
+ break;
+
+ case Find:
+ if (thetoken == T_Not)
+ {
+ state = FindN;
+ invert = 1;
+ }
+ else
+ {
+ if (tokenstr[0])
+ {
+ state = FindF;
+ Strip(tokenstr);
+ if (!SetUpField(tokenstr))
+ state = Error;
+ }
+ else
+ {
+ state = (thetoken == T_Return) ? Wait : Error;
+ CmdError();
+ }
+ }
+ break;
+
+ case FindN:
+ if (tokenstr[0])
+ {
+ state = FindF;
+ Strip(tokenstr);
+ if (!SetUpField(tokenstr))
+ state = Error;
+ }
+ else
+ {
+ state = (thetoken == T_Return) ? Wait : Error;
+ CmdError();
+ }
+ break;
+
+ case FindF:
+ if (tokenstr[0])
+ {
+ state = FindW;
+ prefix = Strip(tokenstr);
+ FindWord(tokenstr, prefix);
+ }
+ else
+ {
+ state = (thetoken == T_Return) ? Wait : Error;
+ CmdError();
+ }
+ break;
+
+ case FindW:
+ switch (thetoken)
+ {
+ case T_And:
+ state = Find;
+ CombineResults(invert, intersect);
+ SaveResults();
+ invert = 0;
+ intersect = 1;
+ break;
+ case T_Or:
+ state = Find;
+ CombineResults(invert, intersect);
+ SaveResults();
+ invert = 0;
+ intersect = 0;
+ break;
+ case T_Semi:
+ state = Wait;
+ CombineResults(invert, intersect);
+ invert = 0;
+ intersect = 1;
+ break;
+ case T_Return:
+ state = Wait;
+ CombineResults(invert, intersect);
+ ReportResults();
+ invert = 0;
+ intersect = 1;
+ break;
+ default:
+ if (tokenstr[0])
+ {
+ state = FindW;
+ prefix = Strip(tokenstr);
+ FindWord(tokenstr, prefix);
+ }
+ else
+ {
+ state = Error;
+ CmdError();
+ }
+ break;
+ }
+ break;
+
+ case Display:
+ if ((thetoken == T_Semi) || (thetoken == T_Return))
+ {
+ state = Wait;
+ PrintResults(NULL);
+ }
+ else
+ {
+ state = Error;
+ CmdError();
+ }
+ break;
+
+ case Save:
+ if (tokenstr[0])
+ {
+ state = SaveF;
+ strcpy(savefile, tokenstr);
+ }
+ else if ((thetoken == T_Semi) || (thetoken == T_Return))
+ {
+ state = Wait;
+ PrintResults(savefile);
+ }
+ else
+ {
+ state = Error;
+ CmdError();
+ }
+ break;
+
+ case SaveF:
+ if ((thetoken == T_Semi) || (thetoken == T_Return))
+ {
+ state = Wait;
+ PrintResults(savefile);
+ }
+ else
+ {
+ state = Error;
+ CmdError();
+ }
+ break;
+
+ case Error:
+ switch (thetoken)
+ {
+ case T_Quit:
+ return;
+ case T_Return:
+ state = Wait;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+
+/* ================================================================= *\
+| The main program
+\* ================================================================= */
+int main(int argc, char **argv)
+{
+ char bibfile[256];
+ char bixfile[256];
+ struct stat bibstat, bixstat;
+ char *p;
+
+ printf("biblook version %d.%d file version %d\n",
+ (int)MAJOR_VERSION, (int)MINOR_VERSION, (int)FILE_VERSION);
+ printf("Type ? or h for help\n");
+
+ if ((argc != 2) && (argc != 3))
+ {
+ fprintf(stderr, "Usage: biblook bib [savefile]\n");
+ exit(1);
+ }
+
+ if (((p = strrchr(argv[1],'.')) != (char*)NULL) &&
+ (strcmp(p, ".bib") == 0))
+ *p = '\0'; /* remove any .bib extension */
+
+ sprintf(bibfile, "%s.bib", argv[1]);
+ sprintf(bixfile, "%s.bix", argv[1]);
+
+ stat(bibfile, &bibstat);
+ stat(bixfile, &bixstat);
+ if (bibstat.st_mtime > bixstat.st_mtime)
+ die(bixfile, "is out of date. Please rerun bibindex.");
+
+ bibfp = fopen(bibfile, "r");
+ if (!bibfp)
+ die("Can't read", bibfile);
+
+ GetTables(bixfile);
+ InitSearch();
+
+ if (argc == 3)
+ Lookup(argv[2]);
+ else
+ Lookup("save.bib");
+
+ FreeSearch();
+ FreeTables();
+
+ fclose(bibfp);
+ exit(0);
+ return(0);
+}
diff --git a/biblio/bibtex/utils/bibindex/biblook.h b/biblio/bibtex/utils/bibindex/biblook.h
new file mode 100644
index 0000000000..b5709099a1
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/biblook.h
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#if (__STDC__ || __cplusplus || c_plusplus)
+#include <stdlib.h>
+#endif /* (__STDC__ || __cplusplus || c_plusplus) */
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#if !__NeXT__
+#include <unistd.h>
+#include <malloc.h>
+#endif /* !__NeXT__ */
+
+#if (__STDC__ || __cplusplus || c_plusplus)
+#define VOID void
+#else /* NOT (__STDC__ || __cplusplus || c_plusplus) */
+#define VOID
+#endif /* (__STDC__ || __cplusplus || c_plusplus) */
+
+#undef bcopy
+#define bcopy(source,target,length) \
+ memcpy((char*)(target),(const char*)(source),length)
+
+#if sun
+#if __cplusplus
+extern "C" int _filbuf(FILE *); /* missing from stdio.h */
+#if !__GNUC__
+int _flsbuf(unsigned char,FILE *); /* missing from stdio.h */
+#endif /* !__GNUC__ */
+extern "C" int _flsbuf(unsigned int, FILE *); /* missing from stdio.h */
+extern "C" char *tempnam(const char *, const char *);
+ /* not defined by acc's stdio.h */
+extern "C" int waitpid(int, int *, int); /* not defined by any Sun .h file */
+#else /* NOT __cplusplus */
+int _filbuf(FILE *); /* missing from stdio.h */
+#if !__GNUC__
+int _flsbuf(unsigned char,FILE *); /* missing from stdio.h */
+#endif /* !__GNUC__ */
+char *tempnam(const char *, const char *); /* not defined by acc's stdio.h */
+int waitpid(int, int *, int); /* not defined by any Sun .h file */
+#endif /* __cplusplus */
+#endif /* sun */
+
+#if DEBUG_MALLOC
+ /* For dynamic memory debugging. */
+ /* Note that the parens around free and malloc */
+ /* are essential to prevent macro expansion in */
+ /* ANSI/ISO Standard C and C++. Erroneous */
+ /* preprocessors will go into an infinite loop */
+ /* (e.g. SunOS /usr/CC/sun4/cpp) */
+#if sun
+int malloc_debug(int level);
+int malloc_verify(void);
+#else /* NOT sun */
+#define malloc_debug(level) level
+#define malloc_verify() 1
+#endif /* sun */
+#undef free
+#undef malloc
+#define free(ptr) (malloc_verify(), \
+ fprintf(stderr,"line %d: free(%p)\n",(int)__LINE__,(ptr)), \
+ (free)(ptr))
+static void *p__; /* for malloc() debugging */
+#define malloc(s) (malloc_verify(),\
+ p__ = (malloc)(s),\
+ fprintf(stderr,"line %d: malloc(%ld) -> %p\n",\
+ (int)__LINE__,(s),(p__)),\
+ p__)
+#endif /* DEBUG_MALLOC */
+
+#if __NeXT__
+static char* p__;
+static char* q__;
+/* NB: This is not a general definition of tempnam(), but works for
+this program! */
+#define tempnam(dir,pfx) (p__ = tmpnam((char*)NULL), \
+ q__ = malloc(strlen(p__)+1), \
+ strcpy(q__,p__))
+#include <libc.h> /* for struct rusage definition */
+#define waitpid(pid, statusp, options) wait4((pid), (statusp), (options),\
+ (struct rusage*)0)
+#endif /* __NeXT__ */
+
+#if ardent
+/* Stardent has only simple wait-for-all-children function, sigh... */
+#define waitpid(pid, statusp, options) wait((int*)0)
+char *getenv(const char *name); /* missing from system header files */
+#endif
+
+#if __hppa
+#include <sys/wait.h>
+#endif /* __hppa */
+
+#if __sgi
+#include <sys/wait.h>
+#endif /* __sgi */
+
+#define FILE_VERSION 2 /* file format version */
+#define MAJOR_VERSION 2 /* program version */
+#define MINOR_VERSION 4
+
+#define MAXWORD 31 /* maximum length of word indexed */
+typedef char Word[MAXWORD+1];
+
+typedef unsigned short Index_t;
+#define INDEX_NAN 65535 /* maximum value representable as Index_t */
+
+#define BADWORDS { /* words not to index */ \
+ "an", "and", "for", "in", "of", "on", "the", "to", "with", 0 \
+}
+
+#define NONABBREV ",\n\t \"#%'()={}" /* See LaTeX book, p.143 */
diff --git a/biblio/bibtex/utils/bibindex/biblook.man b/biblio/bibtex/utils/bibindex/biblook.man
new file mode 100644
index 0000000000..d39c874791
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/biblook.man
@@ -0,0 +1,122 @@
+.\" ====================================================================
+.\" @Troff-man-file{
+.\" author = "Nelson H. F. Beebe",
+.\" version = "1.00",
+.\" date = "30 September 1992",
+.\" time = "16:48:22 MDT",
+.\" filename = "biblook.man",
+.\" address = "Center for Scientific Computing
+.\" Department of Mathematics
+.\" University of Utah
+.\" Salt Lake City, UT 84112
+.\" USA
+.\" Tel: +1 801 581 5254
+.\" FAX: +1 801 581 4148",
+.\" checksum = "20001 122 646 4560",
+.\" email = "beebe@math.utah.edu (Internet)",
+.\" codetable = "ISO/ASCII",
+.\" keywords = "bibliography, BibTeX",
+.\" supported = "yes",
+.\" docstring = "This file is the UNIX nroff/troff manual page
+.\" documentation for biblook, a BibTeX bibliography
+.\" lookup program.
+.\"
+.\" The checksum field above contains a CRC-16
+.\" checksum as the first value, followed by the
+.\" equivalent of the standard UNIX wc (word
+.\" count) utility output of lines, words, and
+.\" characters. This is produced by Robert
+.\" Solovay's checksum utility.",
+.\" }
+.\" ====================================================================
+.if t .ds Bi B\s-2IB\s+2T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Bi BibTeX
+.if t .ds Te T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Te TeX
+.TH BIBLOOK 1 "30 September 1992" "Version 2.2"
+.SH NAME
+biblook \- lookup entries in a bibliography file
+.SH SYNOPSIS
+.B "biblook \fIbasename\fP [\fIsavefile\fP]
+.SH DESCRIPTION
+.I biblook
+permits rapid lookup in a \*(Bi\& bibliography data base, using a
+compact binary index file prepared by \fIbibindex\fP(1).
+.PP
+At the prompt, the user can enter any of the following commands:
+.PP
+.TP
+.B ? or h[elp]
+Display a help message.
+.TP
+.B "f[ind] [not] <field> <words>"
+Find the entries containing the given words in any field
+with a prefix matching the <field> argument. For example,
+`a' matches both `author' and `address', and `au' matches
+`author' only. If the <field> argument is `-' (or any
+string with no letters or numbers), match any field.
+.IP
+If `not' appears before the <field>, the sense of the search
+is reversed. The symbols `~' and `!' can be used in place
+of `not'.
+.IP
+Each word is a contiguous sequence of letters and digits.
+Case is ignored; accents should be omitted; apostrophes are
+not required. Single characters and a few common words are
+also ignored. Any word ending with an asterisk is treated
+as a prefix. Thus, `point*' matches `point', `points',
+`pointer', etc.
+.PP
+.TP
+.BR "and [not] <field> <words>"
+.TP
+.BR "or [not] <field> <words>"
+Intersect (resp. union) the results of the given search
+with the previous search. Several of these commands may be
+combined on a single line. Commands are handled in the order
+in which they appear; there is no precedence. Unlike other
+commands, and like `not', these must be spelled out
+completely. `&' can be used in place of `and', and `|' can
+be used in place of `or'.
+.PP
+.TP
+.B "d[isplay]"
+Display the results of the previous search.
+.PP
+.TP
+.B "s[ave] [<filename>]"
+Save the results of the previous results into the specified
+file. If <filename> is omitted, the previous save file is
+used. If no save file has ever been specified, results are
+saved in the file specified on the command line. If no such
+file is specified, `save.bib' is used. If the save file
+exists, results are appended to it.
+.PP
+.TP
+.B "q[uit]/EOF"
+Quit.
+.PP
+Several commands can be combined on a single line by separating
+them with semicolons. For example, the following command displays
+all STOC papers cowritten by Erdo"s without `Voronoi diagrams' in
+the title:
+.PP
+.nf
+f b stoc* | b symp* theory comp* & au erdos & ~t voronoi diagrams ; d
+.fi
+.PP
+.SH "SEE ALSO"
+bibclean(1), bibindex(1), bibtex(1), latex(1), tex(1)
+.SH AUTHOR
+.nf
+Jeff Erickson
+Computer Science Division
+University of California
+Berkeley, CA 94720
+USA
+Email: <jeff@cs.berkeley.edu>, <jeffe@melody.berkeley.edu>
+.fi
+.PP
+This program is in the public domain. You may use it or modify it to
+your heart's content, at your own risk.
+.\" ========================[End of biblook.man]========================
diff --git a/biblio/bibtex/utils/bibindex/biblook.txt b/biblio/bibtex/utils/bibindex/biblook.txt
new file mode 100644
index 0000000000..2586b0feb4
--- /dev/null
+++ b/biblio/bibtex/utils/bibindex/biblook.txt
@@ -0,0 +1,132 @@
+
+
+
+BIBLOOK(1) User Commands BIBLOOK(1)
+
+
+
+NAME
+ biblook - lookup entries in a bibliography file
+
+SYNOPSIS
+ biblook basename [savefile]
+
+DESCRIPTION
+ biblook permits rapid lookup in a BibTeX bibliography data
+ base, using a compact binary index file prepared by bibin-
+ dex(1).
+
+ At the prompt, the user can enter any of the following com-
+ mands:
+
+ ? or h[elp]
+ Display a help message.
+
+ f[ind] [not] <field> <words>
+ Find the entries containing the given words in any
+ field with a prefix matching the <field> argument. For
+ example, `a' matches both `author' and `address', and
+ `au' matches `author' only. If the <field> argument is
+ `-' (or any string with no letters or numbers), match
+ any field.
+
+ If `not' appears before the <field>, the sense of the
+ search is reversed. The symbols `~' and `!' can be
+ used in place of `not'.
+
+ Each word is a contiguous sequence of letters and
+ digits. Case is ignored; accents should be omitted;
+ apostrophes are not required. Single characters and a
+ few common words are also ignored. Any word ending
+ with an asterisk is treated as a prefix. Thus,
+ `point*' matches `point', `points', `pointer', etc.
+
+ and [not] <field> <words>
+
+ or [not] <field> <words>
+ Intersect (resp. union) the results of the given search
+ with the previous search. Several of these commands
+ may be combined on a single line. Commands are handled
+ in the order in which they appear; there is no pre-
+ cedence. Unlike other commands, and like `not', these
+ must be spelled out completely. `&' can be used in
+ place of `and', and `|' can be used in place of `or'.
+
+ d[isplay]
+ Display the results of the previous search.
+
+ s[ave] [<filename>]
+ Save the results of the previous results into the
+
+
+
+Version 2.2 Last change: 30 September 1992 1
+
+
+
+
+
+
+BIBLOOK(1) User Commands BIBLOOK(1)
+
+
+
+ specified file. If <filename> is omitted, the previous
+ save file is used. If no save file has ever been
+ specified, results are saved in the file specified on
+ the command line. If no such file is specified,
+ `save.bib' is used. If the save file exists, results
+ are appended to it.
+
+ q[uit]/EOF
+ Quit.
+
+ Several commands can be combined on a single line by
+ separating them with semicolons. For example, the following
+ command displays all STOC papers cowritten by Erdo"s without
+ `Voronoi diagrams' in the title:
+
+ f b stoc* | b symp* theory comp* & au erdos & ~t voronoi diagrams ; d
+
+SEE ALSO
+ bibclean(1), bibindex(1), bibtex(1), latex(1), tex(1)
+
+AUTHOR
+ Jeff Erickson
+ Computer Science Division
+ University of California
+ Berkeley, CA 94720
+ USA
+ Email: <jeff@cs.berkeley.edu>, <jeffe@melody.berkeley.edu>
+
+ This program is in the public domain. You may use it or
+ modify it to your heart's content, at your own risk.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Version 2.2 Last change: 30 September 1992 2
+
+
+