[alt.sources] lq-text Full Text Retrieval Database Part 06/13

lee@sq.sq.com (Liam R. E. Quin) (03/04/91)

: cut here --- cut here --
: To unbundle, sh this file
#! /bin/sh
: part 06
echo x - lq-text/src/liblqtext/progname.c 1>&2
sed 's/^X//' >lq-text/src/liblqtext/progname.c <<'@@@End of lq-text/src/liblqtext/progname.c'
X/* progname.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X * This file simply declares progname.
X * This variable MUST be set by main().
X */
X
Xchar *progname = (char *) 0;
X
X/* $Id: progname.c,v 1.2 90/10/06 00:12:19 lee Rel1-10 $
X *
X * $Log:	progname.c,v $
X * Revision 1.2  90/10/06  00:12:19  lee
X * Prepared for first beta release.
X * 
X * Revision 1.1  90/03/24  17:07:22  lee
X * Initial revision
X * 
X *
X */
@@@End of lq-text/src/liblqtext/progname.c
echo x - lq-text/src/liblqtext/smalldb.c 1>&2
sed 's/^X//' >lq-text/src/liblqtext/smalldb.c <<'@@@End of lq-text/src/liblqtext/smalldb.c'
X/* smalldb.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X */
X
X/* Simple interface to start and end dbm.
X * You may also need to supply dbm_store() and dbm_fetch(), but these
X * should certainly be macros.
X *
X * $Id: smalldb.c,v 1.5 91/03/03 00:15:22 lee Rel1-10 $
X *
X * $Log:	smalldb.c,v $
X * Revision 1.5  91/03/03  00:15:22  lee
X * Improved an error message and fixed a permissions bug.
X * 
X * Revision 1.4  91/03/02  18:52:48  lee
X * Default access is now read only -- lqWriteAccess must be called otherwise.
X * 
X * Revision 1.3  90/10/06  00:12:20  lee
X * Prepared for first beta release.
X * 
X * Revision 1.2  90/09/20  17:53:26  lee
X * slight error reporting improvement.
X * 
X * Revision 1.1  90/08/09  19:16:56  lee
X * Initial revision
X * 
X * Revision 2.2  89/10/08  20:47:14  lee
X * Working version of nx-text engine.  Addfile and wordinfo work OK.
X * 
X * Revision 2.1  89/10/02  01:15:55  lee
X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
X * 
X * Revision 1.2  89/09/16  21:18:39  lee
X * First demonstratable version.
X * 
X * Revision 1.1  89/09/07  21:06:11  lee
X * Initial revision
X * 
X *
X */
X
X#include "globals.h"
X
X#include <stdio.h>
X
X#include <fcntl.h>
X#ifdef BSD
X# include <sys/param.h>
X# define PATH_MAX MAXPATHLEN /* untested, sorry */
X#else /*!BSD*/
X# include <limits.h> /* for PATH_MAX */
X#endif
X#include "smalldb.h"
X#include "emalloc.h"
X
Xextern int strcmp();
Xextern char *strcpy();
X
X/* The physical database for the list of words, and for the list
X * of files, uses ndbm.
X * The advantage of this is that it takes only two file system accesses
X * to retrieve any data item (honest!).
X * It's also reasonably fast at insertion.
X * One disadvantage is that it doesn't cope if too many words have the
X * same (32-bit) hash function, although publicly available replacements
X * such as the GNU project's gdbm fix this.
X *
X * Since starting the database is expensive (two opens and a malloc),
X * I have a cache of DBM pointers and keep them open.  Versions of the
X * dbm routines that don't support more than one database will have to
X * have a cache-size of one!
X * I am not sure what the impact of this would be on performance; for
X * adding a new file it shouldn't be too bad, as the file list is examined
X * only once for each file, during reading, and the word database is looked
X * at (at least once for each distinct word) only on writing.
X * For retrieval, however, the word database will be looked at for each
X * word in the query, and the file database for (potentially) each match
X * of each word, so the requests will be more interspersed.
X * Under no circumstances is it acceptable to dispense with the cache, as
X * otherwise you will be doing (literally) thousands of calls to
X * open() and close() per second!
X *
X */
X
X#undef startdb
X
X#ifndef CACHE
X/* It's unusual to deal with lots of databases at once, so let's not
X * waste RAM...
X */
X# define CACHE 3
X#endif
X
Xstatic char NameCache[CACHE][PATH_MAX + 1]; /* + 1 for \0, I think */
Xstatic DBM *Cache[CACHE]; /* (set to zero by definition) */
X
Xstatic int MaxInCache = (-1);
X
X/* FileFlags and Mode are passed to dbm_open */
Xstatic int FileFlags = O_RDONLY;
Xstatic int FileModes = 0;
X
Xvoid
XlqWriteAccess()
X{
X    FileFlags = O_RDWR|O_CREAT;
X    FileModes = 0664; /* owner and group write, others read only */
X}
X
XDBM *
Xstartdb(FilePrefix)
X    char *FilePrefix;
X{
X    extern int errno;
X    register int i;
X
X    for (i = 0; i <= MaxInCache; i++) {
X	if (Cache[i] && STREQ(NameCache[i], FilePrefix)) {
X	    return Cache[i];
X	}
X    }
X
X    /* Find an empty slot */
X    for (i = 0; i <= MaxInCache; i++) {
X	if (Cache[i] == (DBM *) 0) break;
X    }
X
X    if (i > MaxInCache) {
X	if (i >= CACHE) i = 0;
X    }
X
X    if (Cache[i]) dbm_close(Cache[i]);
X    NameCache[i][0] = '\0';
X
X    errno = 0;
X
X    if ((Cache[i] = dbm_open(FilePrefix, FileFlags, FileModes)) == (DBM *)0) {
X	int e = errno;
X	(void) fprintf(stderr, "%s: dbm_open error %d: ", progname, errno);
X	errno = e;
X	perror(FilePrefix);
X	exit(1);
X    }
X    (void) strcpy(NameCache[i], FilePrefix);
X    if (i > MaxInCache) MaxInCache = i;
X
X    return Cache[i];
X}
X
X#undef enddb
X
X/*ARGSUSED*/
Xvoid
Xenddb(db)
X    DBM *db;
X{
X    /* no-op */
X}
X
Xvoid
Xcleanupdb()
X{
X    register int i;
X
X    for (i = 0; i <= MaxInCache; i++) {
X	if (Cache[i]) dbm_close(Cache[i]);
X	Cache[i] = (DBM *) 0;
X	NameCache[i][0] = '\0';
X    }
X}
@@@End of lq-text/src/liblqtext/smalldb.c
echo x - lq-text/src/liblqtext/system.c 1>&2
sed 's/^X//' >lq-text/src/liblqtext/system.c <<'@@@End of lq-text/src/liblqtext/system.c'
X/* system.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X *
X * This is not a very portable way of doing things... and certainly not
X * a very fast one.  MUST be re-written.
X * Only for use from within curses.
X *
X * Lee
X *
X * $Id: system.c,v 1.3 90/10/06 00:21:37 lee Rel1-10 $
X */
X
X#ifdef ultrix
X# include <cursesX.h>
X#else
X# include <curses.h>
X#endif
X
X#ifndef echo
Xextern int echo();
X#endif
X#ifndef wmove
Xextern int wmove();
X#endif
X#ifndef nl
Xextern int nl();
X#endif
X#ifndef noecho
Xextern int noecho();
X#endif
X#ifndef nonl
Xextern int nonl();
X#endif
X#ifndef wrefresh
Xextern int wrefresh();
X#endif
X#ifndef waddstr
Xextern int waddstr();
X#endif
X#ifndef wclear
Xextern int wclear();
X#endif
X
Xint
XMySystem(string)
X    char *string;
X{
X    int val;
X
X    clearok(stdscr, TRUE);
X    clear();
X    refresh();
X    noraw();
X    echo();
X    nl();
X    val = system("stty opost icanon onlcr icrnl echo");
X    (void) system(string);
X    fprintf(stderr, "\n[press  return  to continue] ");
X    raw();
X    noecho();
X    nonl();
X    (void) getch();
X    clearok(stdscr, TRUE);
X    mvwaddstr(stdscr, 10, 10, "                        "); /* ???!?? */
X
X    return val;
X}
X
@@@End of lq-text/src/liblqtext/system.c
echo x - lq-text/src/lqtext/FindCommon.sh 1>&2
sed 's/^X//' >lq-text/src/lqtext/FindCommon.sh <<'@@@End of lq-text/src/lqtext/FindCommon.sh'
X:
X# FindCommon -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
X# This code is NOT in the public domain.
X# See the file COPYRIGHT for full details.
X#
X# $Id: FindCommon.sh,v 1.2 90/10/06 00:50:31 lee Rel1-10 $
X
X# Find the most common words in the database.
X# usage is % n, where n is the n most comon words to find
X
Xlqword -a | sed -e 's/^......................\(.........\)..\(..*\)$/\1	\2/' |
Xsort -nr | sed ${1-500}q
X
Xexit $?
X
X#         1 |       0 |       2 | pcpaintbrush
X#         2 |       0 |       2 | escape
X#         3 |       0 |       1 | durham
X#         4 |   60928 |      12 | making
X#         5 |       0 |       1 | ethical
X#         6 |       0 |       1 | committing
X
X# $Log:	FindCommon.sh,v $
X# Revision 1.2  90/10/06  00:50:31  lee
X# Prepared for first beta release.
X# 
X#
@@@End of lq-text/src/lqtext/FindCommon.sh
echo x - lq-text/src/lqtext/Makefile 1>&2
sed 's/^X//' >lq-text/src/lqtext/Makefile <<'@@@End of lq-text/src/lqtext/Makefile'
X# Makefile for LQ-Text, a full text retrieval package by Liam R. Quin
X# This Makefile belongs in the "src/lqtext" directory.
X#
X# Note that most of the actual configuration is done in ../Makefile and
X# in ../h/global.h, and not here.
X
X# Makefile -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
X# This code is NOT in the public domain.
X# See the file ../COPYRIGHT for full details.
X#
X# $Id: Makefile,v 1.5 91/03/03 00:19:26 lee Rel1-10 $
X
X
XPWD=lqtext
X
XTARGETS = lqaddfile lqfile lqword lqphrase lqshow lqkwik lq
XBINFILES =lqaddfile lqfile lqword lqshow lqphrase lqkwik
X
XDESTDIR=../bin
XMODE=755
XRANLIB=echo
X
XEXTRA=-I../h
X
Xall: $(TARGETS)
X
X# for ndbm (simplest), leave empty or use -lndbm if you need it
X# for sdbm (best so far), use ../lib/libsdbm.a
X# for gdbm... well, I dunno.
XDBMLIBS=../lib/libsdbm.a
X# DBMLIBS=-lndbm
X# DBMLIBS=ndbm.o bcopy.o
X
XTEXTLIB=../lib/liblqtext.a ../lib/liblq.a
X
X# The following are for "make depend" and for sabre to load...
XDEPENDFILES = ReadAhead.c SixBit.c fileindex.c lqaddfile.c lqphrase.c \
X	      lqshow.c lqword.c sizes.c wordtable.c
X
X# MALLFILES=/usr/lib/debug/malloc.o /usr/lib/debug/mallocmap.o
XMALLFILES = 
X
Xinstall: all
X	for i in $(BINFILES); do cp "$$i" $(DESTDIR); \
X	strip "$(DESTDIR)/$$i" ; \
X	done ; \
X	mv lq $(DESTDIR)/lq; chmod $(MODE) $(DESTDIR)/lq;
X
X.SUFFIXES: .c .o .src .obj
X
X.c.src:
X	#load $(CFLAGS) $<
X
X.o.obj:
X	#load $(CFLAGS) $<
X
X# If you are going to use saber on these, you should name the programs.
Xsaber_src:
X
Xsaber_obj:
X
Xlq: lq.sh
X	cp lq.sh lq
X	chmod +x lq
X
Xlqshow: lqshow.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqshow lqshow.o $(TEXTLIB) $(TERMCAP) $(DBMLIBS)
X
Xlqaddfile: lqaddfile.o wordtable.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqaddfile lqaddfile.o wordtable.o \
X			$(TEXTLIB) $(MALLOC) $(DBMLIBS) $(MALLFILES)
X
Xlqfile: fileindex.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqfile fileindex.o $(TEXTLIB) $(MALLOC) $(DBMLIBS)
X	
Xlqword: lqword.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqword lqword.o $(TEXTLIB) $(MALLOC) $(DBMLIBS)
X
Xlqkwik: lqkwik.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqkwik lqkwik.o $(TEXTLIB) $(MALLOC) $(DBMLIBS)
X
Xlqphrase: lqphrase.o $(TEXTLIB)
X	$(CC) $(CFLAGS) -o lqphrase lqphrase.o $(TEXTLIB) $(DBMLIBS)
X
Xlint: AddFile.Lint News.Lint FileInfo.Lint Phrase.Lint
X
Xtidy:
X	/bin/rm -f *.o core
X
Xclean: tidy
X	/bin/rm -f $(TARGETS) $(TEST)
X
Xdepend:
X	mkdep $(CFLAGS) *.c
X
X#
X# $Log:	Makefile,v $
X# Revision 1.5  91/03/03  00:19:26  lee
X# added lqkwik
X# 
X# Revision 1.4  90/10/06  00:50:42  lee
X# Prepared for first beta release.
X# 
X# Revision 1.3  90/10/05  23:54:57  lee
X# deleted mkdep output.
X# 
X# Revision 1.2  90/09/28  21:54:01  lee
X# No longer uses OWNER.
X# 
X# Revision 1.1  90/08/09  19:17:39  lee
X# Initial revision
X# 
X 
X# DO NOT DELETE THIS LINE -- mkdep uses it.
X# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
X
XSixBit.o: SixBit.c ../h/globals.h 
XSixBit.o: ../h/wordrules.h
Xfileindex.o: fileindex.c ../h/globals.h 
Xfileindex.o: ../h/emalloc.h ../h/fileinfo.h
Xlqaddfile.o: lqaddfile.c 
Xlqaddfile.o: ../h/globals.h ../h/fileinfo.h ../h/emalloc.h
Xlqaddfile.o: ../h/wordinfo.h ../h/pblock.h ../h/wordrules.h ../h/filter.h
Xlqkwik.o: lqkwik.c ../h/globals.h ../h/fileinfo.h
Xlqkwik.o: ../h/wordinfo.h ../h/pblock.h ../h/wordrules.h ../h/pblock.h
Xlqkwik.o: ../h/emalloc.h
Xlqphrase.o: lqphrase.c ../h/globals.h ../h/emalloc.h
Xlqphrase.o: ../h/fileinfo.h ../h/wordinfo.h ../h/pblock.h ../h/pblock.h
Xlqphrase.o: ../h/phrase.h
Xlqshow.o: lqshow.c ../h/globals.h
Xlqword.o: lqword.c ../h/globals.h 
Xwordtable.o: wordtable.c ../h/globals.h
X
X# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
@@@End of lq-text/src/lqtext/Makefile
echo x - lq-text/src/lqtext/ReadAhead.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/ReadAhead.c <<'@@@End of lq-text/src/lqtext/ReadAhead.c'
@@@End of lq-text/src/lqtext/ReadAhead.c
echo x - lq-text/src/lqtext/fileindex.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/fileindex.c <<'@@@End of lq-text/src/lqtext/fileindex.c'
X/* fileindex.c -- Copyright 1989, 1990 Liam R. Quin.  All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X */
X
X/* A simple program to give information about one or more files about
X * which information is stored in the NX-Text database.
X *
X * $Id: fileindex.c,v 1.4 91/03/02 18:56:53 lee Rel1-10 $
X */
X
X#include "globals.h" /* defines and declarations for database filenames */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <malloc.h>
X#include "emalloc.h"
X#include "fileinfo.h"
X
Xstatic char *Revision = "@(#) lqtext 2.3 89/11/34";
X
X/* The position of the \n in the 26-char string returned by ctime(3): */
X#define DATENEWLINE 24
X
Xchar *progname;
Xint AsciiTrace = 0;
X
X/** System calls and library functions used in this file: **/
X
X/** Unix System calls: **/
Xextern void exit();
X/** System Library Functions: **/
X
X/** external lqtext functions: **/
Xextern void cleanupdb(), SetDefaults();
Xint SaveFileInfo(), GetFilterType();
X#ifndef efree
X extern void efree();
X#endif
X/** Functions defined within this file: **/
Xvoid AddInfo(), AllInfo(), Display(), PrintInfo();
X
Xint AllFiles = 0;
Xint ListMode = 0;
Xint AddFiles = 0;
X
Xint
Xmain(argc, argv)
X    int argc;
X    char *argv[];
X{
X    extern int optind, getopt();
X    /** extern char *optarg; (unused at the moment) **/
X    int ch;
X    int ErrorFlag = 0;
X
X    progname = argv[0];
X
X    SetDefaults(argc, argv);
X
X    /* All programs take Zz:Vv */
X    while ((ch = getopt(argc, argv, "Zz:VvAax")) != EOF) {
X	switch (ch) {
X	case 'z':
X	case 'Z':
X	    break; /* done by SetDefaults(); */
X	case 'V':
X	    fprintf(stderr, "%s version %s\n", progname, Revision);
X	    break;
X	case 'v':
X	    AsciiTrace = 1;
X	    break;
X	case 'A':
X	    AddFiles = 1;
X	    break;
X	case 'a':
X	    AllFiles = 1;
X	    break;
X	case 'l':
X	    ListMode = 1;
X	    break;
X	case 'x':
X	    ErrorFlag = (-1);
X	    break;
X	case '?':
X	    ErrorFlag = 1;
X	    break;
X	}
X    }
X
X    /* Normally put call to lrqError here to give a helpful message,
X     * but not yet ready to ship the error handling package, sorry
X     */
X    if (ErrorFlag) {
X	fprintf(stderr, "%s: usage: %s [options] [files]\n",progname,progname);
X	fprintf(stderr, "%s: options are:\n", progname);
X	fputs("\
X	-c file -- treat the named file as a list of common words\n\
X	-d dir	-- use the lq-text database in the directory \"dir\"\n\
X	-l	-- list mode: no header output or lines drawn\n\
X	-s	-- show the list of saved files\n\
X	-t N	-- set trace level to N [default: 0]\n\
X	-V	-- print version information\n\
X	-v	-- be verbose (same as -t 1)\n\
X	-x	-- print this explanation\n\
X\n\
XIn addition, if no files are given, the following are understood:\n\
X	-A	-- add the named files to the list of known files\n\
X	-a	-- list information about all files\n", stderr);
X	exit((ErrorFlag > 0) ? 1 : 0);
X    }
X
X    if (AllFiles && AddFiles) {
X	fprintf(stderr, "%s: do not use both -a and -A options\n", progname);
X	fprintf(stderr, "\tuse %s -x for further explanation.\n", progname);
X	exit(1);
X    }
X
X    if (optind >= argc && !AllFiles && !AddFiles) {
X	fprintf(stderr,
X	"%s: You must either give the -a option or specify files to list.\n",
X		progname);
X	fprintf(stderr, "\tuse %s -x for further explanation.\n", progname);
X	exit(1);
X    }
X
X    if (!AddFiles || !ListMode) {
X	printf("%-7.7s | T | %-20.20s | %s\n",
X		"FID", "Date Last indexed", "Current Location");
X	puts(
X"========|===|======================|=========================================="
X	);
X    }
X    if (AllFiles) {
X	AllInfo();
X    } else {
X	if (AddFiles) {
X	    extern lqWriteAccess();
X
X	    lqWriteAccess();
X	}
X
X	while (optind < argc) {
X	    if (AddFiles) {
X		AddInfo(argv[optind++]);
X	    } else {
X		PrintInfo(argv[optind++]); /* ugh */
X	    }
X	}
X    }
X    cleanupdb(); /* close dbm files */
X    exit(0);
X    /*NOTREACHED*/
X    return 1; /* for lint and gcc... */
X}
X
Xvoid
XPrintInfo(Name)
X    char *Name;
X{
X    extern t_FileInfo *GetFileInfo();
X    long FID;
X    extern long atol();
X    extern t_FID Name2FID();
X
X    t_FileInfo *FileInfo;
X
X    if ((FID = Name2FID(Name)) == (t_FID) 0) {
X	fprintf(stderr, "No FID information for filename: %s\n", Name);
X        if ((FID = atol(Name)) == (t_FID) 0) {
X	    return;
X	}
X    }
X
X    /* get info from the list */
X    if ((FileInfo = GetFileInfo(FID)) == (t_FileInfo *) 0) {
X	fprintf(stderr, "No index information for: %s\n", Name);
X	return;
X    }
X    Display(FileInfo);
X}
X
Xvoid
XDisplay(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    extern char *ctime();
X    char *DateString;
X
X    DateString = ctime(&(FileInfo->Date));
X    DateString[DATENEWLINE] = '\0'; /* delete the trailing newline */
X
X    if (ListMode) {
X	printf("%lu %d %s %s\n",
X	FileInfo->FID, FileInfo->FilterType, &DateString[4], FileInfo->Name);
X    } else {
X	printf("%7lu | %d | %-20.20s | %s\n",
X	    FileInfo->FID, FileInfo->FilterType, &DateString[4], FileInfo->Name);
X    }
X}
X
X/**
XMon Sep 25 23:58:53 BST 1989
XFID     | T | Date Last indexed    | Current Location
X========|===|======================|===========================================
X      1 | 0 | Sep 25 20:31:26 1989 | /usr2/liam/Bible/NT/John/john01.kjv
X      2 | 0 | Sep 25 20:31:28 1989 | /usr2/liam/Bible/NT/John/john02.kjv
X      3 | 0 | Sep 25 20:31:30 1989 | /usr2/liam/Bible/NT/John/john03.kjv
X**/
X
Xvoid
XAllInfo()
X{
X    extern long GetMaxFID();
X    extern t_FileInfo *GetFileInfo();
X
X    t_FileInfo *FileInfo;
X    long FID;
X    long MaxFid = GetMaxFID();
X
X    for (FID = 0L; FID <= MaxFid; FID++) {
X	if ((FileInfo = GetFileInfo(FID)) != (t_FileInfo *) 0) {
X	    Display(FileInfo);
X	    efree(FileInfo); /* NOTDONE use destroyfileinfo() */
X	}
X    }
X    printf("Max File Identifier is %lu\n", MaxFid);
X}
X
Xvoid
XAddInfo(FileName)
X    char *FileName;
X{
X    extern time_t time();
X    extern unsigned long GetNextFID();
X    t_FileInfo FileInfo;
X
X    FileInfo.Name = FileName;
X    (void) time(&(FileInfo.Date));
X    FileInfo.FID = GetNextFID();
X    FileInfo.Stream = 0; /* force GetFilterType to use open()? */
X
X    /* determine filter type */
X    FileInfo.FilterType = GetFilterType(&FileInfo);
X
X    printf("%d %s (type %d) %s\n",
X	    FileInfo.FID,
X	    FileInfo.Name,
X	    FileInfo.FilterType,
X	    SaveFileInfo(&FileInfo) == 0 ?
X			    "saved successfully." :
X			    "not saved."
X    );
X}
X
X/*
X * $Log:	fileindex.c,v $
X * Revision 1.4  91/03/02  18:56:53  lee
X * Now asks for write access iff [sic] necessary
X * 
X * Revision 1.3  90/10/06  00:50:50  lee
X * Prepared for first beta release.
X * 
X * Revision 1.2  90/08/29  21:44:51  lee
X * Alpha release
X * 
X * Revision 1.1  90/08/09  19:17:11  lee
X * Initial revision
X * 
X * Revision 2.2  89/10/08  20:45:46  lee
X * Working version of nx-text engine.  Addfile and wordinfo work OK.
X * 
X * Revision 2.1  89/10/02  01:14:18  lee
X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
X * 
X * Revision 1.2  89/09/16  21:16:17  lee
X * First demonstratable version.
X * 
X * Revision 1.1  89/09/07  21:05:55  lee
X * Initial revision
X *
X */
@@@End of lq-text/src/lqtext/fileindex.c
echo x - lq-text/src/lqtext/intersect.sh 1>&2
sed 's/^X//' >lq-text/src/lqtext/intersect.sh <<'@@@End of lq-text/src/lqtext/intersect.sh'
X:
X# intersect word-one word-two
X#
X# intersect -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
X# This code is NOT in the public domain.
X# See the file ../COPYRIGHT for full details.
X#
X# $Id: intersect.sh,v 1.3 91/03/03 00:18:59 lee Rel1-10 $
X#
X
X
XFileNumber=0
XFileList=
XProgram=lqphrase
XProgOpts=
XAll=/tmp/iAll$$
Xexport All
X
Xtrap '/bin/rm -f $All $tmp $First $FileList; exit' 0 1 2 3 15
X
Xif [ x"$1" = x"" ]
Xthen
X    echo "$0: Usage: `basename $0` {-w word} | {-p phrase} ..." 1>&2
X    exit 1
Xfi
X
X
Xfor i
Xdo
X    if [ x"$i" = x"-p" ]
X    then
X	Program=lqphrase
X	ProgOpts=
X    elif [ x"$i" = x"-w" ]
X    then
X	Program=lqword
X	ProgOpts=-l
X    else
X	tmp=/tmp/inter.$FileNumber
X	$Program $ProgOpts "$i" | tee -a $ALL | awk '{ print $3 }' | sort -u > $tmp
X	if [ x"$First" = x"" ]
X	then
X	    First="$tmp"
X	else
X	    FileList="$FileList $tmp"
X	fi
X	FileNumber=`expr $FileNumber + 1`
X    fi
Xdone
X
X# Find matches...
Xtmp=/tmp/inter.tmp$$
X
Xfor i in $FileList
Xdo
X    fgrep -x -f $First $i | sort -u > $tmp
X    mv $tmp $First
Xdone
X
Xmv $First $tmp
Xsed 's/^/ /' $tmp > $First
X
Xfgrep -f $First $All
Xexit 0
X
X#
X#
X# $Log:	intersect.sh,v $
X# Revision 1.3  91/03/03  00:18:59  lee
X# brought up to date a little...
X# 
X# Revision 1.2  90/10/06  00:50:52  lee
X# Prepared for first beta release.
X# 
X# Revision 1.1  90/08/29  21:45:01  lee
X# Initial revision
X# 
X#
X#
@@@End of lq-text/src/lqtext/intersect.sh
echo x - lq-text/src/lqtext/lq.sh 1>&2
sed 's/^X//' >lq-text/src/lqtext/lq.sh <<'@@@End of lq-text/src/lqtext/lq.sh'
X#! /bin/sh
X: use /bin/sh
X# put the : line first on System V
X
X# lq -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
X# This code is NOT in the public domain.
X# See the file ../COPYRIGHT for full details.
X#
X# $Id: lq.sh,v 1.3 90/10/06 00:50:53 lee Rel1-10 $
X#
X
Xif [ x"`echo -n hello`" = x'hello' ]
Xthen
X    N=-n
X    C=
Xelse
X    N=
X    C='\c'
Xfi
X
Xquit=no
Xt=/tmp/lq$$
XListFile=/tmp/lqshow$$
Xexport ListFile
X
Xtrap '/bin/rm -f $t; exit' 0 1 2 3 15
X
X
Xwhile  [ x"$quit" != x"yes" ]
Xdo
X    cat << boy
X| Type a words or phrases to find, one per line,
X| and then press return.
Xboy
X    x='fhdjfd'
X    Phrases=
X    while [ x"$x" != x"" ]
X    do
X	echo $N "| $C"
X	read x
X	if [ x"$x" != x"" ]
X	then
X	    New=`echo "$x" | sed 's/"/:/g'`
X	    Phrases="${Phrases} \"$x\""
X	fi
X    done
X    echo $Phrases
X    eval lqphrase -p $Phrases \> $t
X    if [ ! -s $t ]
X    then
X	echo "No match"
X    else
X	# determine the order in which matches will be presented to the user:
X	sort +2 -o "$t" "$t" # (this is our ranking function)
X	# (it only makes a difference if there was more than one phrase)
X
X	# Now some arcanery, I'm afraid...  The trick is that lqshow can be
X	# given the name of a file descriptor in which to save the names of
X	# any files the user selects (with "s").
X	old_t="$t"
X	t="$t ${ListFile}"
X	lqshow -o 3 -f $t 3>> ${ListFile}
X	t="$old_t"
X	if [ -s ${ListFile} ]
X	then ## the user typed s/k/whatever to save some files...
X	    # make the list by interpreting the list file:
X	    LIST=`awk '
X	    /^#.*$/ { next }
X	    ($1 == "s") { SAVE[$2]++ }
X	    ($1 == "d") { SAVE[$2] = 0 }
X	    END {
X		for (i in SAVE) {
X		    if (SAVE[i] > 0) print i
X		}
X	    }' $ListFile | sort -u`
X	    # make a new list file...
X	    echo "$LIST" | sed '/^[ 	]*$/d' > $ListFile
X	    LIST="" # save memory
X	fi
X	# now see if it's still non-empty...
X	if [ -s ${ListFile} ]
X	then
X	    List="Type S filename to save the list of files (s also quits) "
X	else
X	    /bin/rm -f ${ListFile}
X	fi
X    fi
X    echo $List
X    echo $N "Type q to quit, or return to continue: $C"
X    read quit rest
X    case "$quit" in
X    [qQ]*) quit="yes" ;;
X    [sS]) # save the list of matches
X	cat $ListFile
X
X	if [ ! -s "$ListFile" ]
X	then
X	    echo "No files in the list to save."
X	    quit=no
X	else
X	    if [ -z "$rest" ]
X	    then rest="lq.list"
X	    fi
X
X	    if [ -f "$rest" ]
X	    then echo "Appending to existing file $rest"
X	    fi
X
X	    cat $ListFile >> $rest
X	    rm $ListFile
X	    if [ x"$quit" = x"s" ]
X	    then quit=yes
X	    else quit=no
X	    fi
X	fi
X    ;;
X    *)	   quit=no ;;
X    esac
Xdone
X
X#
X# $Log:	lq.sh,v $
X# Revision 1.3  90/10/06  00:50:53  lee
X# Prepared for first beta release.
X# 
X#
X#
@@@End of lq-text/src/lqtext/lq.sh
echo x - lq-text/src/lqtext/lqaddfile.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/lqaddfile.c <<'@@@End of lq-text/src/lqtext/lqaddfile.c'
X/* lqaddfile.c -- Copyright 1989, 1990 Liam R. Quin.  All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X */
X
X/* addfile -- add a file to the LQ-Text text retrieval index
X * Liam Quin, August 1989 and later...
X *
X * $Id: lqaddfile.c,v 1.14 91/03/02 21:22:39 lee Rel1-10 $ 
X */
X
Xstatic char *Version = "@(#) $Id: lqaddfile.c,v 1.14 91/03/02 21:22:39 lee Rel1-10 $";
X
X#ifdef SYSV
Xextern int _filbuf(); /* used but not defined in stdio.h */
X#endif
X#include <stdio.h>
X#include <malloc.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#ifdef BSD
X# include <strings.h>
X#else
X# include <string.h>
X#endif
X
X#include "globals.h" /* defines and declarations for database filenames */
X#include "fileinfo.h"
X#include "wordinfo.h"
X#include "wordrules.h"
X#include "filter.h"
X
X#include "emalloc.h"
X
X#define enew(var, type) (var = (type *) emalloc(sizeof(type)))
X
X#ifdef SYSV
X#define TOLOWER(ch) ch = tolower(ch)
X#else
X#define TOLOWER(ch) if (isupper(ch)) ch = tolower(ch)
X#endif
X
Xvoid DestroyFileInfo(), SaveFileInfo(), AddStream(), AddFrom();
Xextern lqWriteAccess(); /* Allow write access to the database */
X/* Symbol Table Interface */
Xextern void AddWord(), WriteCurrentMaxWID();
Xextern void DumpCache(), cleanupdb();
Xextern char *WordRoot();
Xextern int TooCommon(), GetFilterType();
Xint RealGetChar(), AddFile();
X
X/** System calls and library routines used in this file: **/
X/** System calls: **/
Xextern void exit();
Xextern int stat();
X/** Library Functions: **/
Xextern int atoi();
X#ifndef tolower
X extern int tolower();
X#endif
Xextern void perror();
X/**/
X
Xchar *progname = "@(#) : addfile.c,v 1.1 89/08/28 20:16:05 lee Locked $";
Xstatic int UseLineNumbers = 0;
X
X/* FROM pblock.c */
Xextern int AsciiTrace; /* provide increasingly verbose info if not zero */
X
Xstatic int LastChar = 0;
Xstatic int _chForLee = 0;
X
X#define GetChar(F) \
X    ( LastChar ? \
X	(++BytesRead, (_chForLee = LastChar), (LastChar = 0), _chForLee) : \
X	( (_chForLee = getc(FileInfo->Stream)) != '\'' || !InWord) ? \
X		(++BytesRead, _chForLee) : RealGetChar(F) )
X	 
Xint
Xmain(argc, argv)
X    int argc;
X    char *argv[];
X{
X    extern char *strrchr();
X    extern int getopt(), cknatstr();
X    extern void SetDefaults();
X    extern char *optarg;
X    extern int optind;
X    extern int MaxWordsInCache; /* see wordtable.c */
X
X    int c;
X    int ErrorFlag = 0;
X    int DoNothing = 0;
X    char *InputFile = (char *) 0;
X
X#ifdef MALLOCTRACE
X    malloc_debug(2);
X#endif
X
X    progname = argv[0]; /* retain the full path at first */
X
X#ifdef M_MXFAST
X    (void) mallopt(M_MXFAST, sizeof(t_WordPlace));
X    /* may need to comment mallopt() out entirely for BSD -- use ifndef.
X     * seems to work under SunOS, though.
X     * When it works, it says "Allocate 100 or so chunks of this size at a
X     * time, and whenver I ask for this much or less, give me one of the
X     * chunks".  Clearly it had better not be too large, but it is a big
X     * win with a structure allocated for every occurrence of every word!
X     */
X#endif
X
X    SetDefaults(argc, argv);
X
X    while ((c = getopt(argc, argv, "w:f:xVZz:")) != -1) {
X	switch (c) {
X	case 'w':
X	    if (!cknatstr(optarg)) {
X		fprintf(stderr,
X			    "%s: -w must be given a number >= 0, not \"%s\"\n",
X							    progname, optarg);
X		fprintf(stderr, "\tuse %s -xv for further information\n");
X		exit(1);
X	    }
X	    MaxWordsInCache = atoi(optarg);
X	    break;
X	case 'Z':
X	case 'z':
X	    break; /* work done in SetDefault() */
X	case 'V':
X	    fprintf(stderr, "%s: version: %s\n", progname, Version);
X	    DoNothing = 1;
X	    break;
X	case 'f':
X	    if (InputFile) {
X		fprintf(stderr,
X"%s: only one -f option allowed; use -xv for explanation\n", progname);
X
X		exit(1);
X	    }
X	    InputFile = optarg;
X	    break;
X	case 'x':
X	    ErrorFlag = (-1);
X	    break;
X	default:
X	case '?':
X	    ErrorFlag = 1;
X	}
X    }
X
X    if ((progname = strrchr(progname, '/')) != (char *) NULL) {
X	++progname; /* step over the last / */
X    } else {
X	progname = argv[0];
X    }
X
X    if (ErrorFlag > 0) {
X	fprintf(stderr, "use %s -x or %s -xv for an explanation.\n",
X							progname, progname);
X	exit(1);
X    } else if (ErrorFlag < 0) { /* -x was used */
X	fprintf(stderr, "%s -- add files to an lq-text retrieval database\n",
X								    progname);
X
X	fputs("Options are:\n\
X	-f file -- read the list of files to index from \"file\"\n\
X	-c file	-- cfile contains a list of common words to be ignored\n\
X	-d dir	-- use the lq-text database in the named directory\n\
X	-t N	-- set the trace level to N [default: N = 0]\n\
X	-V	-- print Version number and exit\n\
X	-v	-- be verbose (equivalent to -t 1)\n\
X	-w n	-- dump the word-cache every n words\n\
X	-x	-- print this eXplanation and exit\n\
X	--	-- all following arguments are file names\n\
X\n\
X", stderr);
X	if (AsciiTrace == 1) {
X	    /* used -v or -t1 */
X	fprintf(stderr, "\n\
X    Any remaining arguments are taken to be file names.  The current\n\
XDOCPATH (%s) is searched for the files, and they are read and added\n\
Xto the index.  (If you use the -f option, you should not give filename\n\
Xarguments on the command line, although you can use \"-f -\" to read the\n\
Xlist of files from standard input, one per line.\n\
XSetting (with -w) the size of the cache may dramatically\n\
Ximprove performance.  Systems with memory larger than the data can try -w0.\n\
XSee lqtext(1) for more information.\n", DocPath);
X	}
X    exit(0);
X
X    }
X
X    if (DoNothing) {
X	if (optind < argc) {
X	    fprintf(stderr, "%s: warning: %d extra argument%s ignored...\n",
X				progname, argc - optind,
X				argc - optind == 1 ? "" : "%s" );
X	    fprintf(stderr, "Use %s -x for an explanation\n", progname);
X	}
X	exit(0);
X    }
X
X    lqWriteAccess();
X
X    if (InputFile) {
X	if (optind < argc) {
X	    fprintf(stderr, "%s: -f: too many arguments; use -xv\n", progname);
X	    exit(1);
X	}
X	AddFrom(InputFile);
X    } else for (; optind < argc; ++optind) {
X	if (AddFile(argv[optind]) < 0 && AsciiTrace >= 1) {
X	    fprintf(stderr, "%s: warning: Problem adding file %s\n",
X			progname, argv[optind]);
X	}
X    }
X
X#ifndef MALLOCTRACE
X    DumpCache(0); /* the 0 means don't bother calling free() */
X#else
X    DumpCache(1); /* Free everthing so whatever is left is a memory leak */
X#endif
X
X    cleanupdb(); /* empty the dbm cache */
X    WriteCurrentMaxWID();
X
X#ifdef MALLOCTRACE
X    (void) fprintf(stderr, "%s: Malloctrace: checking...\n", progname);
X    malloc_verify();
X    (void) fprintf(stderr, "%s: Malloc Map\n", progname);
X    mallocmap();
X#endif
X
X    exit(0);
X    /*NOTREACHED*/
X    return 1; /* disaster if we get here -- it's just for lint! */
X}
X
Xvoid
XAddFrom(Name)
X    char *Name;
X{
X    char *GetLine();
X
X    FILE *fp;
X    char *Line;
X
X    if (Name[0] == '-' && Name[1] == '\0') {
X	fp = stdin;
X    } else {
X	fp = fopen(Name, "r");
X    }
X
X    if (fp == (FILE *) 0) {
X	extern int errno;
X	int e = errno;
X
X	fprintf(stderr, "%s: -f: can't open ", progname);
X	errno = e;
X	perror(Name);
X	exit(1);
X    }
X
X    while ((Line = GetLine(fp, Name)) != (char *) 0) {
X	if (AddFile(Line) < 0 && AsciiTrace >= 1) {
X	    /* we already got one error message from AddFile() */
X	    fprintf(stderr, "%s: warning: Problem adding file %s\n",
X			progname, Line);
X	}
X    }
X
X    if (fp != stdin) {
X	(void) fclose(fp);
X    }
X}
X
Xstatic int LineInFile = 0;
Xstatic FILE *LastFile = 0;
X
Xchar *
XGetLine(fp, Name)
X    FILE *fp;
X    char *Name;
X{
X    static char *Line = (char *) 0;
X    static int Length = 0;
X    int ch;
X    register char *p;
X
X    if (!Line) {
X	if (Length <= 10) Length = 30;
X	Line = emalloc(Length);
X    }
X
X    p = Line;
X
X    if (fp == LastFile) {
X	++LineInFile;
X    } else {
X	LineInFile = 0; /* number lines from zero! */
X	LastFile = fp;
X    }
X
X    while ((ch = getc(fp)) != EOF) {
X	static int HaveWarned = 0;
X
X	if (isspace(ch)) {
X	    if (p == Line) { /* ignore blank lines and leading blanks */
X		continue;
X	    }
X	    if (ch == '\n') {
X		if (p == (char *) 0) {
X		    /* how could this ever happen?  do I need it? */
X		    p = Line;
X		    continue;
X		}
X		*p = '\0';
X		return Line;
X	    }
X	    if (AsciiTrace && !HaveWarned) {
X		fprintf(stderr,
X"%s: -f: Warning: spaces found in filenames read from \"%s\"\n",
X							    progname, Name);
X		HaveWarned = 1;
X	    }
X	}
X
X	/* add the character to the string */
X	if (p - Line + 1 >= Length) {
X	    int SaveWhere = p - Line;
X	    Length += 30;
X	    Line = erealloc(Line, Length);
X	    p = &Line[SaveWhere];
X	}
X	*p++ = ch;
X    }
X
X    if (p && Line && p != Line) {
X	fprintf(stderr, "%s: -f: warning: no newline at the end of \"%s\"\n",
X						progname, Name);
X	*p = '\0';
X	return Line;
X    }
X
X    return (char *) 0;
X}
X
Xextern int fclose(), pclose();
X
Xt_FileInfo *
XMakeFileInfo(Name)
X    char *Name;
X{
X#ifdef BSD
X    extern time_t time();
X#else
X    extern long time();
X#endif
X    extern t_FID Name2FID();
X    extern t_FileInfo *GetFileInfo();
X    extern t_FID GetNextFID();
X    FILE *MakeInput();
X    struct stat StatBuf;
X
X    t_FileInfo *FileInfo = 0;
X    t_FID FID;
X
X    if (!Name || !*Name) return (t_FileInfo *) 0; /* sanity */
X
X    if (stat(Name, &StatBuf) < 0) {
X#ifndef FindFile /* it is a macro these days... */
X	extern char *FindFile();
X#endif
X	extern int errno;
X
X	int e = errno;
X	char *doc;
X
X	if ((doc = FindFile(Name)) == (char *) 0) {
X	    fprintf(stderr, "Can't index ");
X	    errno = e; /* fprintf might well clobber errno! */
X	    perror(Name);
X	    return (t_FileInfo *) 0;
X	}
X
X	if (stat(doc, &StatBuf) < 0) {
X	    e = errno;
X	    fprintf(stderr, "Can't index ");
X	    errno = e; /* fprintf might well clobber errno! */
X	    perror(Name);
X	    return (t_FileInfo *) 0;
X	}
X	Name = doc;
X    }
X
X    if (StatBuf.st_size == 0L) {
X	if (AsciiTrace) {
X	    fprintf(stderr, "%s empty -- not indexed\n", Name);
X	}
X	return (t_FileInfo *) 0;
X    }
X    /* See if it's in the index already: */
X    if ((FID = Name2FID(Name)) != (t_FID) 0) {
X
X	if ((FileInfo = GetFileInfo(FID)) != (t_FileInfo *) 0) {
X	    /* Check to see if the file hass changed since it was last
X	     * indexed.  If it has, we should delete the old one from
X	     * the database and give this one a new FID, but I have
X	     * not done that yet -- that's /usr/local/lib/lqtextd or
X	     * something, I suppose!
X	     */
X	    if (FileInfo->Date >= StatBuf.st_mtime) {
X		if (AsciiTrace) {
X		    fprintf(stderr, "%s unchanged -- not indexed\n", Name);
X		}
X		DestroyFileInfo(FileInfo);
X		return (t_FileInfo *) 0;
X	    }
X	}
X    } else {
X	FID = GetNextFID((long) StatBuf.st_size);
X    }
X
X    if (FileInfo == (t_FileInfo *) 0) {
X	/* Allocate Structure */
X	enew(FileInfo, t_FileInfo);
X
X	/* Although not always necessary, call emalloc here so that a
X	 * FileInfo can always be deleted with DestroyFileInfo()
X	 */
X	FileInfo->Name = emalloc((unsigned)(strlen(Name) + 1));
X	(void) strcpy(FileInfo->Name, Name);
X
X	/* Other bits to set: */
X
X	/* date */
X	FileInfo->Date = StatBuf.st_mtime;
X
X	/* file type */
X	if ((FileInfo->FilterType = GetFilterType(FileInfo, &StatBuf)) < 0) {
X	    if (AsciiTrace) {
X		fprintf(stderr, "%s unknown file type -- not indexed\n", Name);
X	    }
X	    (void) efree(FileInfo->Name);
X	    (void) efree((char *) FileInfo);
X	    return (t_FileInfo *) 0;
X	}
X    }
X
X    FileInfo->FID = FID;
X    FileInfo->Date = (long) time((long *) 0); /* it's a time_t on BSD */
X
X    if ((FileInfo->Stream = MakeInput(FileInfo)) == (FILE *) 0) {
X	fprintf(stderr, "%s: couldn't open filter for %s -- not indexed\n",
X						    progname, FileInfo->Name);
X	(void) efree(FileInfo->Name);
X	(void) efree((char *) FileInfo);
X	return (t_FileInfo *) 0;
X    }
X
X    return FileInfo;
X}
X
Xvoid
XDestroyFileInfo(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    if (FileInfo->Stream) {
X	if (FileInfo->FilterType >= 0 && FileInfo->FilterType < MaxFilterType){
X	    (* FilterTable[FileInfo->FilterType].close)(FileInfo->Stream);
X	}
X	FileInfo->Stream = (FILE *) 0;
X    }
X    if (FileInfo->Name) (void) efree(FileInfo->Name);
X    (void) efree((char *) FileInfo);
X}
X
Xint
XAddFile(Name)
X    char *Name;
X{
X    t_FileInfo *FileInfo;
X
X    if (!Name || !*Name) return -1;
X    if ((FileInfo = MakeFileInfo(Name)) == (t_FileInfo *) 0) return -1;
X
X    AddStream(FileInfo);
X    SaveFileInfo(FileInfo);
X    DestroyFileInfo(FileInfo);
X
X    return 0;
X}
X
XFILE *
XMakeInput(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    FILE *fp;
X    char *Buffer;
X    unsigned BufLen;
X    extern FILE *fopen(), *popen();
X
X#define FSTRING FilterTable[FileInfo->FilterType].String
X
X    if (FileInfo->FilterType > MaxFilterType) {
X	fprintf(stderr, "%s: Warning: filter type %d for %s too high (max %d)\n",
X		progname, FileInfo->FilterType, FileInfo->Name, MaxFilterType);
X	return (FILE *) 0;
X    }
X
X    if (FilterTable[FileInfo->FilterType].Type != FileInfo->FilterType) {
X	fprintf(stderr, "Fatal Filter table error, %d\n", FileInfo->FilterType);
X	exit(3);
X    }
X
X    if (FSTRING == (char *) 0) {
X	return fopen(FileInfo->Name, "r");
X    }
X
X    BufLen = strlen(FileInfo->Name) * 2 + 4 + strlen(FSTRING);
X	/* The +4 is to allow for an embedded " < " plus a \0;
X	 * we append "< Name", but also expand %s to be the Name, hence
X	 * the strlen * 2
X	 */
X    Buffer = emalloc(BufLen);
X
X    (void) sprintf(Buffer, FSTRING, FileInfo->Name);
X    (void) strcat(Buffer, " < ");
X    (void) strcat(Buffer, FileInfo->Name);
X
X    fp = popen(Buffer, "r");
X    (void) efree(Buffer);
X    return fp;
X}
X
Xstatic long BytesRead = 0L;
Xstatic int InWord = 0;
X
X/* Character input */
X
X#ifdef __GNU__
Xinline
X#endif
Xint
XRealGetChar(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    /* ASSERT: InWord && _chForLee == '\'' */
X    LastChar = getc(FileInfo->Stream);
X    if (WithinWord(LastChar) && LastChar != '\'') {
X	BytesRead++;
X	return '\'';
X    } else {
X	/* delete the single quote, as it was at the end of
X	 * a word, not in the middle
X	 */
X	BytesRead++;
X	return ' ';
X    }
X    /*NOTREACHED*/
X    /* exit(1); */
X}
X
Xt_WordInfo *
XReadWord(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    /* use two static storage areas so we can be called twice in a row.
X     * This is necessary to implement the WPF_LASTINBLOCK flag.
X     */
X    static t_WordInfo This, That;
X    static int ThisOrThat = 0;
X    t_WordInfo *WordInfo;
X    static char Buffer[MaxWordLength + 1];
X    int ch;
X    register char *q = Buffer;
X    static int WordInBlock;
X    static t_FID LastFid = 0L;
X    static long LastPos = 0L;
X    static int SawCommon = 0;
X    static int SawLetters = 0;
X    static int BlockInFile = 0L;
X    static unsigned long LastBlock;
X    unsigned long Start;
X
X    WordInfo = (ThisOrThat ? &This : &That);
X
X    if (FileInfo->FID != LastFid) {
X	LastFid = FileInfo->FID;
X	WordInBlock = (-1); /* none, yet! */
X	LastPos = BlockInFile = LastBlock = 0L;
X	BytesRead = 0L;
X	SawCommon = SawLetters = 0;
X	if (AsciiTrace) {
X	    fprintf(stderr, "Reading file \"%s\"", FileInfo->Name);
X	}
X    }
X
X    /* Skip non-word characters */
X    while ((ch = GetChar(FileInfo)) != EOF) {
X	if (StartsWord(ch)) break;
X    }
X
X    /* ASSERT: we have read at least one character */
X
X    if (ch == EOF) {
X	if (AsciiTrace) {
X	    fprintf(stderr, "\n");
X	}
X	return (t_WordInfo *) 0;
X    }
X
X    Start = BytesRead - 1;
X
X    if (UseLineNumbers) {
X	BlockInFile = LineInFile;
X    } else {
X	BlockInFile = Start / FileBlockSize;
X    }
X
X    if (BlockInFile != LastBlock) {
X	LastBlock = BlockInFile;
X	if (AsciiTrace > 1) {
X	    fprintf(stderr, ".");
X#ifdef sun
X	    /* SunOS seems to line-buffer stderr! */
X	    fflush(stderr);
X#endif
X	}
X	WordInBlock = (-1);
X    }
X
X    if (isupper(ch)) {
X	WordInfo->WordPlace.Flags = WPF_UPPERCASE;
X	ch = tolower(ch);
X    } else {
X	WordInfo->WordPlace.Flags = 0;
X    }
X
X    InWord = 1; /* For GetChar() */
X
X    do {
X	if (q - Buffer < MaxWordLength) {
X	    *q++ = ch;
X	}
X	ch = GetChar(FileInfo);
X	TOLOWER(ch);
X    } while (WithinWord(ch) || EndsWord(ch));
X
X    *q = '\0';
X    InWord = 0;
X
X#ifdef __GNUC__
X    /* this is to get round a gcc bug... */
X    {
X	int i = q - Buffer;
X	WordInfo->Length = i;
X
X	if (i < MinWordLength) {
X	    register char *p;
X
X	    for (p = Buffer; p < q; p++) {
X		if (isalpha(*p)) {
X		    SawLetters = 1;
X		    break;
X		}
X	    }
X	    return ReadWord(FileInfo);
X	}
X    }
X#else
X    if ((WordInfo->Length = q - Buffer) < MinWordLength) {
X	register char *p;
X
X	for (p = Buffer; p < q; p++) {
X	    if (isalpha(*p)) {
X		SawLetters = 1;
X		break;
X	    }
X	}
X	return ReadWord(FileInfo);
X    }
X#endif
X
X    WordInfo->Word = Buffer;
X
X    (void) WordRoot(WordInfo);
X
X    WordInfo->Length = strlen(WordInfo->Word);
X
X    if (TooCommon(WordInfo)) {
X	SawCommon++;
X	WordInBlock++;
X#ifdef ASCIITRACE
X	if (AsciiTrace > 10) {
X	    fprintf(stderr, "%s too common to index\n", WordInfo->Word);
X	}
X#endif
X	return ReadWord(FileInfo);
X    } else if (SawCommon) {
X	SawCommon = 0;
X	WordInfo->WordPlace.Flags |= (WPF_LASTWASCOMMON|WPF_LASTHADLETTERS);
X    }
X    if (SawLetters) {
X	SawLetters = 0;
X	WordInfo->WordPlace.Flags |= WPF_LASTHADLETTERS;
X    }
X
X    /* StuffBefore is the # of chars between the end of the last word and
X     * the start of this one.
X     */
X    if (Start > 1L) {
X	if (Start - (LastPos + 1) <= 0) {
X	    WordInfo->WordPlace.StuffBefore = 1; /* save a byte in the index */
X	} else if (Start - (LastPos + 1) >= 255 ) {
X	    WordInfo->WordPlace.StuffBefore = 255;
X	} else {
X	    WordInfo->WordPlace.StuffBefore = Start - (LastPos + 1);
X	}
X    } else {
X	WordInfo->WordPlace.StuffBefore = 1; /* i.e., the default */
X    }
X
X    WordInfo->WordPlace.FID = WordInfo->FID = FileInfo->FID;
X    WordInfo->WID = (t_WID) 0;
X    WordInfo->Next = (t_WordInfo *) 0;
X    WordInfo->WordPlaces = (t_WordPlace *) 0;
X    WordInfo->WordPlacesInHere = 0;
X    WordInfo->WordPlace.WordInBlock = (++WordInBlock);
X    WordInfo->WordPlace.BlockInFile = BlockInFile;
X    WordInfo->DataBlock = (char *) 0;
X
X    WordInfo->Word[WordInfo->Length] = '\0';
X
X    {
X	/* I want to avoid using malloc() here... 
X	 * Another kludge would be to malloc sizeof(t_WordInfo) +
X	 * strlen(WordInfo->Word + 1) and to put the string at the end
X	 * of (i.e. just after) the struct.
X	 */
X	static char Word2[MaxWordLength + 1];
X	static char Word1[MaxWordLength + 1];
X	char *p = (ThisOrThat) ? Word1 : Word2;
X
X	(void) strncpy(p, WordInfo->Word, (int) WordInfo->Length);
X	WordInfo->Word = p;
X	WordInfo->Word[WordInfo->Length] = '\0';
X    }
X
X    LastPos = BytesRead - 1;
X
X    ThisOrThat = !ThisOrThat;
X    /* toggle between 0 and 1.  Boring life, really */
X
X    if (!WordInfo->Word[0]) {
X	fprintf(stderr, "Null word in ReadWord()\n");
X    }
X    return WordInfo;
X}
X
Xvoid
XAddStream(FileInfo)
X    t_FileInfo *FileInfo;
X{
X    /* I have to mark the last word in the block.
X     * I do that by marking the previous word if it was in a differant block
X     * than the current one.
X     */
X    t_WordInfo *WordInfo;
X    t_WordInfo *LastWord = 0;
X
X    BytesRead = 0;
X
X    while ((WordInfo = ReadWord(FileInfo)) != (t_WordInfo *) 0) {
X	if (LastWord) {
X	    if (LastWord->WordPlace.BlockInFile !=
X	    				WordInfo->WordPlace.BlockInFile) {
X		LastWord->WordPlace.Flags |= WPF_LASTINBLOCK;
X	    }
X	    AddWord(LastWord);
X	}
X	LastWord = WordInfo;
X    }
X    if (LastWord) {
X	/* it's the last in the file, so it is also the last in the block */
X	LastWord->WordPlace.Flags |= WPF_LASTINBLOCK;
X	AddWord(LastWord);
X    }
X
X    if (AsciiTrace) {
X	fprintf(stderr, "Read %lu bytes from \"%s\"\n", BytesRead, FileInfo->Name);
X    }
X}
X
X/* lqaddfile has been carried through several incarnations of lq-text,
X * and hence has more than one Inital Revision in the following history.
X *
X * $Log:	lqaddfile.c,v $
X * Revision 1.14  91/03/02  21:22:39  lee
X * Added write access call.
X * 
X * Revision 1.13  91/03/02  18:53:25  lee
X * Common words are now counted, so you can now edit the common word list
X * without invalidating the index.
X * 
X * Revision 1.12  90/10/06  00:50:54  lee
X * Prepared for first beta release.
X * 
X * Revision 1.11  90/10/05  23:46:11  lee
X * Allow compilation with -UASCIITRACE
X * 
X * Revision 1.10  90/10/04  17:54:46  lee
X * fixed a typo in the usage message.
X * 
X * Revision 1.9  90/09/28  23:20:22  lee
X * Put more of GetChar into a macro and parameterised TOLOWER.
X * 
X * Revision 1.8  90/09/28  22:19:04  lee
X * Did the previous fix _properly_!
X * 
X * Revision 1.7  90/09/28  22:12:35  lee
X * Made getchar a macro, and deleted the call to CallFree...
X * 
X * Revision 1.6  90/09/20  18:46:03  lee
X * Closed up a (very small) memory leak.
X * 
X * Revision 1.5  90/09/19  20:16:41  lee
X * Fixed problems associated with indexing an empty file.
X * 
X * Revision 1.4  90/08/29  21:45:18  lee
X * Alpha release
X * 
X * Revision 1.3  90/08/09  19:17:12  lee
X * *** empty log message ***
X * 
X * Revision 1.1  90/02/27  11:05:02  lee
X * Initial revision
X * 
X * Revision 2.2  89/10/08  20:45:13  lee
X * Working version of nx-text engine.  Addfile and wordinfo work OK.
X * 
X * Revision 2.1  89/10/02  01:14:12  lee
X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
X * 
X * Revision 1.3  89/09/17  23:02:42  lee
X * Various fixes; NumberInBlock now a short...
X * 
X * Revision 1.2  89/09/16  21:16:11  lee
X * First demonstratable version.
X * 
X * Revision 1.1  89/09/07  21:05:52  lee
X * Initial revision
X *
X */
@@@End of lq-text/src/lqtext/lqaddfile.c
echo end of part 06
-- 
Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337