diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /biblio/bibtex/utils/bibindex |
Initial commit
Diffstat (limited to 'biblio/bibtex/utils/bibindex')
-rw-r--r-- | biblio/bibtex/utils/bibindex/Makefile | 189 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/README | 47 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/bibindex.c | 1062 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/bibindex.man | 112 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/bibindex.txt | 132 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/biblook.c | 1245 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/biblook.h | 113 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/biblook.man | 122 | ||||
-rw-r--r-- | biblio/bibtex/utils/bibindex/biblook.txt | 132 |
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 + + + |