summaryrefslogtreecommitdiff
path: root/biblio/bibtex/utils/bibindex/biblook.c
diff options
context:
space:
mode:
Diffstat (limited to 'biblio/bibtex/utils/bibindex/biblook.c')
-rw-r--r--biblio/bibtex/utils/bibindex/biblook.c1245
1 files changed, 1245 insertions, 0 deletions
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);
+}